环境
- 控制节点:Ubuntu 22.04
 - Ansible 2.10.8
 - 管理节点:CentOS 8
 
role
目录结构
role的文件结构中,包含了8个标准目录:
- tasks
 - handlers
 - templates
 - files
 - vars
 - defaults
 - meta
 - library
 
例如,下面是 common 这个role的目录结构:
            
            
              powershell
              
              
            
          
          roles/
    common/               # this hierarchy represents a "role"
        tasks/            #
            main.yml      #  <-- tasks file can include smaller files if warranted
        handlers/         #
            main.yml      #  <-- handlers file
        templates/        #  <-- files for use with the template resource
            ntp.conf.j2   #  <------- templates end in .j2
        files/            #
            bar.txt       #  <-- files for use with the copy resource
            foo.sh        #  <-- script files for use with the script resource
        vars/             #
            main.yml      #  <-- variables associated with this role
        defaults/         #
            main.yml      #  <-- default lower priority variables for this role
        meta/             #
            main.yml      #  <-- role dependencies
        library/          # roles can also include custom modules
        Ansible默认会在每个目录下查找 main.yml (或者 main.yaml / main )文件。
例:创建目录结构如下:
            
            
              powershell
              
              
            
          
          ➜  testRole1 tree
.
├── roles
│   ├── role1
│   │   └── tasks
│   │       └── main.yml
│   ├── role2
│   │   └── tasks
│   │       └── main.yml
│   └── role3
│       └── tasks
│           └── main.yml
└── test.yml
        其中, test.yml 内容如下:
            
            
              yaml
              
              
            
          
          ---
- hosts: all
  roles:
    - role1
    - role2
        注:此处也可以写为:
            
            
              yaml
              
              
            
          
          ---
- hosts: all
  roles:
    - role: role1
    - role: role2
        两种方式甚至可以混用。
本例中,指定为只包含 role1 和 role2 的内容。
roles/role1/tasks/main.yml 的内容如下(role2和role3类似):
            
            
              yaml
              
              
            
          
          ---
- name: role1 task1
  debug:
    msg: "I am role1 task1"
- name: role1 task2
  debug:
    msg: "I am role1 task2"
        运行结果如下:
            
            
              powershell
              
              
            
          
          ➜  testRole1 ansible-playbook testRole_1.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [role1 : role1 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task1"
}
TASK [role1 : role1 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task2"
}
TASK [role2 : role2 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role2 task1"
}
TASK [role2 : role2 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role2 task2"
}
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
        可见,ansible只运行了指定的role。本例中,运行了role1和role2的所有task。
Ansible有以下几种方式来查找role:
- 在collections里
 - 在 
roles目录下,即本例所示 - 配置 
roles_path,其缺省的查找路径为~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles - 在playbook文件同级,即省略 
roles目录,对于本例,可变为: 
            
            
              powershell
              
              
            
          
          ➜  testRole2 tree
.
├── role1
│   └── tasks
│       └── main.yml
├── role2
│   └── tasks
│       └── main.yml
├── role3
│   └── tasks
│       └── main.yml
└── test.yml
        虽然可以省略 roles 目录,但显然加上这一级目录会更清晰一些。
- 直接在playbook中指定role路径,比如:
 
            
            
              yaml
              
              
            
          
          ---
- hosts: all
  roles:
    - role: '/path/to/my/roles/common'
        使用role
可通过以下几种方式使用role:
- 在play级别使用 
roles:经典用法,静态引入 - 在task级别使用 
include_role:动态引入 - 在task级别使用 
import_role:静态引入 
在play级别使用role
比如:
            
            
              yaml
              
              
            
          
          ---
- hosts: webservers
  roles:
    - common
    - webservers
        可以给 roles 添加 tags , vars 等选项。
修改role1,role2,role3的 tasks/main.yml ,打印 {``{ var1 }} 变量。role1修改如下(role2,role3类似):
            
            
              yaml
              
              
            
          
          ---
