delegate_to 是让 ansible 在指定主机(非目标主机)上执行任务但保留原始主机变量上下文的机制;需确保委托目标在 inventory 中定义且连接可达,变量引用须用 hostvars 显式获取原始主机数据。

Delegate_to 是用来绕过当前 play 目标主机的执行跳转机制
它不是“在另一台机器上跑这个任务”,而是让 Ansible 控制节点(或你指定的某台 host)来执行该 task,同时保留原始 host 的变量上下文。常见误解是以为它能替代 delegate_to: localhost 就等于“本地执行”——其实不一定,得看你的 inventory 和 connection 配置是否真把 localhost 当作可连接节点。
典型使用场景包括:在跳板机上触发部署、在某台 DB 主机上执行备份脚本、对一组目标主机统一调用一个外部 API(但只由一台机器发请求)。
-
delegate_to不会自动切换变量作用域,hostvars、inventory_hostname等仍指向原始 host - 如果委托到
localhost,需确保connection: local或已配置好 SSH 到 localhost 的密钥;否则可能报FAILED! => {"msg": "Failed to connect to the host via ssh"} - 委托目标必须出现在当前 play 的
hosts列表里,或显式加进 inventory(哪怕只是localhost ansible_connection=local)
delegate_to: localhost 为什么有时不生效
最常踩的坑是:你以为 localhost 总是可用,但它在默认 inventory 里未必被定义,或者被定义为不可达。Ansible 不会为你自动创建一个“逻辑 localhost”,它严格按 inventory 解析。
检查方式很简单:ansible-inventory -i your_inventory.yml --list | grep localhost。如果没输出,那它就不在 scope 内。
- 修复方法是在 inventory 文件顶部加一行:
localhost ansible_connection=local - 若用 YAML 格式 inventory,写成:
- name: localhost hosts: localhost connection: local
- 别依赖
127.0.0.1替代localhost,除非 inventory 明确写了127.0.0.1这个 hostname,否则变量如hostvars['localhost']会取不到值
委托时如何正确引用原始主机的变量
你委托任务到别的主机执行,但往往还要操作原始主机的信息——比如生成一个以目标主机名命名的文件、或把它的 IP 写进配置。这时候不能直接写 {{ inventory_hostname }},因为委托后当前上下文已经是新 host。
必须显式通过 hostvars 获取原始 host 数据:
- name: Write target's IP to remote config
delegate_to: jumpbox
copy:
content: "target_ip={{ hostvars[inventory_hostname]['ansible_host'] | default(inventory_hostname) }}"
dest: "/tmp/{{ inventory_hostname }}_config.txt"
-
inventory_hostname在 task 中始终代表原始 host 名(即当前 loop 的那个 host) - 不要用
play_hosts或groups直接索引,它们不保证顺序且易受动态 inventory 影响 - 如果原始 host 没跑过 setup 模块(比如用了
gather_facts: false),hostvars[...]['ansible_host']可能为空,建议 fallback 到inventory_hostname
delegate_to 和 run_once 的组合陷阱
两者经常一起用,比如“只在一台机器上执行一次命令,并委托给跳板机”。但要注意:run_once 是针对 play 内所有 host 的去重,而 delegate_to 是 per-task 的跳转。它们不互斥,但行为叠加容易误判。
例如你对 10 台 web server 执行一个 task,设了 run_once: true + delegate_to: jumpbox,结果是:整个 play 只触发一次该 task,且这次执行发生在 jumpbox 上——而不是每台 web server 各自委托一次。
- 如果你想要“每台 web server 都触发一次,但都在 jumpbox 上执行”,就别加
run_once - 如果你还用了
loop或with_items,run_once会让整个 loop 只跑一遍,不是 loop 内每个 item 跑一遍 - 委托目标本身如果是动态生成的(比如从 facts 里取的
{{ some_var }}),要确保该变量在委托发生前已确定,否则会报undefined variable
真正难的不是写对 delegate_to,而是厘清“谁在执行”“谁的变量可用”“谁在连接”这三件事是否一致。inventory 定义、connection 类型、变量作用域,三个地方错一个,就会静默失败或行为偏移。










