运行维护命令并处理失败

AnsibleBeginner
立即练习

介绍

将现有维护程序集成到声明式自动化时需要谨慎。你将区分 command、shell 和 script,安全传递带空格路径,对只读结果分类,并为真实脚本输出设置执行保护。管道和委派的控制节点回执将使执行位置和失败传播可观察。你还将触发并恢复一次真实的输入缺失故障。

使用安全参数直接运行命令

本步骤将通过 command 检查受管输入,保留带空格路径,并正确分类只读操作。

状态管理优先使用 copy 和 file 等声明式模块。集成现有可执行文件或维护程序时,command、shell 和 script 很有用。command 不通过 shell 运行受管可执行文件,因此不解释管道和重定向。其 argv 形式将各参数分开,包括带空格路径。

预先准备的各目标均有 /home/automation/maintenance input.txt,包含一行批准数据,以及 maintenance-ready 前置输入。控制节点在 scripts/ 下提供了后续使用的现有脚本。请保留这些输入。

进入控制节点项目:

cd /home/labex/project

创建使用 argv 的 playbook。带空格路径是一个 YAML 列表条目。register 捕获可执行文件结果,包括 stdout 及数字返回码 rc。零通常表示成功;非零命令返回码通常使任务失败。

cat > maintenance.yml <<'YAML'
- name: Inspect maintenance inputs safely
  hosts: all
  gather_facts: false
  tasks:
    - name: Read the managed input with a space-safe argument list
      ansible.builtin.command:
        argv:
          - /usr/bin/cat
          - /home/automation/maintenance input.txt
      register: input_result
      changed_when: false

    - name: Retain the inspected input in a managed record
      ansible.builtin.copy:
        dest: /home/automation/command-result.txt
        content: "{{ input_result.stdout }}\n"
        mode: "0640"
YAML

cat 命令只读,因此 changed_when false 将其结果分类为未更改。此设置改变报告,不改变命令实际行为;不得用于隐藏会修改状态的程序产生的更改。copy 任务使用返回的 stdout,写入具有明确结尾换行的真实受管记录。

检查并运行 playbook:

ansible-playbook maintenance.yml --syntax-check
ansible-playbook maintenance.yml

两个读取任务都应报告 ok,首次记录复制则更改各目标。检查实际记录:

ansible all -m ansible.builtin.command -a 'cat /home/automation/command-result.txt'

各记录应包含 Approved maintenance data。在此 core 版本中,command 即使不调用 shell,也能通过 Python 展开环境变量参数。需要以美元符开头的字面参数时,使用 expand_argument_vars false;此固定路径无需该设置。

传输控制节点脚本并保护其真实输出

本步骤将在两个目标上调用提供的控制节点维护脚本,避免不必要的重复执行。

运行前检查提供的程序:

cat scripts/host-maintenance.sh

它在创建任何输出前检查目标的 maintenance-ready 输入。就绪输入缺失时打印错误并以 42 退出。否则写入受保护报告,包含实际目标主机名及标签参数。这个小型 shell 实现是已有集成输入;普通文件配置仍应使用声明式模块。

script 模块将控制节点源传输到目标并在那里执行。它不同于 command,后者要求可执行文件已存在于受管节点。下面带引号的标签保持为一个带空格参数。creates 路径在受管目标上检查:该报告已存在时跳过脚本。

追加脚本任务:

cat >> maintenance.yml <<'YAML'

    - name: Run the supplied controller script once per target report
      ansible.builtin.script:
        cmd: /home/labex/project/scripts/host-maintenance.sh 'Maintenance checked'
        creates: /home/automation/maintenance-run/report.txt
      register: maintenance_result
      failed_when: maintenance_result.rc is defined and maintenance_result.rc != 0
YAML

注册结果的 rc 控制 failed_when。is defined 保护条件处理不包含执行返回码的结果。不要用 ignore_errors 或 failed_when false 将真实脚本失败变为表面成功。

运行扩展后的 playbook:

ansible-playbook maintenance.yml

两个目标都应创建报告。读取真实输出:

