角色(roles):把playbook根据功能,如handler,tasks等分门别类的放在在各自的子目录下,形成一个集合,就是角色。 Roles目录可以是ansible.cfg中roles_path定义的路径,也可以和入口Playbook文件存放在同级目录。建议使用roles_path,方便统一管理。这篇的示例使用和入口Playbook文件存放在同级目录。

Roles are ways of automatically loading certain vars_files, tasks, and handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users.

官方文档的连接:https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html

github上官方的简单示例:https://github.com/ansible/ansible-examples

最佳实践的官方文档:https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html

Roles 目录结构

Roles依赖目录命名规则和目录摆放。下面是官方的示例,定义了一个Roles的目录结构:

site.yml
webservers.yml
fooservers.yml
roles/
    common/
        tasks/
        handlers/
        files/
        templates/
        vars/
        defaults/
        meta/
    webservers/
        tasks/
        defaults/
        meta/

*.yml文件: 入口Playbook文件

site.yml: 这个名字一般就是部署项目的Playbook文件名。如果还有其他任务在创建Playbook,名字随意。

roles目录: 存放Roles的目录。下面每一个子目录就是一个角色,这里有common和webservers

在每个角色下,根据功能按需创建目录,目录名有规范。每个目录下都要有一个main.yml文件,ansible会调用这个文件。 目录名称的含义如下:

  • tasks: 定义角色的任务列表,可以使用include包含其它的位于此目录的task文件
  • handlers: 定义角色中触发条件时执行的动作
  • defaults: 设定默认变量,优先级极低,基本就是该变量没有值的时候才会用到这个值
  • vars: 定义角色使用的变量,优先级高
  • files: 用来存放由copy模块或script模块调用的文件。不需要main.yml文件
  • templates: 用来存放jinjia2模板,template模块会自动在此目录中寻找jinjia2模板文件。不需要main.yml文件
  • meta: 定义角色的元数据,主要是用作依赖关系

多平台支持

官方有一个在tasks里区分的多平台支持的示例。 另外还有一个从书上看到的不同平台定义不同角色名的示例。 最后是一个在官方最佳实践中的示例。

tasks 多平台支持

tasks级别,对应tasks目录。 定义好角色,角色下定义多个task文件。在main.yml中进行条件判断,导入对应的task文件。 tasks/main.yml是必须有的,作为tasks的入口文件,但是实际的代码不用写在这个文件里,可以在这个文件里包含其他yaml文件。官方有一个多平台支持的示例:

# roles/example/tasks/main.yml
- import_tasks: redhat.yml
  when: ansible_facts['os_family']|lower == 'redhat'
- import_tasks: debian.yml
  when: ansible_facts['os_family']|lower == 'debian'

# roles/example/tasks/redhat.yml
- yum:
    name: "httpd"
    state: present

# roles/example/tasks/debian.yml
- apt:
    name: "apache2"
    state: present

roles 多平台支持

角色级别,对应roles下的角色子目录,比如common和webservers目录。 直接定义好多个角色,有条件地将所有的角色导入。只有满足要求的角色会被导入执行。 在roles层面来支持多平台。预先为不同平台定义好不用的角色。比如两个角色名分别为 httpd_db 和 httpd_rh。那么Playbook文件可以这么写:

# site.yml
- name: install httpd
  hosts: webservers
  roles:
    - { role: httpd_db, when: ansible_os_family == 'Deian' }
    - { role: httpd_rh, when: ansible_os_family == 'RedHat' }

这里的判断平台的方式和上面的应该是一样的,都是通过Ansible自动获取的主机信息来进行自动判断。这里的写法比上面的官网的写法low。我没有实际测试,就保留原来的写法了。

group_by 模块自动对主机分组

playbook级别,对应roles的同级目录,比如site.yml文件。 先执行一个 group_by 模块的task,对主机进行动态分组。之后再分别对组进行playbook的设置。 这部分内容和roles无关,用了roles可以套用,没用roles也可以套用。是官方最佳实践里的做法。 group_by 模块可以根据关键字对主机进行分组。关键字可以从主机信息中提取,提取到之后,可以先拼接上一段自定义的前缀或后缀。最终获得的字符串会成为一个主机组的组名,这样Playbook中的hosts就可以直接填写这个组名:

