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

存在不可达主机时Ansible循环执行失败的解决问询

问题

我正在编写一个Ansible Playbook,用于创建本地文件记录所有运行Docker的主机,Playbook内容如下:

- name: Retrieve list of hosts that match certain attributes
  hosts: all
  gather_facts: true
  become: true

  tasks:

  - name: Check servers where Docker is running
    systemd:
      name: docker
    register: is_docker_service_running

  - name: Write to file all servers where docker is running
    copy:
      content: |
        {% for host in groups['all'] %}
        {% if hostvars[host].is_docker_service_running.status.ActiveState == 'active' %}{{ hostvars[host].inventory_hostname }}
        {% endif -%}
        {% endfor %}
      dest: /tmp/servers_with_docker
    delegate_to: localhost

该Playbook在正常情况运行良好,但当Inventory中有主机宕机/不可达时,整个Play会失败(即使其他200多台主机正常),所有可达主机都会报变量未定义的错误:

fatal: [alive-host1 -> localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'is_docker_service_running'. 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'is_docker_service_running'
fatal: [alive-host2 -> localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'is_docker_service_running'. 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'is_docker_service_running'
fatal: [alive-host3 -> localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'is_docker_service_running'. 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'is_docker_service_running'
fatal: [alive-host4 -> localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'is_docker_service_running'. 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'is_docker_service_running'
[...]
[...]

将不可达主机从Inventory移除后,Playbook恢复正常。我想知道Ansible为何未忽略不可达主机,以及如何让循环完全忽略这类主机。

根本原因分析
  1. 静态主机组特性:groups['all']是Inventory的静态主机集合,不管主机是否可达、是否能执行任务,都会被包含在内。Ansible不会自动剔除不可达主机,除非你明确配置过滤逻辑。
  2. 变量注册逻辑:第一个任务(检查Docker服务)只会在可达主机上执行并注册is_docker_service_running变量;不可达主机根本没机会执行这个任务,自然不会生成该变量。
  3. 模板循环的致命错误:第二个任务的Jinja2循环遍历了groups['all']的所有主机,当循环到不可达主机时,尝试读取不存在的is_docker_service_running变量,触发未定义变量的致命错误,直接导致整个任务失败,所有可达主机的结果也无法输出。
解决方案

方案一:过滤不可达主机+变量存在性检查

修改Playbook,让任务跳过不可达主机,并在循环中先检查变量是否存在:

- name: Retrieve list of hosts that match certain attributes
  hosts: all
  gather_facts: true
  become: true
  max_fail_percentage: 100  # 允许所有主机失败,确保Play执行到最后

  tasks:

  - name: Check servers where Docker is running
    systemd:
      name: docker
    register: is_docker_service_running
    ignore_unreachable: true  # 主机不可达时标记任务为跳过,而非失败
    ignore_errors: true       # 可选:处理Docker服务不存在等错误场景

  - name: Write to file all servers where docker is running
    copy:
      content: |
        {% for host in groups['all'] %}
        {% set host_data = hostvars[host] %}
        {% if host_data.get('is_docker_service_running') is defined 
              and host_data.is_docker_service_running.status.ActiveState == 'active' %}
        {{ host_data.inventory_hostname }}
        {% endif -%}
        {% endfor %}
      dest: /tmp/servers_with_docker
    delegate_to: localhost

方案二:使用动态主机集合简化逻辑

直接使用Ansible内置的ansible_play_hosts变量,这个集合只包含成功执行了Play中任务的主机,天然排除不可达或任务失败的主机:

- name: Retrieve list of hosts that match certain attributes
  hosts: all
  gather_facts: true
  become: true
  max_fail_percentage: 100

  tasks:

  - name: Check servers where Docker is running
    systemd:
      name: docker
    register: is_docker_service_running
    ignore_unreachable: true
    ignore_errors: true

  - name: Write to file all servers where docker is running
    copy:
      content: |
        {% for host in ansible_play_hosts %}
        {% if hostvars[host].is_docker_service_running.status.ActiveState == 'active' %}
        {{ hostvars[host].inventory_hostname }}
        {% endif -%}
        {% endfor %}
      dest: /tmp/servers_with_docker
    delegate_to: localhost

关键修改说明

  • ignore_unreachable: true:让不可达主机的任务标记为跳过,而非终止整个Play。
  • max_fail_percentage: 100:确保即使大量主机不可达,Play依然能执行到生成文件的步骤。
  • ansible_play_hosts:替代静态的groups['all'],自动过滤掉未完成任务的主机,避免变量不存在的错误。

内容的提问来源于stack exchange,提问作者Tony

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 12:35:16