使用 block 和 rescue 处理故障

AnsibleBeginner
立即练习

介绍

Ansible 的 block(块)可以将相关的任务组合在一起。当块中的任务失败时,rescue(救援)部分会运行;而 always(始终)部分则无论块中任务成功与否都会运行。

在本实验中,你将创建一个受控故障并从中恢复,然后通过禁用故障再次运行同一个 Playbook,以观察成功路径的执行情况。

创建故障处理项目

在此步骤中,你将创建一个小型项目,其中包含一个本地受管主机,以及用于存放 block、rescue 和 always 标记的 reports 目录。

cd /home/labex/project
mkdir -p rhce-ansible/{inventory,playbooks,reports}
cd rhce-ansible
cat > ansible.cfg <<'EOF'
[defaults]
inventory = ./inventory/hosts
host_key_checking = False
retry_files_enabled = False
EOF
cat > inventory/hosts <<'EOF'
[managed]
host ansible_connection=local
EOF

reports 目录用于存放 Playbook 在每条路径下生成的标记文件。这使得我们无需仅依赖终端输出,就能直观地看到块的行为。

编写包含 rescue 和 always 的块

在此步骤中,你将编写一个包含受控故障的 Playbook。当 simulate_failure 为 true 时,块会失败,rescue 部分会写入一个恢复标记,而 always 部分则会写入一个审计标记。 在运行此步骤的命令之前,请返回 Ansible 项目目录。这样即使 LabEx 分步运行,命令也能保持稳定。

cd /home/labex/project/rhce-ansible
cat > playbooks/block-rescue.yml <<'EOF'
---
- name: Practice block rescue and always
  hosts: managed
  gather_facts: false
  vars:
    simulate_failure: true
  tasks:
    - name: Remove old markers
      ansible.builtin.file:
        path: "/home/labex/project/rhce-ansible/reports/{{ item }}.txt"
        state: absent
      loop:
        - block-success
        - rescued
        - always

    - name: Protect a risky operation with rescue
      block:
        - name: Run a command that may fail
          ansible.builtin.command: /usr/bin/false
          when: simulate_failure | bool

        - name: Mark block success
          ansible.builtin.copy:
            dest: /home/labex/project/rhce-ansible/reports/block-success.txt
            content: |
              block completed
            mode: '0644'
      rescue:
        - name: Mark rescue path
          ansible.builtin.copy:
            dest: /home/labex/project/rhce-ansible/reports/rescued.txt
            content: |
              rescued controlled failure
            mode: '0644'
      always:
        - name: Mark always path
          ansible.builtin.copy:
            dest: /home/labex/project/rhce-ansible/reports/always.txt
            content: |
              always ran
            mode: '0644'
EOF

其中的风险命令是 /usr/bin/false,它总是以失败退出。当 simulate_failure 为 true 时,块会在该失败任务处停止,rescue 部分会写入 rescued.txt,而 always 部分则无论如何都会写入 always.txt。

ansible-playbook playbooks/block-rescue.yml --syntax-check

block、rescue 和 always 关键字必须在相同的缩进级别对齐。在运行受控故障之前,进行语法检查是快速发现段落位置错误的有效方法。

运行受控故障路径

在此步骤中,你将运行启用了默认故障的 Playbook。Play 的汇总信息(recap)应该显示一个已救援(rescued)的任务,而不是失败的 Play。 在运行此步骤的命令之前,请返回 Ansible 项目目录。

cd /home/labex/project/rhce-ansible
ansible-playbook playbooks/block-rescue.yml | tee reports/failure-path.txt

预期此次运行在汇总信息中显示 rescued=1 和 failed=0。这意味着 Ansible 检测到块内任务失败,执行了救援任务,并且没有将整个 Play 视为失败。

cat reports/rescued.txt reports/always.txt

这两个标记文件应该分别显示 rescued controlled failure 和 always ran。在此路径下,你不应该看到 block-success.txt,因为块没有执行到成功标记任务。

使用额外变量运行成功路径

在此步骤中,你将在命令行覆盖 simulate_failure 变量。块应该正常完成,rescue 部分不应运行,而 always 部分仍应运行。 在运行此步骤的命令之前,请返回 Ansible 项目目录。

cd /home/labex/project/rhce-ansible
ansible-playbook playbooks/block-rescue.yml -e simulate_failure=false | tee reports/success-path.txt

-e simulate_failure=false 选项仅针对本次运行覆盖 Playbook 变量。现在 false 命令被跳过,正常的块会执行到 block-success.txt,且 rescue 部分不会运行。

cat reports/block-success.txt reports/always.txt

成功路径应该显示 block completed 和 always ran。由于 Playbook 在开始时会删除旧标记,因此运行后 rescued.txt 应该不再存在。

比较故障与成功证据

在此步骤中,你将比较两次保存的 Playbook 运行结果。保持证据简洁有助于在不阅读全部输出的情况下排查哪条路径被执行了。 在运行此步骤的命令之前,请返回 Ansible 项目目录。

cd /home/labex/project/rhce-ansible
grep -E 'rescued=|failed=' reports/failure-path.txt reports/success-path.txt

故障路径报告应包含 rescued=1,而成功路径报告应包含 rescued=0。两者都应显示 failed=0,但原因不同。

ls -1 reports

报告列表应包含两次保存的 Playbook 输出以及最近一次路径生成的标记。保留这两份输出可以更轻松地对比 rescue 如何改变汇总信息。

总结

你使用了 block、rescue 和 always 部分来处理受控故障,然后通过正常的成功路径运行了同一个 Playbook,并比较了保存的证据。