- name: role1 task1
  debug:
    msg: "I am role1 task1 {{ var1 }}"
- name: role1 task2
  debug:
    msg: "I am role1 task2"
        创建 test.yml 如下:
            
            
              yaml
              
              
            
          
          ---
- hosts: all
  roles:
    - role1
    - role: role2
      vars:
        var1: "hello"
      tags: tag1
        运行结果如下:
            
            
              powershell
              
              
            
          
          ➜  testRole3 ansible-playbook test.yml            
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [role1 : role1 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task1 hello"
}
TASK [role1 : role1 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task2"
}
TASK [role2 : role2 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role2 task1 hello"
}
TASK [role2 : role2 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role2 task2"
}
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
        可见,在 role2 中定义的变量 var1 ,在 role1 中也可用(可通过 DEFAULT_PRIVATE_ROLE_VARS 来定制其行为)。
但是,请注意 role2 中添加的tag tag1 ,只针对role2中的每个task都有效:
            
            
              powershell
              
              
            
          
          ➜  testRole3 ansible-playbook test.yml --tags tag1
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [role2 : role2 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role2 task1 hello"
}
TASK [role2 : role2 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role2 task2"
}
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
        在task级别include_role
修改role1,role2,role3的 tasks/main.yml ,去掉变量var1。修改 test.yml 如下:
            
            
              yaml
              
              
            
          
          ---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "I am task1"
    - name: task2
      include_role:
        name: role3
    - name: task3
      debug:
        msg: "I am task3"
        运行结果如下:
            
            
              powershell
              
              
            
          
          ➜  testRole4 ansible-playbook test.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am task1"
}
TASK [task2] ***************************************************************************************
TASK [role3 : role3 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role3 task1"
}
TASK [role3 : role3 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role3 task2"
}
TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am task3"
}
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
        include_role 可以添加 tags , vars , when 等选项。
例如:
            
            
              yaml
              
              
            
          
          ......
    - name: task2
      include_role:
        name: role3
      when: "ansible_facts['os_family'] == 'RedHataa'"
......
        则只有满足 when 的条件时,才会生效。
这里格外需要注意 tags 。 include_role 的tag只针对 include_role 自身有效。
如果运行playbook时,指定了 --tags 选项,则:
- 不满足tag条件的 
include_role都不会运行 - 满足tag条件的 
include_role中,只有满足tag条件的task会运行 
例如,创建 test.yml 如下:
            
            
              yaml
              
              
            
          
          ---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "I am task1"
    - name: task2
      include_role:
        name: role1
      tags: tag1
    - name: task3
      include_role:
        name: role3
      tags: tag2
    - name: task4
      debug:
        msg: "I am task4"
        role1/task/main.yml 如下:
            
            
              yaml
              
              
            
          
          ---
- name: role1 task1
  debug:
    msg: "I am role1 task1"
- name: role1 task2
  debug:
    msg: "I am role1 task2"
        role3/task/main.yml 如下:
            
            
              yaml
              
              
            
          
          ---
- name: role3 task1
  debug:
    msg: "I am role3 task1"
  tags: tag1
- name: role3 task2
  debug:
    msg: "I am role3 task2"
  tags: tag2
        运行playbook时,如果不指定 --tags ,则所有task都会运行。
运行playbook时,如果指定 --tags tag1 ,则只有task2(即role1)满足tag条件,但是role1里的task都不满足tag条件,所以也都不会运行:
            
            
              powershell
              
              
            
          
          ➜  testRole4 ansible-playbook test.yml --tags tag1
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task2] ***************************************************************************************
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
        运行playbook时,如果指定 --tags tag2 ,则只有task3(即role3)满足tag条件,而role3的task中,只有"role3 task2"同时满足tag条件,所以只有"role3 task2"会运行:
            
            
              powershell
              
              
            
          
          ➜  testRole4 ansible-playbook test.yml --tags tag2
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task3] ***************************************************************************************
TASK [role3 : role3 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role3 task2"
}
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
        在task级别import_role