ansible all -m ansible.builtin.command -a 'cat /home/automation/maintenance-run/report.txt'

各报告应包含自身主机名及 label=Maintenance checked。重复相同 playbook:

ansible-playbook maintenance.yml

目标报告存在,因此脚本应跳过,两个汇总均应报告 changed=0,无失败/不可达目标。creates 是存在性保护,并非内容协调引擎:它不会修复已存在的损坏报告,也不会在脚本输入变化时自动重新生成报告。

传播失败并委派控制节点回执

本步骤将仅为真实管道使用 shell,存储独立控制节点回执,并练习可见的脚本失败,不隐藏失败。

管道将一个命令的输出连接到另一个命令的输入,因此需要 shell。Bash pipefail 使管道内失败的命令导致失败,避免成功的最后一个 wc 命令掩盖它。下面的管道仅统计输入行数;changed_when false 合适,因为它只读、不改变目标状态。failed_when 也拒绝意外行数。

创建控制节点回执目录。各委派 copy 在独立文件名中使用原始清单名,防止主机相互覆盖同一回执:

mkdir -p receipts

追加管道及回执任务。delegate_to localhost 仅在控制节点运行回执任务;inventory_hostname 仍标识原始目标。become false 使控制节点文件创建使用普通 labex 账户。

cat >> maintenance.yml <<'YAML'

    - name: Count managed input lines with explicit pipeline failure propagation
      ansible.builtin.shell: |
        set -o pipefail
        cat '/home/automation/maintenance input.txt' | wc -l
      args:
        executable: /bin/bash
      register: line_result
      changed_when: false
      failed_when: line_result.rc != 0 or (line_result.stdout | trim) != '1'

    - name: Store a per-target receipt on the controller
      ansible.builtin.copy:
        dest: "/home/labex/project/receipts/{{ inventory_hostname }}.txt"
        content: |
          host={{ inventory_hostname }}
          input_lines={{ line_result.stdout | trim }}
        mode: "0640"
      delegate_to: localhost
      become: false
YAML

运行完整维护流程:

ansible-playbook maintenance.yml

检查两个控制节点回执。各回执必须标识自身原始主机及 input_lines=1:

cat receipts/web1.txt receipts/web2.txt

现在在 web1 练习脚本的真实失败分支。用 file 模块移除其受保护的报告及就绪输入;这些是训练节点内有意进行的临时更改:

ansible web1 -m ansible.builtin.file -a 'path=/home/automation/maintenance-run/report.txt state=absent'
ansible web1 -m ansible.builtin.file -a 'path=/home/automation/maintenance-ready state=absent'

下次运行应在 web1 的脚本任务失败,返回 rc 42 及输入缺失消息。|| 使终端流程在预期的非零 playbook 退出后继续;它不会改变 Ansible 的失败分类:

ansible-playbook maintenance.yml || printf 'Expected missing-readiness failure\n'

检查报告路径;脚本失败后,web1 不得具有伪造的成功报告:

ansible web1 -m ansible.builtin.stat -a 'path=/home/automation/maintenance-run/report.txt'

从精确的控制节点源文件恢复就绪输入,并重跑完整流程:

printf 'Ready\n' > ready.txt
ansible web1 -m ansible.builtin.copy -a 'src=/home/labex/project/ready.txt dest=/home/automation/maintenance-ready mode=0644'
ansible-playbook maintenance.yml

web1 应成功重新生成报告。最后的重复运行应无更改、不可达目标或失败:

ansible-playbook maintenance.yml

只读检查仍然执行,受 creates 保护的脚本跳过已有报告,各委派回执保留在控制节点。这既保留真实错误信号,也使成功的重复执行保持稳定。

总结

你用 argv 直接在受管节点执行程序,用 script 将程序从控制节点传输到目标,并仅为带显式失败传播的真实管道使用 shell。只读更改报告、真实 creates 保护条件和未隐藏的退出码,使结果保持有意义。委派的各主机回执展示了控制节点执行,输入缺失练习最终恢复为稳定流程。