文章目录
1.编写循环和条件任务
1.1利用循环迭代任务
通过利用循环,我们无需编写多个使用同一模块的任务。例如,他们不必编写五个任务来确保存在五个用户,而是只需编写一个任务来对含有五个用户的列表迭代,从而确保它们都存在。
Ansible支持使用loop关键字对一组项目迭代任务。可以配置循环以利用列表中的各个项目、列表中各个文件的内容、生成的数字序列或更为复杂的结构来重复任务。
1.1.1简单循环
简单循环对一组项目迭代任务。loop关键字添加到任务中,将应对其迭代任务的项目列表取为值。循环变量item保存每个迭代过程中使用的值。
请思考以下代码片段,它使用两次service模块来确保两个网络服务处于运行状态:
- name: Postfix is running
service:
name: postfix
state: started
- name: Devecot is running
service:
name: crond
state: started
这两个任务可以重新编写为使用一个简单循环,从而只需一个任务来确保两个服务都在运行
(item用来循环loop里内容):
[root@king playbook]# vim 123.yml
---
- hosts: all
tasks:
- name: service //任务为service
service: //使用模块是service
name: "{{ item }}" //任务写循环变量,标注循环变量是item
state: started //状态开启
loop: //[循环内容值]loop不属于模块里的,所以等级和模块等级一样,以下是循环的内容
- postfix
- crond
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [service] *******************************************************************************
ok: [192.168.120.130] => (item=postfix)
ok: [192.168.120.130] => (item=crond)
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可以通过一个变量提供loop所使用的列表。在以下示例中,使用变量含有需要处于运行状态的服务的列表:
[root@king playbook]# vim 123.yml
---
- hosts: all
vars:
start_service:
- postfix
- crond
tasks:
- name: service
service:
name: "{{ item }}"
state: started
loop: "{{ start_service }}" //使用变量代替循环内容
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [service] *******************************************************************************
ok: [192.168.120.130] => (item=postfix)
ok: [192.168.120.130] => (item=crond)
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.1.2循环散列或字典列表
loop列表不需要是简单值列表。在以下示例中,列表中的每个项实际上是散列或字典。示例中的每个散列或字典具有两个键,即name和groups,当前item循环变量中每个键的值可以分别通过item.name和item.groups变量来检索。
这一示例中结果是用户lilei存在且为组root的成员,并且用户lisi存在且为组root的成员。
[root@king playbook]# vim 123.yml
---
- hosts: all
tasks:
- name: 循环user模块
user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- name: lilei //如果用户不存在就present创建新用户归属于root组下
groups: root
- name: lisi
groups: root
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [循环user模块] ******************************************************************************
changed: [192.168.120.130] => (item={'name': 'lilei', 'groups': 'root'})
changed: [192.168.120.130] => (item={'name': 'lisi', 'groups': 'root'})
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.1.3 较早样式的循环关键字
在Ansible2.5之前,大多数playbook使用不同的循环语法。提供了多个循环关键字,前缀为whth_,后面跟Ansible查找插件的名称。这种循环语法在现有playbook中很常见,但在将来的某个时候可能会被弃用。
较早样式的Ansible循环
循环关键字:
with_items:行为与简单列表的loop关键字相同,例如字符串列表或散列/字典列表。但与loop不同的是,如果为with_items提供了列表的列表,它们将被扁平化为单级列表。循环变量item保存每次迭代过程中使用的列表项。
with_file:此关键字需要控制节点文件名列表。循环变量item在每次迭代过程中保存文件列表中相应文件的内容。
with_sequence:此关键字不需要列表,而是需要参数来根据数字序列生成值列表。循环变量item在每次迭代过程中保存生成的序列中的一个生成项的值。
playbook中的with_items的示例如下所示:
---
- hosts: 192.168.120.130
vars:
service:
- postfix
- mysql
- crond
tasks:
- name: " with_items "
debug:
with_items: "{{ service }}"
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [ with_items ] **********************************************************************
ok: [192.168.120.130] => (item=postfix) => {
"msg": "Hello world!"
}
ok: [192.168.120.130] => (item=mysql) => {
"msg": "Hello world!"
}
ok: [192.168.120.130] => (item=crond) => {
"msg": "Hello world!"
}
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
从Ansible2.5开始,建议使用loop关键字编写循环。
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
简单练习题
题目:
服务为:postfix mysql
状态:关闭服务
第一种直接用循环loop内容;第二种用变量操作。
(先测试执行看结果)
第一种:
[root@king playbook]# vim 123.yml
---
- hosts: 192.168.120.130
tasks:
- name: stop_service
service:
name: "{{ item }}"
state: stopped
loop:
- postfix
- mysql
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [stop_service] **************************************************************************
changed: [192.168.120.130] => (item=postfix)
changed: [192.168.120.130] => (item=mysql)
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
第二种:
[root@king playbook]# vim 123.yml
---
- hosts: 192.168.120.130
vars:
service:
- postfix
- mysql
tasks:
- name: stop_service
service:
name: "{{ item }}"
state: stopped
loop: "{{ service}}"
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [stop_service] **************************************************************************
changed: [192.168.120.130] => (item=postfix)
changed: [192.168.120.130] => (item=mysql)
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
1.1.4 将Register变量与Loop一起使用
register关键字也可以捕获循环任务的输出。以下代码片段显示了循环任务中register变量的结构:
[root@king playbook]# vim 123.yml
---
- hosts: all
gather_facts: no //不获取事实
tasks:
- name: echo
shell: "echo look {{ item }}" //输出变量内容命令
loop: //要循环的变量内容
- lisi
- lilei
register: result //使用已注册变量用register捕获命令输出
- name: show result
debug: var=result //debug把捕获输出内容打印在终端
---
- hosts: all
gather_facts: no
tasks:
- name: echo
shell: "echo look {{ item }}"
loop:
- lisi
- lilei
register: result
- debug: var=result 】
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [echo] **********************************************************************************
changed: [192.168.120.130] => (item=lisi)
changed: [192.168.120.130] => (item=lilei)
TASK [show result] ***************************************************************************
ok: [192.168.120.130] => {
"result": {
"changed": true,
"msg": "All items completed",
"results": [
......
}
},
"item": "lisi",
"rc": 0,
"start": "2021-11-09 16:26:40.491367",
"stderr": "",
"stderr_lines": [],
"stdout": "look lisi",
"stdout_lines": [
"look lisi"
]
.........
}
},
"item": "lilei",
"rc": 0,
"start": "2021-11-09 16:26:40.745050",
"stderr": "",
"stderr_lines": [],
"stdout": "look lilei",
"stdout_lines": [
"look lilei"
]
}
]
}
}
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
第二种写法,也可以不在debug前面加任务描述:
---
- hosts: all
gather_facts: no
tasks:
- name: echo
shell: "echo look {{ item }}"
loop:
- lisi
- lilei
register: result
- debug: var=result
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [all] ***********************************************************************************
TASK [echo] **********************************************************************************
skipping: [192.168.120.130] => (item=lisi)
skipping: [192.168.120.130] => (item=lilei)
TASK [debug] *********************************************************************************
ok: [192.168.120.130] => {
"result": {
"changed": false
........
在上面的例子中,results键包含一个列表。在下面,修改了playbook,使第二个任务迭代此列表:
---
- hosts: all
tasks:
- name: echo
shell: "echo hello {{ item }}"
loop:
- lisi
- lilei
register: result
- name: show result //此第二个任务迭代循环第一个任务
debug:
msg: result:"{{ item.stdout }}"
loop: "{{ result['results'] }}"
1.2有条件地运行任务
Ansible可使用conditionals在符合特定条件时执行任务或play。例如,可以利用一个条件在Ansible安装或配置服务前确定受管主机上的可用内存。
我们可以利用条件来区分不同的受管主机,并根据它们所符合的条件来分配功能角色。Playbook变量、注册的变量和Ansible事实都可通过条件来进行测试。可以使用比较字符串、数字数据和布尔值的运算符。
以下场景说明了在Ansible中使用条件的情况:
- 可以在变量中定义硬限制(如min_memory)并将它与受管主机上的可用内存进行比较。
- Ansible可以捕获并评估命令的输出,以确定某一任务在执行进一步操作前是否已经完成。例如,如果某一程序失败,则将路过批处理。
- 可以利用Ansible事实来确定受管主机网络配置,并决定要发送的模板文件(如,网络绑定或中继)。
- 可以评估CPU的数量,来确定如何正确调节某一Web服务器。
- 将注册的变量与预定义的变量进行比较,以确定服务是否已更改。例如,测试服务配置文件的MD5检验以和查看服务是否已更改。
1.1.2条件任务语法
when语句用于有条件地运行任务。它取要测试的条件为值。如果条件满足(是True),则运行(ok)任务。如果条件不满足(是False),则跳过(skipping)任务。
可以测试的一个最简单条件是某一布尔变量是True还是False。
以下示例中的when语句导致任务仅在定义变量内容为True时运行:
---
- hosts: 192.168.120.130
vars:
package_installed: True
tasks:
- name: install wget
yum:
name: wget
state: present
when: package_installed
[root@king playbook]# ansible-playbook 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install wget] **************************************************************************
ok: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
以下示例中的when语句导致任务仅在定义变量内容为False时运行:
---
- hosts: 192.168.120.130
vars:
package_installed: False
tasks:
- name: install wget
yum:
name: wget
state: present
when: package_installed
[root@king playbook]# ansible-playbook 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install wget] **************************************************************************
skipping: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
以下示例测试my_service变量是否具有值。若有值,则将my_service的值用作要安装的软件包的名称。如果未定义my_service变量,则跳过任务且不显示错误。
若有值时::
---
- hosts: 192.168.120.130
vars:
my_service: tree
tasks:
- name: service confirm installed
yum:
name: "{{ my_service }}"
state: present
when: my_service is defined
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [service confirm installed] *************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
如果未定义my_service变量时::
---
- hosts: 192.168.120.130
tasks:
- name: service confirm installed
yum:
name: "{{ my_service }}"
state: present
when: my_service is defined
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [service confirm installed] *************************************************************
skipping: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
下表显示了在处理条件时可使用的一些运算:
示例条件
操作 | 示例 |
---|---|
等于(值为字符串,字符都要用引号引起来) | ansible_machine == “x86_64” |
等于(值为数字) | max_memory == 512 |
小于 | min_memory < 128 |
大于 | min_memory > 256 |
小于等于 | min_memory <= 256 |
大于等于 | min_memory >= 512 |
不等于 | min_memory != 512 |
变量存在 | min_memory is defined |
变量不存在 | min_memory is not defined |
布尔变量是True。1、True或yes的求值为True | memory_available |
布尔变量是False。0、False或no的求值为False | not memory_available |
第一个变量的值存在,作为第二个变量的列表中的值 | ansible_facts [‘distribution’] in supported_distros |
ansible all -m setup|less //查看setup模块该受控主机详细信息
---
- hosts: 192.168.120.130
tasks:
- name: install service mysql
yum:
name: mysql
state: present
when: ansible_facts ['distribution_version'] >= "7.6"
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install service mysql] *****************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
上表中的最后一个条目初看起来有些令人困惑。下例演示了它的工作原理。
在示例中,ansible_distribution变量是在Gathering Facts任务期间确定的事实,用于标识托管主机的操作系统分支。变量supported_distros由playbook创建,包含该playbook支持的操作系统分发列表。如果ansible_distribution的值在supported_distros(支持版本)列表中,则条件通过且任务运行。
---
- hosts: 192.168.120.130
vars:
supported_distros:
- CentOS
- Fedora
tasks:
- name: install mysql
yum:
name: mysql
state: present
when: ansible_facts ['distribution'] in supported_distros
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install mysql] *************************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
注意when语句的缩进。由于when语句不是模块变量,它必须通过缩进到任务的最高级别,放置在模块的外面。
任务是YAML散列/字典,when语句只是任务中的又一个键,就如任务的名称以及它所使用的模块一样。通常的惯例是将可能存在的任何when关键字放在任务名称和模块(及模块参数)的后面。
1.2.2 测试多个条件
一个when语句可用于评估多个条件。使用and和or关键字组合条件,并使用括号分组条件。
如果任一条件为真时满足条件语句,则应当使用or语句。例如,如果计算机上运行的是红帽企业linux或Fedora,则下述条件得到满足:
---
- hosts: 192.168.120.130
tasks:
- name: install mysql
yum:
name: mysql
state: present
when: ansible_facts ['distribution'] == "RedHat" or ansible_facts ['distribution'] >= "CentOS"
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install mysql] *************************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
使用and运算时,两个条件都必须为真,才能满足整个条件语句。例如,如果远程主机是红帽企业Linux7.6主机,并且安装的内核是指定版本,则将满足以下条件:
---
- hosts: 192.168.120.130
tasks:
- name: install mysql
yum:
name: mysql
state: present
when: ansible_facts ['distribution_version'] == "7.6" and ansible_facts ['kernel'] == "3.10.0-957.el7.x86_64"
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install mysql] *************************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
when关键字还支持使用列表来描述条件列表。向when关键字提供列表时,将使用and运算组合所有条件。下面的示例演示了使用and运算符组合多个条件语句的另一方式:
---
- hosts: 192.168.120.130
tasks:
- name: install mysql
yum:
name: mysql
state: present
when:
- ansible_facts ['distribution_version'] <= "7.6"
- ansible_facts ['kernel'] == "3.10.0-957.el7.x86_64"
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install mysql] *************************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
这种格式提高了可读性,而可读性是良好编写Ansible Playbook的关键目标。
通过使用括号分组条件,可以表达更复杂的条件语句。例如,如果计算机上运行的是红帽企业Linux7.6或Fedora28,则下述条件语句得到满足。此示例使用大于字符 >,这样长条件就可以在playbook中分成多行,以便于阅读。如下:
---
- hosts: 192.168.120.130
tasks:
- name: install mysql
yum:
name: mysql
state: present
when: >
( ansible_facts ['distribution'] == "CentOS" and
ansible_facts ['distribution_version'] == "7.6")
or
( ansible_facts ['distribution'] == "Fedora" and
ansible_facts ['distribution_version'] == "28")
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install mysql] *************************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.3 组合循环和有条件任务
循环和条件可以组合使用。
在下例中,yum模块将安装mariadb-server软件包,只要 / 上挂载的文件系统具有超过300MB的可用空间。ansible_facts[‘mounts’] 事实是一组字典,各自代表一个已挂载文件系统的相关事实。循环迭代列表中每一字典,只有找到了代表两个条件都为真的已挂载文件系统的字典时,条件语句才得到满足。
---
- hosts: 192.168.120.130
tasks:
- name: mysql
yum:
name: mysql
state: present
loop: "{{ ansible_facts ['mounts'] }}"
when:
- item.mounts == "/"
- item.size_available == 17036374016
对某个任务结合使用when和loop时,将对每个项检查when语句。
下面是组合使用条件和注册变量的另一个示例:
---
- hosts: all
tasks:
- name: postfix service status
command: /usr/bin/systemctl is-active postfix //Postfix是否在运行
ignore_errors: yes //如果它不在运行并且命令失败,则不停止处理
register: result //将模块的结果信息保存在名为result的变量中
- name: Postfix激活并且重启httpd服务
service:
name: httpd
state: restarted
when: result.rc == 0 //评估Postfix任务的输出。如果systemctl命令的退出代码为0,则Postfix激活并且此任务重启httpd服务
2.实施处理程序
2.1 ansible处理程序
Ansible模块设计为具有幂等性。这表示,在正确编写的playbook中,playbook及其任务可以运行多次而不会改变受管主机,除非需要进行更改使受管主机进入所需的状态。
但在时候,在任务确实更改系统时,可能需要运行进一步的任务。例如,更改服务配置文件时可能要求重新加载该服务以便使其更改的配置生效。
处理程序是响应由其他任务触发的通知的任务。仅当任务在受管主机上更改了某些内容时,任务才通知其处理程序。每个处理程序具有全局唯一的名称,在playbook中任务块的末尾触发。如果没有任务通过名称通知处理程序,处理程序就不会运行。如果一个或多个任务通知处理程序,处理程序就会在play中的所有其他任务完成后运行一次。因为处理程序就是任务,所以可以在处理程序中使用他们将用于任何其他任务的模块。通常而言,处理程序被用于重新引导主机和重启服务。
处理程序可视为非活动任务,只有在使用notify语句显式调用时才会被触发。在下列代码片段中,只有配置文件更新并且通知了该任务,restart apache处理程序才会重启Apache服务器:
[root@king playbook]# vim /tmp/httpd.conf //配置文件已发生更改
[root@king playbook]# vim 123.yml
---
- hosts: all
tasks:
- name: install httpd //安装httpd
yum:
name: httpd
state: present
- name: 传输配置文件 //该任务是通知处理程序执行的关键
template:
src: /tmp/httpd.conf //源-绝对路径位置
dest: /etc/httpd/conf/httpd.conf //目的-绝对路径位置
notify: //notify语句指出该任务需要触发一个处理程序
- restart httpd //要运行处理程序的名称
handlers: // handlers关键字表示处理程序任务列表的开头
- name: restart httpd //此任务名称必须和处理名称一致
service: //处理程序执行模块.此时传输文件有并成功传输到受控主机上
name: httpd // 就会被触发,执行重启httpd服务
state: restarted
[root@king playbook]# ansible-playbook 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install_httpd] *************************************************************************
ok: [192.168.120.130]
TASK [传输配置文件] ********************************************************************************
changed: [192.168.120.130]
RUNNING HANDLER [restart httpd] **************************************************************
changed: [192.168.120.130] //配置文件发生更改并成功传输,触发处理程序.handlers处理程序任务就会执行
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
在上面的例子中,restart apache处理程序只有在template任务通知已发生更改时才会触发。一个任务可以在其notify部分中调用多个处理程序。Ansible将notify语句视为数组,并且迭代处理程序名称:
[root@king playbook]# vim /tmp/httpd.conf //配置文件已发生更改
[root@king playbook]# vim 123.yml
---
- hosts: 192.168.120.130
tasks:
- name: install_httpd
yum :
name: httpd
state: present
- name: 传输配置文件
template:
src: /tmp/httpd.conf
dest: /etc/httpd/conf/httpd.conf
notify:
- restart httpd
- restart crond
handlers:
- name: restart httpd
service:
name: httpd
state: restarted
- name: restart crond
service:
name: crond
state: restarted
[root@king playbook]# ansible-playbook 123.yml
PLAY [192.168.120.130] ***********************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install_httpd] *************************************************************************
ok: [192.168.120.130]
TASK [传输配置文件] ********************************************************************************
changed: [192.168.120.130]
RUNNING HANDLER [restart httpd] **************************************************************
changed: [192.168.120.130]
RUNNING HANDLER [restart crond] **************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2.2 使用处理程序的好处
使用处理程序时需要牢记几个重要事项:
- 处理程序始终按照play的handlers部分指定的顺序运行。它们不按在任务中由notify语句列出的顺序运行,或按任务通知它们的顺序运行。
- 处理程序通常在相关play中的所有其他任务完成后运行。playbook的tasks部分中某一任务调用的处理程序,将等到tasks下的所有任务都已处理后才会运行。
- 处理程序名称存在于各play命名空间中。如果两个处理程序被错误地给予相同的名称,则仅会运行一个。
- 即使有多个任务通知处理程序,该处理程序依然仅运行一次。如果没有任务通知处理程序,它就不会运行。
- 如果包含notify语句的任务没有报告changed结果(例如,软件包已安装并且任务报告ok),则处理程序不会获得通知。处理程序将被跳过,直到有其他任务通知它。只有相关任务报告了changed状态,Ansible才会通知处理程序。
处理程序用于在任务对受管主机进行更改时执行额外操作。它们不应用作正常任务的替代。
3. 处理任务失败
3.1 管理play中的任务错误
Ansible评估任务的返回代码,从而确定任务是成功还是失败。通常而言,当任务失败时,Ansible将立即在该主机上中止play的其余部分并且跳过所有后续任务。
但有些时候,可能希望即使在任务失败时也继续执行play。例如,或许预期待定任务有可能会失败,并且希望通过有条件地运行某项其他任务来修复。
Ansible有多种功能可用于管理任务错误。
3.2 忽略任务失败
默认情况下,任务失败时play会中止。不过,可以通过忽略失败的任务来覆盖此行为。可以在任务中使用ignore_errors关键字来实现此目的。
下列代码片段演示了如何在任务中使用ignore_errors,以便在任务失败时也继续在主机上执行playbook。例如,如果play软件包不存在,则yum模块将失败,但若将ignore_errors设为yes,则执行将继续。
---
- hosts: all
ignore_errors: yes
tasks:
- name: install play
yum:
name: play
state: present
测试:
[root@king playbook]# ansible-playbook -C 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [install play] **************************************************************************
fatal: [192.168.120.130]: FAILED! => {"changed": false, "msg": "No package matching 'play' found available, installed or updated", "rc": 126, "results": ["No package matching 'play' found available, installed or updated"]}
...ignoring
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1 //已忽略
3.3 任务失败后强制执行处理程序
通常而言,如果任务失败并且play在该主机上中止,则收到play中早前任务通知的处理程序将不会运行。如果在play中设置force_handlers: yes关键字,则即使play因为后续任务失败而中止也会调用被通知的处理程序。
下列代码片段演示了如何在play中使用force_handlers关键字,以便在任务失败时也强制执行相应的处理程序:
---
- hosts: all
force_handlers: yes
tasks:
- name: print in terminal
command: echo "cat"
notify: //notify语句指出该任务需要触发一个处理程序
- restart httpd //处理程序名称
- name: install play
yum:
name: play
state: present
handlers: //处理程序任务
- name: restart httpd
service:
name: httpd
state: restarted
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [print in terminal] *********************************************************************
changed: [192.168.120.130]
TASK [install play] **************************************************************************
fatal: [192.168.120.130]: FAILED! => {"changed": false, "msg": "No package matching 'play' found available, installed or updated", "rc": 126, "results": ["No package matching 'play' found available, installed or updated"]}
//任务失败时也会强制执行相应的处理程序
RUNNING HANDLER [restart httpd] **************************************************************
changed: [192.168.120.130] //处理程序任务已被强制执行成功
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=3 changed=2 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
❗处理程序会在任务报告changed结果时获得通知,而在任务报告ok或failed结果时不会获得通知。
3.4 指定任务失败条件
可以在任务中使用failed_when关键字来指定表示任务已失败的条件。这通常与命令模块搭配使用,这些模块可能成功执行了某一命令,但命令的输出可能指示了失败。
例如,可以运行输出错误消息的脚本(内容命令报错),并使用该消息定义任务的失败状态。下列代码片段演示了如何在任务中使用failed_when关键字:
---
- hosts: all
tasks:
- name: running
script: files/cat.sh
register: result
failed_when: "'No such file or directory' in result.stdout"
fail模块也可用于强制任务失败。上面的场景也可以编写为两个任务:
---
- hosts: all
tasks:
- name: running
script: files/cat.sh
register: result
ignore_errors: yes
- name: script fsiled
fail:
msg: "内容有错" //自定义报错结果--当报错时,会提醒自己什么样结果
when: "'No such file or directory' in result.stdout" //当出现此语句就报错不在执行
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [running] *******************************************************************************
changed: [192.168.120.130]
TASK [script fsiled] *************************************************************************
fatal: [192.168.120.130]: FAILED! => {"changed": false, "msg": "内容有错"}
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
我们可以使用fail模块为任务提供明确的失败消息。此方法还支持延迟失败,允许在运行中间任务以完成或回滚其他更改。
3.5 指定何时任务报告 “Changed” 结果
当任务对托管主机进行了更改时,会报告 changed 状态并通知处理程序。如果任务不需要进行更改,则会报告ok并且不通知处理程序。
changed_when关键字可用于控制任务在何时报告它已进行了更改。例如,下一示例中的shell模块将用于获取供后续任务使用的Kerberos凭据。它通常会在运行时始终报告changed。为抵制这种更改,应设置changed_when: false,以便它仅报告ok或failed。
---
- hosts: all
tasks:
- name: copy file
template:
src: files/cat.sh
dest: /root
changed_when: True //就算任务执行多次,执行结果始终是显示chenged
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [copy file] *****************************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [copy file] *****************************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
---
- hosts: all
tasks:
- name: copy file
template:
src: files/cat.sh
dest: /root
changed_when: False //设置changed_when: False,它仅报告ok或failed
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [copy file] *****************************************************************************
ok: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
3.6 Ansible块和错误处理
在playbook中,块是对任务进行逻辑分组的子句,可用于控制任务的执行方式。通过块,也可结合rescue和always语句来处理错误。如果块中的任何任务失败,则执行其rescue块中的任务来进行恢复。在block子句中的任务以及rescue子句中的任务(如果出现故障)运行之后,always子句中的任务运行。总结:
- block:定义要运行的主要任务
- rescue:定义要在block子句中定义的任务失败时运行的任务
- always:定义始终都独立运行的任务,不论block和rescue子句中定义的任务是成功还是失败
以下示例演示了如何在playbook中实施块。即使block子句中定义的任务失败,rescue和always子句中定义的任务也会执行。block中的when条件也会应用到其rescue和always子句(若存在)。例子:
---
- hosts: all
tasks:
- name: both
block:
- name: run1
command: ping 192.168.120.128
rescue: //rescue只会在block任务执行错误时,才会启动执行
- name: run2
command: cat yyuu111
always:
- name: run2-
command: echo "127.0.0.0"
when: ansible_facts ['distribution'] == "CentOS"
[root@king playbook]# ansible-playbook 123.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.120.130]
TASK [run1] **********************************************************************************
changed: [192.168.120.130]
TASK [run2-] *********************************************************************************
changed: [192.168.120.130]
PLAY RECAP ***********************************************************************************
192.168.120.130 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0