如何在Ansible主Playbook失败时仍复用logs.yml?
问题背景
你有一个可正常运行的logs.yml Playbook,功能是在目标服务器归档/tmp/*my_files*文件并复制到RemoteServer。现在需要在run-app.yml中复用这个Playbook,要求无论run-app角色执行成功还是失败,最终都要运行logs.yml。
原run-app.yml代码:
cat run-app.yml --- - hosts: "{{ target }}" gather_facts: no vars: ansible_ssh_user: "{{ user }}" ansible_ssh_pass: "{{ password }}" roles: - download-zip-files - run-app
你尝试用block的always块实现,但修改后的代码运行时报语法错误:
cat run-app.yml --- - hosts: "{{ target }}" gather_facts: no vars: ansible_ssh_user: "{{ user }}" ansible_ssh_pass: "{{ password }}" roles: - download-zip-files tasks: - name: test block: - ansible.builtin.include_role: name: run-app always: - include: logs.yml
报错信息:
The error appears to be in
'.../logs.yml':
line 5, column 3, but may be elsewhere in the file depending on the
exact syntax problem.
- hosts: "{{ target }}"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value.
错误原因
核心问题是:你在tasks的always块里用include导入了完整的Playbook(包含hosts字段)。Ansible的tasks上下文只能执行单个任务、角色或者任务文件,不能直接嵌套完整的Play(带有hosts的顶级定义)。logs.yml单独运行正常是因为它本身是完整的Play,但放到已有Play的tasks中调用时,相当于在任务里重新定义hosts,这违反了Ansible的语法规则。
解决方法
方法一:拆分logs.yml为可复用任务文件(推荐)
把logs.yml中的核心任务提取成单独的任务文件,这样就能在always块里直接导入执行。
- 创建
tasks/logs-tasks.yml,将logs.yml里的任务逻辑迁移过来(假设原logs.yml的tasks如下):
# tasks/logs-tasks.yml - name: 归档/tmp目录下的my_files文件 ansible.builtin.archive: path: /tmp/*my_files* dest: /tmp/my_files_archive.tar.gz format: gz - name: 将归档文件复制到RemoteServer ansible.builtin.copy: src: /tmp/my_files_archive.tar.gz dest: /path/on/RemoteServer/ remote_src: yes
- 修改
run-app.yml,在always块里导入这个任务文件:
--- - hosts: "{{ target }}" gather_facts: no vars: ansible_ssh_user: "{{ user }}" ansible_ssh_pass: "{{ password }}" roles: - download-zip-files tasks: - name: 执行run-app角色并确保日志任务运行 block: - ansible.builtin.include_role: name: run-app always: - name: 运行日志归档与复制任务 ansible.builtin.include_tasks: tasks/logs-tasks.yml
这种方式符合Ansible的最佳实践,把可复用逻辑拆分为独立任务,便于维护和多处调用。
方法二:采用多Play结构,无需修改原logs.yml
如果不想改动现有的logs.yml,可以把run-app.yml改成多Play的结构,让第二个Play总是执行logs.yml,无论第一个Play的结果如何。
修改后的run-app.yml:
--- # 第一个Play:执行下载和run-app角色,忽略失败以保证后续流程继续 - hosts: "{{ target }}" gather_facts: no vars: ansible_ssh_user: "{{ user }}" ansible_ssh_pass: "{{ password }}" tasks: - name: 执行download-zip-files角色 ansible.builtin.include_role: name: download-zip-files - name: 执行run-app角色,忽略错误 ansible.builtin.include_role: name: run-app ignore_errors: yes # 第二个Play:无论第一个Play结果如何,执行日志归档任务 - import_playbook: logs.yml
这里通过ignore_errors: yes让run-app角色的失败不会终止整个Play,然后第二个Play会自动运行(Ansible默认会依次执行所有Play,除非遇到致命错误)。如果需要更严格的保证,还可以在第一个Play中添加any_errors_fatal: no,确保任何错误都不会中断后续Play的执行。
内容的提问来源于stack exchange,提问作者fr0zt

