ansible条件errors处理

时间:2023-02-21 11:19:29

一、概述

当Ansible从一个命令接收到一个非零的返回码或从一个模块接收到一个故障时,默认情况下它会停止在该主机上执行,并在其他主机上继续执行。

然而,在某些情况下,您可能需要不同的行为。有时,非零返回码表示成功。有时,您希望一台主机上的故障停止所有主机上的执行。Ansible提供了
处理这些情况的工具和设置,并帮助您获得所需的行为、输出和报告。

Ignoring failed commands 忽略命令失败
Ignoring unreachable host errors 忽略不可达的主机错误
Resetting unreachable hosts 重新设置不可到达的主机
Handlers and failure 处理程序和失败
Defining failure 定义失败
Defining “changed”定义“改变”
Ensuring success for command and shell 确保成功的命令和shell
Aborting a play on all hosts 在所有主机上终止一个play
Aborting on the first error: any_errors_fatal 在第一个错误时中止:any_errors_fatal
Setting a maximum failure percentage 设置最大失败百分比
Controlling errors in blocks 在blocks区块上控制errors

二、配置

1、忽略命令失败

默认情况下,当主机上的任务失败时,Ansible会停止执行该主机上的任务。使用ignore_errors,即使碰到任务失败,仍会继续下面的任务:
- name: Do not count this as a failure
shell: /bin/false
ignore_errors: yes
ignore_errors指令仅在任务能够运行并返回值为failed时有效。它不会使Ansible忽略未定义的变量错误、连接失败、执行问题(例如,丢失包)或语法错误。

2、忽略不可达的主机错误

您可以使用ignore_unreachable关键字忽略由于主机实例为UNREACHABLE 而导致的任务失败。Ansible忽略任务错误,但继续对不可达的主机执行后续的任务。
1)在task中使用:
# task1中忽略不可达的主机继续执行;task2不忽略不可达主机,且该不可达主机终止执行
tasks:
- name: task1
command: /bin/true
ignore_unreachable: yes
- name: task2
command: /bin/true

2)在play中使用:
# 所有tasks忽略不可达的主机,task1:忽略不可达主机继续执行play,tasks2:不忽略不可达主机且该不可达主机终止执行
- hosts: all
ignore_unreachable: yes
tasks:
- name: task1
command: /bin/true
- name: task2
command: /bin/true
ignore_unreachable: no

3、重新设置不可到达的主机

如果Ansible无法连接到某个主机,它会将该主机标记为unreachable,并将其从运行的活动主机列表中删除。
您可以使用meta: clear_host_errors重新激活所有主机,以便后续任务可以尝试再次访问它们。

4、处理程序和失败

如果一个任务通知了一个handler ,但另一个任务在稍后的运行中失败,默认情况下handler 不会在该主机上运行,这可能会使主机处于意外状态。
例如,一个任务可以更新配置文件并通知handler 重新启动某些服务。如果同一task中稍后的任务失败,则可能只会更改配置文件,而不会重新启动服务。

你可以通过--force-handlers命令行选项来改变这种行为,方法是在play中包含force_handlers: True,或者在ansible.cfg中添加force_handlers = True。
当handler 被强制执行时,Ansible将在所有主机上运行所有通知的handler ,甚至是任务失败的主机。
(注意,某些错误仍然会阻止处理程序运行,比如主机变得不可访问。)

5、定义失败

Ansible允许你在每个任务中使用failed_when条件定义“failure”的含义。
多个failed_when条件的列表使用隐式的and连接,这意味着任务只有在满足所有条件时才会失败。如果希望在满足任何条件时触发失败,则必须使用
显式或操作符在字符串中定义条件。

您可以通过在命令的输出中搜索一个单词或短语来检查是否失败:
- name: Fail task when the command error output prints FAILED
command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"

基于返回码:
- name: Fail task when both files are identical
raw: diff foo/file1 bar/file2
register: diff_cmd
failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2

还可以组合多个失败条件。如果两个条件都为真,该任务将失败:
# 检查某个文件是否存在
- name: Check if a file exists in temp and fail task if it does
command: ls /tmp/file1
register: result
failed_when:
- result.rc == 0
- '"No such" not in result.stdout'

# 如果你想让任务在只满足任何一个条件时失败,将failed_when的定义修改为:
failed_when: result.rc == 0 or "No such" not in result.stdout
如果有太多的条件不能整齐地放在一行中,可以使用“>”将其分割为多行yaml值:
- name: example of many failed_when conditions with OR
shell: "./myBinary"
register: ret
failed_when: >
("No such file or directory" in ret.stdout) or
(ret.stderr != '') or
(ret.rc == 10)

6、定义“改变”

Ansible允许你使用条件changed_when来定义一个特定的任务何时“changed”了一个远程节点。这让您可以根据返回代码或输出来决定是否应该在Ansible统计中报告更改,以及是否应该触发处理程序。
与Ansible中的所有条件一样,多个changed_when条件的列表使用隐式的“and”连接,这意味着任务只在满足所有条件时报告更改。

如果希望在满足任何条件时报告更改,则必须使用显式“or“在字符串中定义条件。例如:
tasks:
- name: Report 'changed' when the return code is not equal to 2
shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"

