Ansible的role

环境

  • 控制节点:Ubuntu 22.04
  • Ansible 2.10.8
  • 管理节点:CentOS 8

role

目录结构

role的文件结构中,包含了8个标准目录:

  • tasks
  • handlers
  • templates
  • files
  • vars
  • defaults
  • meta
  • library

例如,下面是 common 这个role的目录结构:

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 )文件。

例:创建目录结构如下:

➜  testRole1 tree
.
├── roles
│   ├── role1
│   │   └── tasks
│   │       └── main.yml
│   ├── role2
│   │   └── tasks
│   │       └── main.yml
│   └── role3
│       └── tasks
│           └── main.yml
└── test.yml

其中, test.yml 内容如下:

---
- hosts: all
  roles:
    - role1
    - role2

注:此处也可以写为:

---
- hosts: all
  roles:
    - role: role1
    - role: role2

两种方式甚至可以混用。

本例中,指定为只包含 role1role2 的内容。

roles/role1/tasks/main.yml 的内容如下(role2和role3类似):

---
- name: role1 task1
  debug:
    msg: "I am role1 task1"
- name: role1 task2
  debug:
    msg: "I am role1 task2"

运行结果如下:

➜  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 目录,对于本例,可变为:
➜  testRole2 tree
.
├── role1
│   └── tasks
│       └── main.yml
├── role2
│   └── tasks
│       └── main.yml
├── role3
│   └── tasks
│       └── main.yml
└── test.yml

虽然可以省略 roles 目录,但显然加上这一级目录会更清晰一些。

  • 直接在playbook中指定role路径,比如:
---
- hosts: all
  roles:
    - role: '/path/to/my/roles/common'

使用role

可通过以下几种方式使用role:

  • 在play级别使用 roles :经典用法,静态引入
  • 在task级别使用 include_role :动态引入
  • 在task级别使用 import_role :静态引入

在play级别使用role

比如:

---
- hosts: webservers
  roles:
    - common
    - webservers

可以给 roles 添加 tagsvars 等选项。

修改role1,role2,role3的 tasks/main.yml ,打印 {{ var1 }} 变量。role1修改如下(role2,role3类似):

---
- name: role1 task1
  debug:
    msg: "I am role1 task1 {{ var1 }}"
- name: role1 task2
  debug:
    msg: "I am role1 task2"

创建 test.yml 如下:

---
- hosts: all
  roles:
    - role1
    - role: role2
      vars:
        var1: "hello"
      tags: tag1

运行结果如下:

➜  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都有效:

➜  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 如下:

---
- hosts: all
  tasks:
    - name: task1
      debug:
        msg: "I am task1"
    - name: task2
      include_role:
        name: role3
    - name: task3
      debug:
        msg: "I am task3"

运行结果如下:

➜  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 可以添加 tagsvarswhen 等选项。

例如:

......
    - name: task2
      include_role:
        name: role3
      when: "ansible_facts['os_family'] == 'RedHataa'"
......

则只有满足 when 的条件时,才会生效。

这里格外需要注意 tagsinclude_role 的tag只针对 include_role 自身有效。

如果运行playbook时,指定了 --tags 选项,则:

  • 不满足tag条件的 include_role 都不会运行
  • 满足tag条件的 include_role 中,只有满足tag条件的task会运行

例如,创建 test.yml 如下:

---
- 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 如下:

---
- name: role1 task1
  debug:
    msg: "I am role1 task1"
- name: role1 task2
  debug:
    msg: "I am role1 task2"

role3/task/main.yml 如下:

---
- 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条件,所以也都不会运行:

➜  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”会运行:

➜  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 选项是一样的。

这里格外需要注意 tagsimport_role 的tag针对role里的所有task都有效。

如果运行playbook时,指定了 --tags ,则:

  • 不满足tag条件的 import_role 中,满足tag条件的task会运行
  • 满足tag条件的 import_role 中,所有task都会运行

例如,修改上例中的 test.yml ,把 include_role 改为 import_role ,如下:

---
- 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会运行:

➜  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都会运行:

➜  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,例如:

---
- hosts: all
  tasks:
    - name: task1
      include_role:
        name: role1
➜  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 ,再次运行,如下:

➜  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

你可能感兴趣的:(Ansible,ansible)