---
- name: talk to all hosts just so we can learn about them
  hosts: all
  tasks:
    - name: Classify hosts depending on their OS distribution
      group_by:
        key: os_{{ ansible_facts['distribution'] }}

# now just on the CentOS hosts...
- hosts: os_CentOS
  gather_facts: False
  tasks:
    - # tasks that only happen on CentOS go here

通过 group_by 模块动态为主机添加了分组,还可以事先创建好组变量文件,这些组变量也是会生效的。

Using Roles

官网有各种花式技巧:https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html#using-roles

各种花式技巧:

  • 把role写在tasks中,这是一种新的语法格式。可以在引入role的前后执行别的task
  • 使用绝对路径引入role
  • 引入role的同时,定义变量
  • 有条件的导入role,上面roles的多平台就是这么用的
  • 为角色添加标签。使用场景我还没想明白

角色依赖关系

通过角色依赖,可以在使用角色时自动加入其它角色。角色依赖定义在 meta/main.yml 文件中的 dependencies 下,内容包含当前角色的依赖角色和依赖角色的参数列表。 配置文件示例:

---
dependencies:
  - role: common
    vars:
      some_parameter: 3
  - role: apache
    vars:
      apache_port: 80
  - role: postgres
    vars:
      dbname: blarg
      other_parameter: 12

角色的依赖关系,仅仅是meta文件中的一个key,也就是dependencies。别的key还有allow_duplicates,这个是定义角色可以重复执行任务的。其他key暂时在文档中没有出现。

角色中嵌入模块和插件

在目录结构中,列出了角色使用的基本目录名称。这里的自定义模块和插件也是放在roles目录下的角色名子目录中的。 新的目录结构:

roles/
    webservers/
        tasks/
        defaults/
        meta/
        library/
            module1
            module2
        filter_plugins
            filter1
            filter2

在webservers角色下添加了自定义模块,目录名称使用library。 ansible的插件有很多种,这里是在webserver角色下,为filter插件添加了两个自定义功能的文件。

项目目录结构

这里是 Ansible 官方最佳实践中推荐的一个工作目录的结构:

production                # 生产环境的 inventory 文件
stage                     # 试运行环境的 inventory 文件

group_vars/               # 定义组变量
   group1
   group2
host_vars/                # 定义主机变量
   hostname1
   hostname2

library/                  # 如果有自定义的模块,放在这里(可选)
module_utils/             # 如果有自定义的模块中要使用的工具,放在这里(可选)
filter_plugins/           # 如果有自定义的过滤插件,放在这里(可选)

site.yml                  # 主 playbook
webservers.yml            # Web 服务器的 playbook
dbservers.yml             # 数据库服务器的 playbook

roles/                    # role 文件存放目录
    common/               # common 角色目录
        tasks/            #
            main.yml      #  task 入口
        handlers/         #
            main.yml      #  handler 入口
        templates/        #  存放模板文件
            ntp.conf.j2   #
        files/            #  存放文件资源
            bar.txt       #
            foo.sh        #
        vars/             #
            main.yml      #  定义角色使用的变量
        defaults/         #
            main.yml      #  定义角色默认变量
        meta/             #
            main.yml      #  定义角色依赖

    webservers/           # webservers 角色目录,不展开了

这里的自定义模块和插件是放在项目目录下的,不会自动生效。 library的默认值: ~/.ansible/plugins/modules:/usr/share/ansible/plugins/modules filter_plugins的默认值: ~/.ansible/plugins/filter:/usr/share/ansible/plugins/filter 官方文档的这篇里可以查到很多有关Ansible设置相关的信息,包括每个变量在配置文件中的名称,在环境变量中的名称,以及变量的默认值。 https://docs.ansible.com/ansible/latest/reference_appendices/config.html

可以去修改 ansible.cfg 配置文件,也可以利用默认的家目录中的位置,创建软连接。