注:本文demo使用ansible2.7稳定版
在我看来,role是task文件、变量文件、handlers文件的集合体,这个集合体的显著特点是:可移植性和可重复执行性。
实践中,通常我们以部署某个服务为单元作为一个role ,然后将这些服务单元(role)放在一个roles目录下。主playbook文件通过调用roles目录下的role,来实现各种灵活多变的部署需求。
本节主要为大家介绍下roles的目录结构、引用方法及其他特性。
通常创建一个role的方法有两种:
命令mkdir和touch行手动创建
使用ansible-galaxy自动初始化一个role
命令行手动创建方式就无需多说了,即需要哪个目录和文件就用「mkdir」和「touch」命令创建出来。
我个人比较喜欢使用「ansible-galaxy」命令创建,创建完后再删除我不需要的目录,这样可以避免因手误创建出错的问题。
例如,我想使用「ansible-galaxy init」命令创建一个名字为role_A 的role,可以这样写:
? lab-ansible ansible-galaxy init role_A
- role_A was created successfully
创建后的目录结构如下:
? lab-ansible tree role_A
role_A
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
8 directories, 8 files
使用「ansible-galaxy」命令自动创建的role是最全的目录结构,根据需求,可以删除不用的目录文件。
我们以上面创建的「role_A」为例,介绍下各目录文件的作用:
role中各个目录下的main.yaml文件很重要,这是ansible默认加载的YAML文件。
比较常用的方法,我们可以使用「roles:」语句引用role :
---
- hosts: node1
roles:
- role_A
或者
---
- hosts: node1
roles:
- name: role_A
- name: role_A
或者
---
- hosts: node1
roles:
- role: role_A
- role: role_A
或者使用绝对路径:
---
# playbooks/test.yaml
- hosts: node1
roles:
- role: /root/lab-ansible/roles/role_A
引入的同时添加变量参数:
---
# playbooks/test.yaml
- hosts: node1
roles:
- role: role_A
vars:
name: Maurice
age: 100
引入的同时添加tag参数:
---
# playbooks/test.yaml
- hosts: node1
roles:
- role: role_B
tags:
- tag_one
- tag_two
# 等价于上面
- { role: role_B, tags:[‘tag_one‘,‘tag_two‘] }
根据需求,我们在playbook中引用不同的role,引用后的效果也很好理解:ansible会把role所包含的任务、变量、handlers、依赖等加载到playbook中,顺次执行。
检索路径
上面介绍了使用「roles」语句的引用方法,那么ansible去哪找这些role呢?
在不使用绝对路径的情况下,ansible检索role的默认路径有:
注:上面的检索路径是在centos操作系统下测试出来的结果。
在后来版本(ansible>=2.4)中,ansible引入了「import_role」(静态)和「include_role」(动态)方法:
---
# playbooks/test.yaml
- hosts: node1
tasks:
- include_role:
name: role_A
vars:
name: maurice
age: 100
- import_role:
name: role_B
比较于「roles」语句,「import_role」和「include_role」的优点如下:
关于include_role(动态)和import_role(静态)的区别,可以参考笔者之前的文章《ansible中include_tasks和import_tasks》。
也正是因为「include_task」是动态导入,当我们给「include_role」导入的role打tag时,实际并不会执行该role的task。
举个??,当我们使用include导入role_A,使用import导入role_B时:
---
# playbooks/test.yaml
- hosts: node1
tasks:
- include_role:
name: role_A
tags: maurice
- import_role:
name: role_B
tags: maurice
role_A内容如下:
---
# tasks file for role_A
- debug:
msg: "age"
- debug:
msg: "maurice"
执行结果显示,role_A虽然被引用,但里面的task并没有执行:
PLAY [node1] *************************************************************
TASK [Gathering Facts] *************************************************************
ok: [node1]
TASK [include_role : role_A] *************************************************************
TASK [role_B : debug] *************************************************************
ok: [node1] => {
"msg": "I‘m just role_B"
}
PLAY RECAP *************************************************************
node1 : ok=2 changed=0 unreachable=0 failed=0
不添加其他变量、tag等的情况下,一个playbook中对同一个role引入多次时,实际ansible只会执行一次。
例如:
---
# playbooks/test.yaml
- hosts: node1
roles:
- role_A
- role_A
当然,我们也可以让其运行多次,方法如下:
两种情况示例分别如下:
---
- hosts: webservers
roles:
- role: foo
vars:
message: "first"
- { role: foo, vars: { message: "second" } }
# playbook.yml
---
- hosts: webservers
roles:
- foo
- foo
# roles/foo/meta/main.yml
---
allow_duplicates: true
笔者之前的文章《ansible基础-playbooks》「5.执行顺序」中提到,如果playbook中只有「tasks」和「roles」,ansible总是会先执行「roles」语句部分。此时可以使用「per_tasks」和「post_tasks」来规范执行顺序。
在使用「per_tasks」+「roles」+「tasks」+「post_tasks」结构下,task的执行顺序为:
role的依赖指role_A可以引入其他的role,例如role_B。
roles的依赖知识点总结如下:
多层依赖
被引入的role总是优先执行,即便是同一个 role 被引入了多次(遵循3.3重复引用规则)。
举个??:
role「car」引入了role「wheel」:
---
# roles/car/meta/main.yml
dependencies:
- role: wheel
vars:
n: 1
- role: wheel
vars:
n: 2
- role: wheel
vars:
n: 3
- role: wheel
vars:
n: 4
同时,role「wheel」引入了role「tire」和「brake」:
---
# roles/wheel/meta/main.yml
dependencies:
- role: tire
- role: brake
最终的执行结果为:
tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car
我们可以为一个role自定义模块和插件,这部分属于高级特性,在以后的「ansible进阶」系列文章中会详细阐述,这里只简单介绍下。
模块
给某个role自定义模块,需要在这个role的目录下创建「library」目录,然后将自定义模块放在该目录下。
创建自定义模块目录结构示例:
roles/
my_custom_modules/
library/
module1
module2
引用的时候在「my_custom_modules」后被引用的role也可以使用该自定义模块:
- hosts: webservers
roles:
- my_custom_modules
- some_other_role_using_my_custom_modules
- yet_another_role_using_my_custom_modules
插件
自定义插件的方法与模块类似,需要创建的目录为「filter_plugins」。
创建自定义插件目录结构示例:
roles/
my_custom_filter/
filter_plugins
filter1
filter2
与模块一样,自定义插件可以在本身role的task和模版内使用,也可以在 role「my_custom_filter」之后被引用的role内使用。
Ansible Galaxy是一个免费的roles仓库,里面有很多社区里现成稳定的role,在生产中的部署项目中,可以直接下载下来使用。
Ansible Galaxy客户端是「ansible-galaxy」,「ansible-galaxy」命令行可以用来初始化一个role(本文2.1 创建roles有提),也可以用来从Ansible Galaxy下载role。
关于Ansible Galaxy的详细介绍,因篇幅原因,这里不再扩展,有兴趣的同学可以去官网<https://galaxy.ansible.com/docs/>加深学习下。
八 参考链接
欢迎大家关注我的公众号:
原文:https://www.cnblogs.com/mauricewei/p/10056458.html