在日常使用ansible的过程中会发现,执行shell或command模块时,Ansible只认为0退出状态码是正确的,其它所有退出状态码都是失败的,但我们自己知道非0退出状态码并非一定代表着失败。
比如下例:
1 2 3 4 5 6 7 8
| - hosts: localhost gather_facts: false tasks: - shell: > ls /home/aa register: res - debug: var: res.stdout
|
执行结果会直接报错:
1 2 3 4 5
| TASK [shell] ***************************************************************************************************** fatal: [localhost]: FAILED! => {"changed": true, "cmd": "ls /home/aa\n", "delta": "0:00:00.034093", "end": "2021-12-02 14:16:45.029276", "msg": "non-zero return code", "rc": 2, "start": "2021-12-02 14:16:44.995183", "stderr": "ls: 无法访问/home/aa: 没有那个文件或目录", "stderr_lines": ["ls: 无法访问/home/aa: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
PLAY RECAP ******************************************************************************************************* localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
|
默认情况下,Ansible端无法连接某个节点时、某节点执行某个任务失败时,Ansible都会将这个节点从活动节点列表中(即play_hosts
变量中)移除,以避免该节点继续执行之后的任务。用户可以去修改Ansible对这种异常现象的默认处理方式,比如遇到错误也不让该节点退出舞台,而是继续执行后续任务,又或者某节点执行任务失败并让整个play都失败。
fail模块
使用fail
模块,可以人为制造一个失败的任务。
1 2 3 4 5 6 7 8 9
| --- - hosts: test gather_facts: false tasks: - fail: msg: "已手动设置失败" when: inventory_hostname == '192.168.100.10' - debug: msg: "成功了"
|
上面的fail会任务失败,并使得此节点不会执行后续任务,但其它节点会继续执行任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| PLAY [test] ******************************************************************************************************
TASK [fail] ****************************************************************************************************** fatal: [192.168.100.10]: FAILED! => {"changed": false, "msg": "已手动设置失败"} skipping: [192.168.100.11]
TASK [debug] ***************************************************************************************************** ok: [192.168.100.11] => { "msg": "成功了" }
PLAY RECAP ******************************************************************************************************* 192.168.100.10 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 192.168.100.11 : ok=1 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
|
assert模块
assert
与其他编程语言中的功能一致,断言功能,对于当满足某某条件时就失败的逻辑,可直接使用assert
模块实现。
例如上面的例子可改为:
1 2 3 4 5 6 7 8 9
| --- - hosts: test gather_facts: false tasks: - assert: that: - inventory_hostname != '192.168.100.10' fail_msg: "失败了" success_msg: "成功了"
|
其中that
参数接收一个列表,用于定义一个或多个条件,如果条件全为true,则任务成功,只要有一个条件为false,则任务失败。fail_msg
(或其别名参数msg
)定义任务失败时的信息,success_msg
定义任务成功时的信息。
ignore_errors
当某个任务执行失败(或被Ansible认为失败,比如通过返回值判断)时,如果不想让这个失败的任务导致节点退出,可以使用ignore_errors
指令来忽略失败。
例如开头的那个例子,加上ignore_errors
指令:
1 2 3 4 5 6 7 8 9
| - hosts: localhost gather_facts: false tasks: - shell: > ls /home/aa ignore_errors: yes register: res - debug: var: res.stdout
|
输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| PLAY [localhost] *************************************************************************************************
TASK [shell] ***************************************************************************************************** fatal: [localhost]: FAILED! => {"changed": true, "cmd": "ls /home/aa\n", "delta": "0:00:00.034821", "end": "2021-12-02 14:41:13.638790", "msg": "non-zero return code", "rc": 2, "start": "2021-12-02 14:41:13.603969", "stderr": "ls: 无法访问/home/aa: 没有那个文件或目录", "stderr_lines": ["ls: 无法访问/home/aa: 没有那个文件或目录"], "stdout": "", "stdout_lines": []} ...ignoring
TASK [debug] ***************************************************************************************************** ok: [localhost] => { "res.stdout": "" }
PLAY RECAP ******************************************************************************************************* localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
|
可以看到,加上ignore_errors
后成功执行了debug任务,最后的任务汇总处的ignored=1证明忽略了一个错误。但是从结果中可以看到,虽然确实忽略了错误,但红红报错信息仍然提醒在终端上,让不了解此机制的人感觉很慌。这时可以使用failed_when
解决。
failed_when
failed_when
指令可以让用户自己定义任务何时失败:当条件表达式为true时任务强制失败,当条件表达式为false时,任务强制不失败。
接着改下上面的例子:
1 2 3 4 5 6 7 8 9
| - hosts: localhost gather_facts: false tasks: - shell: > ls /home/aa failed_when: false register: res - debug: var: res.stdout
|
此时查看输出,已经没有相关报错了:
1 2 3 4 5 6 7 8 9 10 11 12
| PLAY [localhost] *************************************************************************************************
TASK [shell] ***************************************************************************************************** changed: [localhost]
TASK [debug] ***************************************************************************************************** ok: [localhost] => { "res.stdout": "" }
PLAY RECAP ******************************************************************************************************* localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
|
failed_when
经常会和shell或command模块以及register指令一起使用,用来手动定义失败的退出状态码。比如,退出状态码为0 1 2
都认为任务成功执行,其它状态码都认为认为执行失败。
1 2 3 4 5 6 7 8 9
| - hosts: localhost gather_facts: false tasks: - shell: > ls /home/aa failed_when: res.rc not in (0,1,2) register: res - debug: var: res.stdout
|
failed_when
和when
一样都可以将多个条件表达式写成列表的形式来表示逻辑与。
rescue和always
Ansible允许在任务失败的时候,去执行某些任务,还允许不管任务失败与否,都执行某些任务。
- rescue和always都是block级别的指令
- rescue表示block中任意任务失败后,都执行rescue中定义的任务,但如果block中没有任务失败,则不执行rescue中的任务
- always表示block中任务无论失败与否,都执行always中定义的任务
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| --- - hosts: localhost gather_facts: false tasks: - block: - fail: - debug: msg="hello world" rescue: - debug: msg="rescue1" - debug: msg="rescue2" always: - debug: msg="always1" - debug: msg="always2"
|
输出为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| PLAY [localhost] *************************************************************************************************
TASK [fail] ****************************************************************************************************** fatal: [localhost]: FAILED! => {"changed": false, "msg": "Failed as requested from task"}
TASK [debug] ***************************************************************************************************** ok: [localhost] => { "msg": "rescue1" }
TASK [debug] ***************************************************************************************************** ok: [localhost] => { "msg": "rescue2" }
TASK [debug] ***************************************************************************************************** ok: [localhost] => { "msg": "always1" }
TASK [debug] ***************************************************************************************************** ok: [localhost] => { "msg": "always2" }
PLAY RECAP ******************************************************************************************************* localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=1 ignored=0
|
block中的fail任务会失败,于是跳转到rescue中开始执行任务,然后再跳转到always中执行任务。
如果注释掉block中的fail模块任务,则block中没有任务失败,于是rescue中的任务不会执行,但是在执行完block中所有任务后会跳转到always中继续执行任务。
处理连接失败(unreachable)的异常
如果Ansible突然和某个节点无法连接上,会将此节点设置为UNREACHABLE状态,并从活动节点列表(play_hosts
)中删除。
如果想要忽略连接失败的节点,可设置ignore_unreachable: true
指令,该指令是Ansible 2.7添加的,可设置在play、Role、block、task级别上。
当Ansible遇到UNREACHABLE时,会进行连接重试。重试次数可在Ansible配置文件中配置:
1 2
| [root@slions_pc1 ansible_poc]# cat /etc/ansible/ansible.cfg |grep -w retries #retries = 3
|