定义
Ansible 角色 (Roles) 是一种用于组织和复用 Ansible Playbooks 的方式。它们提供了一种标准化的方法来打包和管理配置任务、文件、模板、变量等。
基本结构
一个典型的 Ansible 角色目录结构如下所示:
myrole/
├── tasks/
│ └── main.yml
├── files/
│ └── example.conf
├── templates/
│ └── example.conf.j2
├── vars/
│ └── main.yml
├── defaults/
│ └── main.yml
├── handlers/
│ └── main.yml
└── meta/
└── galaxy.yml
- tasks/: 包含任务定义的 YAML 文件,通常是
main.yml。
- files/: 存储要复制到远程主机的文件。
- templates/: 存储 Jinja2 模板文件,这些模板可以用来生成配置文件。
- vars/: 存储角色相关的变量定义。
- defaults/: 存储角色的默认变量值。
- handlers/: 定义 handler 任务,这些任务在被通知时才会执行。
- meta/: 存储元数据文件,如依赖关系和 Galaxy 元数据。
可以使用ansible-galaxy init --help查看更多选项。比如,使用--init-path选项指定创建的Role路径:
$ ansible-galaxy init --init-path /etc/ansible/roles first_role
角色的使用场合
- 当你需要在多个环境中重复使用相同的配置时。
- 当你需要将复杂的配置分解成更小的、可管理的部分时。
- 当你需要与其他团队成员共享配置时。
Ansible role 入口文件
ansible-playbook执行的入口playbook文件(就像main()函数一样),在这个入口文件中引入一个或多个roles目录下的Role。入口文件的名称可以随意,比如www.yml、site.yml、main.yml等,但注意它们和roles目录在同一个目录下。
例如:
├── enter.yml
└── roles
├── first_role/
└── second_role/
上面和roles同目录的enter.yml文件内容如下,此文件中使用roles指令引入了roles目录内的两个Role。
---
- name: play with role
hosts: nginx
gather_facts: false
roles:
- first_role
- second_role
如果遵循了Role规范,入口文件中可以直接使用Role名称来引入roles目录下的Role(正如上面的示例),也可以指定Role的路径来引入。
定义Role的task
Role的任务定义在roles/xxx/tasks/main.yml文件中,main.yml是该Role任务的入口,在执行Role的时候会自动执行main.yml中的任务。可以直接将所有任务定义在此文件中,也可以定义在其它文件中,然后在main.yml文件中去引入。
定义Role的handler
handler和task类似,它定义在roles/xxx/handlers/main.yml中,当Role的task触发了对应的handler,会自动来此文件中寻找。
仍然要说的是,可以将handler定义在其它文件中,然后在roles/xxx/handlers/main.yml使用include_tasks或import_tasks指令来引入,而且前面也提到过这两者在handler上的区别和注意事项。
例如,roles/first_role/handlers/main.yml中定义了如下简单的handler:
例如,roles/first_role/handlers/main.yml中定义了如下简单的handler:
---
- name: handler for test
debug:
msg: "a simple handler for test"
在roles/first_role/tasks/main.yml中通过notify触发该Handler:
---
- name: task in main.yml
debug:
msg: "task in main.yml"
changed_when: true
notify: handler for test
定义Role的变量
Role中有两个地方可以定义变量:
- (1).
roles/xxx/vars/main.yml
- (2).
roles/xxx/defaults/main.yml
从目录名称中可以看出,defaults/main.yml中用于定义Role的默认变量,那么显然,vars/main.yml用于定义其它变量。
这两个文件之间的区别在于,defaults/main.yml中定义的变量优先级低于vars/main.yml中定义的变量。事实上,defaults/main.yml中的变量优先级几乎是最低的,基本上其它任何地方定义的变量都可以覆盖它。
Role中定义的模块和插件
对于绝大多数需求,使用Ansible已经提供的模块和插件就能解决问题,但有时候确实有些需求需要自己去写模块或插件,Ansible也支持用户自定义的模块和插件。
对于Role来说,如果这个Role需要额外使用自己编写的模块或插件,则模块放在roles/xxx/librarys/目录下,而插件放在各自对应类型的目录下:
定义Role的依赖关系
很多时候,一个系统化配置管理的需求中并不仅仅只有一个Role。一个典型的案例是部署LAMP,这里面涉及到了部署Apache、MySQL、PHP,但通常不会将它们全部定义到单个Role中,而是按照一些分类逻辑进行Role的划分,比如MySQL作为DB,单独定义成一个Role,apache和php作为一个webserver整体定义成一个Role。如何对功能进行划分由我们自己决定,正如上面的apache和php完全可以定义成两个Role。
有了多个Role,这些Role可能会存在依赖关系。例如,在新结点上部署集群服务的时候,通常都要对这些新结点进行一些初始化配置,比如设置时间同步,只有满足了这个条件,才会去启动集群服务。
换句话说,有些任务必须先行,这些先行任务就是被依赖的任务。
按照Role规范,被依赖的先行任务都定义在roles/xxx/meta/main.yml文件中。
例如:
---
dependencies:
- second_role
- third_role
使用Role:roles、include_role和import_role
写好Role之后就是使用Role,即在一个入口playbook文件中去加载Role。
加载Role的方式有多种:
- (1).
roles指令:play级别的指令,在playbook解析阶段加载对应文件,这是传统的引入Role的方式
- (2).
import_role指令:task级别的指令,在playbook解析阶段加载对应文件
- (3).
include_role指令:task级别的指令,在遇到该指令的时候才加载Role对应文件
例如前面使用的是roles,如下:
---
- name: play1
hosts: localhost
gather_facts: false
roles:
- first_role
上面通过roles指令来定义要解析和执行的Role,可以同时指定多个Role,且也可以加上role:参数,例如:
roles:
- first_role
- role: seconde_role
- role: third_role
也可以使用include_role和import_role来引入Role,但需注意,这两个指令是tasks级别的,也正因为它们是task级别,使得它们可以和其它task共存。
---
- hosts: localhost
gather_facts: false
tasks:
- debug:
msg: "before first role"
- import_role:
name: first_role
- include_role:
name: second_role
- debug:
msg: "after second role"
这三种引入Role的方式都可以为对应的Role传递参数,例如:
---
- hosts: localhost
gather_facts: false
roles:
- role: first_role
varvar: "valuevalue"
vars:
var1: value1
tasks:
- import_role:
name: second_role
vars:
var1: value1
- include_role:
name: third_role
vars:
var1: value1
查看任务和打标签tags
每个Role的任务有可能分布在多个文件中,还有可能会在入口文件中加载多个Role,这时想要知道执行playbook时具体会执行哪些任务,可以使用ansible-playbook命令的--list-tasks选项,它会列出某个playbook中的所有任务。
$ ansible-playbook --list-tasks enter.yml
playbook: enter.yml
play #1 (localhost): play1 TAGS: []
tasks:
first_role : task1 TAGS: []
first_role : task in t.yml TAGS: []
上面的结果表示,enter.yml这个playbook文件中只有一个play,这个play中包含了两个任务。
从结果中还看到play和task的后面都带有TAGS: [],它是标签。当在play或task级别使用tags指令后就表示为此play或task打了标签。
1.可以在task级别为单个任务打一个或多个标签,多个任务可以打同一个标签名。
例如:
- name: yum install ntp
yum:
name: ntp
state: present
tags:
- initialize
- pkginstall
- ntp
- name: started ntpd
service:
name: ntpd
state: started
tags:
- ntp
- initialize
当任务具有了标签之后,就可以在ansible-playbook命令行中使用--tags来指定只有带有某标记的任务才执行,也可以使用--skip-tags选项明确指定不要执行某个任务。
# 只执行第一个任务
$ ansible-playbook test.yml --tags "pkginstall"
# 两个任务都执行
$ ansible-playbook test.yml --tags "ntp,initialize"
# 第一个任务不执行
$ ansible-playbook test.yml --skip-tags "pkginstall"
如果想要确定tag筛选之后会执行哪些任务,加上--list-tasks即可:
2.可以在play级别打标签,这等价于对play中的所有任务都打上标签。
例如:
- name: play1
hosts: localhost
gather_facts: false
tags:
- tag1
- tag2
pre_tasks:
- debug: "msg='pre_task1'"
- debug: "msg='pre_task2'"
tasks:
- debug: "msg='task1'"
- debug: "msg='task2'"