其行为和 roles 选项是一样的。
这里格外需要注意 tags 。 import_role 的tag针对role里的所有task都有效。
如果运行playbook时,指定了 --tags ,则:
- 不满足tag条件的 
import_role中,满足tag条件的task会运行 - 满足tag条件的 
import_role中,所有task都会运行 
例如,修改上例中的 test.yml ,把 include_role 改为 import_role ,如下:
            
            
              yaml
              
              
            
          
          ---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "I am task1"
    - name: task2
      import_role:
        name: role1
      tags: tag1
    - name: task3
      import_role:
        name: role3
      tags: tag2
    - name: task4
      debug:
        msg: "I am task4"
        role1和role3同上例。
运行playbook时,如果不指定 --tags ,则所有task都会运行。
运行playbook时,如果指定 --tags tag1 ,则task2(即role1)满足tag条件,则role1里的所有task都会运行。同时task3(即role3)虽然不满足tag条件,但role3里满足tag条件的task会运行:
            
            
              powershell
              
              
            
          
          ➜  testRole5 ansible-playbook test.yml --tags tag1
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [role1 : role1 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task1"
}
TASK [role1 : role1 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task2"
}
TASK [role3 : role3 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role3 task1"
}
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
        同理,如果运行playbook时,指定 --tags tag2 ,则task2(即role2)不满足tag条件,且role2里的所有task也都不满足tag条件,所以都不会运行。同时,task3(即role3)满足tag条件,则role3里所有的task都会运行:
            
            
              powershell
              
              
            
          
          ➜  testRole5 ansible-playbook test.yml --tags tag2
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [role3 : role3 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role3 task1"
}
TASK [role3 : role3 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role3 task2"
}
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
        include_role和import_role的tag
二者区别总结如下:
| include_role 满足tag条件 | include_role 不满足tag条件 | |
|---|---|---|
| task满足tag条件 | Y | N | 
| task不满足tag条件 | N | N | 
| import_role 满足tag条件 | import_role 不满足tag条件 | |
|---|---|---|
| task满足tag条件 | Y | Y | 
| task不满足tag条件 | Y | N | 
roles 的tag行为和import_role 一致(都是静态引入)。
动态引入和静态引入的区别
include_role是动态引入,import_role是静态引入。
动态引入, include_task 本身也是一个task,例如:
            
            
              yaml
              
              
            
          
          ---
- hosts: all
  tasks:
    - name: task1
      include_role:
        name: role1
        
            
            
              powershell
              
              
            
          
          ➜  testRole6 ansible-playbook test.yml            
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [task1] ***************************************************************************************
TASK [role1 : role1 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task1"
}
TASK [role1 : role1 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task2"
}
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  
        把 include_role 改为 import_role ,再次运行,如下:
            
            
              powershell
              
              
            
          
          ➜  testRole6 ansible-playbook test.yml
PLAY [all] *****************************************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [192.168.1.55]
TASK [role1 : role1 task1] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task1"
}
TASK [role1 : role1 task2] *************************************************************************
ok: [192.168.1.55] => {
    "msg": "I am role1 task2"
}
PLAY RECAP *****************************************************************************************
192.168.1.55               : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
        可见,前者的输出结果中,有一行 TASK [task1] ,而后者没有这一行输出。
这是因为,静态引入,是在预编译期,已经把task1所代表的role1的task静态的引入进来了,在运行期也就没有task1了。而动态引入,在运行期保留了task1,运行到此处时,才把role1引入进来。
这也能解释tag的行为为何会有不同,因为对于静态引入,运行期已经没有task1了,所以tag在预编译期已经被应用到role1实际的task上去了。而对于动态引入,tag确实是应用于task1上的。
参考
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html