Ansible:如何结合hosts关键字与rescue块实现任务委派?
问题与解决方案:Ansible 按组执行任务并处理失败场景
问题描述
环境与场景
我有一个包含三个主机组的inventory,内容如下:
[all:vars] ansible_user = myuser ansible_port = 22 [A] XXX.XXX.XXX.XX ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new' [B] XXX.XXX.XXX.XX ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new' [C] XXX.XXX.XXX.XX ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new'
每个组仅包含一台主机。
需求
先针对A组内的主机执行一组任务;若这组任务执行失败,则对A、B组所有主机执行另一组任务。
尝试的Playbook(执行失败)
我使用Ansible v2.16.4版本,编写了如下Playbook:
- hosts: "[A, B]" tags: always tasks: - name: Execute against A block: - name: Do something against A ansible.builtin.include_tasks: file: ./task_lists/a_tasks.yaml run_once: true delegate_to: "{{ groups['A'][0] }}" rescue: - name: Do something against A and B ansible.builtin.include_tasks: file: ./task_lists/ab_tasks.yaml
问题现象
执行失败,输出显示任务出现[A -> B]的委派行为,不符合预期——预期第一个任务块输出类似[localhost -> A],rescue块输出类似[localhost -> A] [localhost -> B]。
解决方案
问题根源在于Play的hosts指定了[A,B],delegate_to在这个上下文里会和原目标主机产生混淆,导致委派行为异常。推荐拆分Play来实现需求,逻辑更清晰,也能避免委派的问题:
方案一:基于全局标记的分步执行
--- # 第一个Play:仅针对A组执行任务 - hosts: A gather_facts: false tasks: - name: 执行A组专属任务 block: - ansible.builtin.include_tasks: ./task_lists/a_tasks.yaml rescue: # 在localhost上设置全局可见的失败标记 - name: 标记A组任务失败 ansible.builtin.set_fact: a_tasks_failed: true delegate_to: localhost run_once: true # 主动标记Play失败,确保后续Play能识别状态 - name: 终止当前Play并标记失败 ansible.builtin.fail: msg: "A组任务执行失败,将执行A+B组的补救任务" # 第二个Play:仅在A组任务失败时,针对A+B组执行补救任务 - hosts: A,B gather_facts: false tasks: - name: 执行A+B组补救任务 ansible.builtin.include_tasks: ./task_lists/ab_tasks.yaml when: hostvars['localhost'].a_tasks_failed is defined and hostvars['localhost'].a_tasks_failed
逻辑说明
- 第一个Play仅针对A组执行任务:
- 任务成功则正常结束,不会设置失败标记;
- 任务失败则进入
rescue块,在localhost上设置全局可见的失败标记,然后主动终止当前Play并标记失败。
- 第二个Play针对A+B组:
- 通过
when条件判断失败标记是否存在,仅当标记存在时执行补救任务; - 无需使用
delegate_to,直接针对目标组执行任务,行为完全符合预期。
- 通过
方案二:使用meta: end_play简化流程
如果不需要保留第一个Play的失败状态,可直接用meta: end_play在任务成功时终止后续流程:
--- - hosts: A gather_facts: false tasks: - name: 执行A组专属任务 block: - ansible.builtin.include_tasks: ./task_lists/a_tasks.yaml # 任务成功时,直接终止所有后续Play - name: 任务成功,终止后续流程 ansible.builtin.meta: end_play rescue: # 任务失败,不终止流程,继续执行后续Play - name: A组任务失败,准备执行补救任务 ansible.builtin.debug: msg: "A组任务失败,将执行A+B组补救任务" - hosts: A,B gather_facts: false tasks: - name: 执行A+B组补救任务 ansible.builtin.include_tasks: ./task_lists/ab_tasks.yaml
逻辑说明
- 若A组任务成功,第一个Play执行
meta: end_play后会直接停止所有后续流程; - 若A组任务失败,进入
rescue块后不会终止流程,后续Play自动执行A+B组的补救任务。这个方案更简洁,无需额外的状态标记。
内容的提问来源于stack exchange,提问作者jlan
相关产品推荐
相关产品推荐