- name: This will never report 'changed' status
shell: wall 'beep'
changed_when: False

你也可以组合多个条件来覆盖" changed "结果:
- name: Combine multiple conditions to override 'changed' result
command: /bin/fake_command
register: result
ignore_errors: True
changed_when:
-'"ERROR" in result.stderr'
-result.rc == 2

7、确保命令和shell成功

command 和shell模块关心返回码,所以如果你有一个成功退出码不为零的命令,你可以这样做:

tasks:
- name: Run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true

8、在所有主机上终止一个play

有时,您希望单个主机或一定百分比的主机出现failure,从而中止所有主机上的整个play。使用any_errors_fatal可以在第一次失败发生后停止
play执行。对于更细粒度的控制,您可以使用max_fail_percentage在给定百分比的主机失败后中止运行。

在第一个错误时中止:any_errors_fatal
如果你设置了any_errors_fatal并且一个任务返回了一个错误,Ansible会在当前批处理的所有主机上完成致命任务,然后停止在所有主机上执行剧
本。后续的任务和剧本不会被执行。你可以在play或block级别设置any_errors_fatal:
- hosts: somehosts
any_errors_fatal: true
roles:
-myrole

通过向块中添加一个拯救部分,可以从致命错误中恢复运行。

- hosts: somehosts
tasks:
- block:
-include_tasks: mytasks.yml
any_errors_fatal: true

当所有任务必须100%成功才能继续执行剧本时,您可以使用此特性。例如,如果您在多个数据中心的机器上运行一个服务,使用负载平衡器将流量从用
户传递到服务,那么您希望在停止服务进行维护之前禁用所有负载平衡器。为了确保任务中任何禁用负载均衡器的失败都会停止所有其他任务:
---
- hosts: load_balancers_dc_a
any_errors_fatal: true
tasks:
- name: Shut down datacenter 'A'
command: /usr/bin/disable-dc

- hosts: frontends_dc_a
tasks:
- name: Stop service
command: /usr/bin/stop-software
- name: Update software
command: /usr/bin/upgrade-software

- hosts: load_balancers_dc_a
tasks:
- name: Start datacenter 'A'
command: /usr/bin/enable-dc

在这个例子中,只有当所有的负载均衡器都被成功禁用时,Ansible才会在前端启动软件升级。

设置最大失败百分比:max_fail_percentage
默认情况下,只要有主机没有发生故障,Ansible就会继续执行任务。在某些情况下,例如在执行滚动更新时,当达到一定的失败阈值时,您可能想要
中止play。为了实现这一点,你可以设置play的最大失败百分比:
---
- hosts: webservers
max_fail_percentage: 30
serial: 10

max_fail_percentage设置在与serial一起使用时适用于每个批处理。在上面的例子中,如果第一批(或任何一批)服务器中的10个服务器中有3个以
上失败,那么剩下的play将被中止。

9、block

在说block用法之前,先说一下ansible的yaml文件中判断的使用,符合when的条件的时候,执行,不满足条件就不执行,例如:
- name: when用法举例
hosts: all
tasks:
- name: 修改文件权限
file: src=/root/test.txt.j2 dest=/opt/test.txt mode=0644
when: '$USER=root'

再来看block的例子:
- name: block的用法
hosts: node
tasks:
- debug:
msg: "task1 not in block"
- block:
- debug:
msg: "task2 in block1"
- debug:
msg: "task3 in block1"
when: 2 > 1
是的,当when的判断语句一样时,可以将任务合并,写起来省力一点。此外,block除了能和when结合起来使用,还有一个很重要的功能,就是"错误处理"功能。
不用block:
- hosts: test
remote_user: root
tasks:
- shell: 'cat /etc/redhat-release'
register: stdout_info
ignore_errors: true
rescue:
- debug:
msg: 'I caught an error'
when: 'stdout_info is failed'

使用block:
- hosts: test
remote_user: root
tasks:
- block:
- shell: 'cat /etc/redhat-release'
rescue:
- debug:
msg: 'I caught an error'

如上例所示,定义了一个block,这个block中有一个任务,这个任务在目标主机中执行了’‘cat /etc/redhat-release’’'命令,除了block关键字
,还有另外一个关键字rescue,rescue关键字与block关键字对齐,rescue的字面意思为"救援",表示当block中的任务执行失败时,会执行
rescue中的任务进行补救,当然,在rescue中定义什么任务,是由你决定的。
也就是说当block中的任务出错时,会执行rescue中的任务,当block中的任务顺利执行时,则不会执行rescue中的任务。

你可能会问,使用block的方法完成"错误处理"的功能,似乎与使用failed的方法并没有什么不同,除了代码似乎"精简"了一点,block还有其他优势
么?其实,使用block的方式还是有一定优势的,当block中有多个任务时,这种优势就比较明显了:
---
- hosts: testuser
remote_user: root
tasks:
- block:
- debug:
msg: 'I execute normally'
- command: /bin/false
- debug:
msg: 'I never execute, due to the above task failing'
rescue:
- debug:
msg: 'I caught an error'
- command: /bin/false
- debug:
msg: 'I also never execute'
always:
- debug:
msg: "This always executes"
when: 2>1