介绍
将现有维护程序集成到声明式自动化时需要谨慎。你将区分 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 保护条件和未隐藏的退出码,使结果保持有意义。委派的各主机回执展示了控制节点执行,输入缺失练习最终恢复为稳定流程。


