剧本中的错误处理
当 Ansible 从命令接收非零返回值或从模块接收失败时,默认情况下它会停止在该主机上的执行,并在其他主机上继续执行。但是,在某些情况下,您可能需要不同的行为。有时非零返回值表示成功。有时您希望在一台主机上的失败停止所有主机的执行。Ansible 提供工具和设置来处理这些情况,并帮助您获得所需的 behavior、output 和 reporting。
忽略失败的命令
默认情况下,当任务在主机上失败时,Ansible 会停止在主机上的执行任务。您可以使用 ignore_errors
来继续执行,即使任务失败。
- name: Do not count this as a failure
ansible.builtin.command: /bin/false
ignore_errors: true
ignore_errors
指令仅在任务可以运行并返回值为“失败”时有效。它不会使 Ansible 忽略未定义的变量错误、连接失败、执行问题(例如,缺少软件包)或语法错误。
忽略无法访问的主机错误
2.7 版本中的新功能。
您可以使用 ignore_unreachable
关键字忽略由于主机实例“无法访问”而导致的任务失败。Ansible 会忽略任务错误,但会继续对无法访问的主机执行后续任务。例如,在任务级别
- name: This executes, fails, and the failure is ignored
ansible.builtin.command: /bin/true
ignore_unreachable: true
- name: This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
在剧本级别
- hosts: all
ignore_unreachable: true
tasks:
- name: This executes, fails, and the failure is ignored
ansible.builtin.command: /bin/true
- name: This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
ignore_unreachable: false
重置无法访问的主机
如果 Ansible 无法连接到主机,它会将该主机标记为“无法访问”,并将其从运行的活动主机列表中删除。您可以使用 meta: clear_host_errors 重新激活所有主机,以便后续任务可以再次尝试访问它们。
处理程序和故障
Ansible 在每个剧本结束后运行 处理程序。如果一个任务通知一个处理程序,但另一个任务在剧本中后来失败了,默认情况下该处理程序 *不会* 在该主机上运行,这可能会导致主机处于意外状态。例如,一个任务可以更新配置文件并通知一个处理程序重启某个服务。如果同一个剧本中稍后的任务失败了,配置文件可能会被更改,但服务不会被重启。
您可以使用 --force-handlers
命令行选项、在剧本中包含 force_handlers: True
,或在 ansible.cfg 中添加 force_handlers = True
来更改此行为。当强制处理程序时,Ansible 会在所有主机上运行所有已通知的处理程序,即使是具有失败任务的主机。(注意,某些错误仍然可能阻止处理程序运行,例如主机变得无法访问。)
定义故障
Ansible 允许您使用 failed_when
条件语句在每个任务中定义“故障”的含义。与 Ansible 中的所有条件语句一样,多个 failed_when
条件语句列表隐式地用 and
连接,这意味着只有当 *所有* 条件都满足时,任务才会失败。如果您希望在任何条件满足时触发故障,则必须在字符串中定义条件,并使用显式的 or
运算符。
您可以通过在命令输出中搜索单词或短语来检查故障
- name: Fail task when the command error output prints FAILED
ansible.builtin.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
ansible.builtin.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
ansible.builtin.command: ls /tmp/this_should_not_be_here
register: result
failed_when:
- result.rc == 0
- '"No such" not in result.stderr'
如果您希望在只有一个条件满足时任务失败,请将 failed_when
定义更改为
failed_when: result.rc == 0 or "No such" not in result.stderr
如果条件太多而无法整齐地放在一行,则可以使用 >
将其拆分为多行 YAML 值。
- name: example of many failed_when conditions with OR
ansible.builtin.shell: "./myBinary"
register: ret
failed_when: >
("No such file or directory" in ret.stdout) or
(ret.stderr != '') or
(ret.rc == 10)
定义“已更改”
Ansible 允许您使用 changed_when
条件语句来定义何时某个任务“更改”了远程节点。这使您可以根据返回值或输出确定是否应该在 Ansible 统计信息中报告更改,以及是否应该触发处理程序。与 Ansible 中的所有条件语句一样,多个 changed_when
条件语句列表隐式地用 and
连接,这意味着只有当 *所有* 条件都满足时,任务才会报告更改。如果您希望在任何条件满足时报告更改,则必须在字符串中定义条件,并使用显式的 or
运算符。例如
tasks:
- name: Report 'changed' when the return code is not equal to 2
ansible.builtin.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
ansible.builtin.shell: wall 'beep'
changed_when: False
- name: This task will always report 'changed' status
ansible.builtin.command: /path/to/command
changed_when: True
您还可以将多个条件组合起来以覆盖“已更改”结果。
- name: Combine multiple conditions to override 'changed' result
ansible.builtin.command: /bin/fake_command
register: result
ignore_errors: True
changed_when:
- '"ERROR" in result.stderr'
- result.rc == 2
注意
与 when
一样,这两个条件语句不需要模板分隔符({{ }}
),因为它们是隐式的。
有关更多条件语句语法示例,请参阅 定义故障。
确保命令和 shell 成功
command 和 shell 模块关心返回值,因此如果您有一个成功的退出代码不是零的命令,您可以这样做
tasks:
- name: Run this command and ignore the result
ansible.builtin.shell: /usr/bin/somecommand || /bin/true
在所有主机上中止一个剧本
有时您希望一台主机的失败,或一定百分比的主机的失败,中止所有主机上的整个剧本。您可以使用 any_errors_fatal
在第一个失败发生后停止剧本执行。为了更细粒度的控制,您可以使用 max_fail_percentage
在超过一定百分比的主机失败后中止运行。
在第一个错误时中止:any_errors_fatal
如果设置了 any_errors_fatal
并且任务返回错误,Ansible 将在当前批次中的所有主机上完成致命任务,然后停止在所有主机上执行剧本。后续任务和剧本不会执行。可以通过在块中添加一个 rescue 部分 来从致命错误中恢复。可以在剧本或块级别设置 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'
ansible.builtin.command: /usr/bin/disable-dc
- hosts: frontends_dc_a
tasks:
- name: Stop service
ansible.builtin.command: /usr/bin/stop-software
- name: Update software
ansible.builtin.command: /usr/bin/upgrade-software
- hosts: load_balancers_dc_a
tasks:
- name: Start datacenter 'A'
ansible.builtin.command: /usr/bin/enable-dc
在此示例中,只有在所有负载均衡器成功禁用后,Ansible 才会启动前端的软件升级。
设置最大失败百分比
默认情况下,只要有主机尚未失败,Ansible 就会继续执行任务。在某些情况下,例如执行滚动更新时,您可能希望在达到某个失败阈值时中止剧本。为了实现这一点,您可以在剧本上设置最大失败百分比
---
- hosts: webservers
max_fail_percentage: 30
serial: 10
当与 serial 一起使用时,max_fail_percentage
设置适用于每个批次。在上面的示例中,如果第一批(或任何批次)服务器中的 10 台服务器中有超过 3 台服务器出现故障,则将中止剧本的其余部分。
注意
必须超过设置的百分比,而不是等于它。例如,如果 serial 设置为 4,并且希望在 2 个系统出现故障时中止剧本,则将 max_fail_percentage 设置为 49 而不是 50。
控制块中的错误
还可以使用块来定义对任务错误的响应。此方法类似于许多编程语言中的异常处理。有关详细信息和示例,请参阅 使用块处理错误。
另请参阅
- Ansible 剧本
剧本简介
- 一般提示
剧本提示和技巧
- 条件语句
剧本中的条件语句
- 使用变量
关于变量的一切
- 沟通
有问题?需要帮助?想要分享你的想法?请访问 Ansible 沟通指南