You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

逻辑说明

  1. 第一个Play仅针对A组执行任务:
    • 任务成功则正常结束,不会设置失败标记;
    • 任务失败则进入rescue块,在localhost上设置全局可见的失败标记,然后主动终止当前Play并标记失败。
  2. 第二个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 03:40:04