默认情况下,Ansible 会在 playbook 的 hosts 行所匹配的机器上收集 facts,并执行所有任务。通过任务委派,可以让任务在另一台机器或另一组机器上运行,把 facts 归属于指定机器,也可以让整个 playbook 在本地执行。这些方式适合精确地管理彼此关联的环境。例如,更新 Web 服务器时,可能需要暂时把它们从负载均衡池中移除。
移出负载均衡池的操作不能由这些 Web 服务器自身完成。把这类操作委派给 localhost,就能把相关任务保留在同一个 play 中。
不能委派的任务
某些任务始终在控制节点上执行,不能进行委派,包括 include、add_host 和 debug。判断某项 action 能否委派,可以查看它的 connection 属性文档:如果该属性的 support 为 False 或 None,说明该 action 不使用连接,也就不能委派。
委派任务
如果希望在一台主机上执行操作,同时引用其他主机的信息,可以在任务上使用 delegate_to。这适合管理负载均衡池中的节点,或控制停机窗口。把委派与 serial 配合,可以限制每次处理的主机数量:
---
- hosts: webservers
serial: 5
tasks:
- name: Take out of load balancer pool
ansible.builtin.command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
- name: Actual steps would go here
ansible.builtin.yum:
name: acme-web-stack
state: latest
- name: Add back to load balancer pool
ansible.builtin.command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
这里每批处理 5 台 Web 服务器。第一个和第三个任务在 127.0.0.1 上运行,也就是运行 Ansible 的控制机器;中间的更新任务仍在当前 Web 服务器上执行。inventory_hostname 提供当前 play 所处理的 Web 服务器名称,使控制机器知道应该移出或放回哪一个节点。
对于单个任务,还有简写形式 local_action。下面展示同一 playbook 中委派给 127.0.0.1 的任务片段:
---
# ...
tasks:
- name: Take out of load balancer pool
local_action: ansible.builtin.command
args:
cmd: /usr/bin/take_out_of_pool {{ inventory_hostname }}
# ...
- name: Add back to load balancer pool
local_action: ansible.builtin.command
args:
cmd: /usr/bin/add_back_to_pool {{ inventory_hostname }}
也可以通过本地 action 调用 rsync,把文件递归复制到受管理服务器:
---
# ...
tasks:
- name: Recursively copy files from management server to target
local_action: ansible.builtin.command
args:
cmd: "rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/"
要让这个命令不需要交互输入,必须有不需要输入口令的 SSH 密钥,或配置好 ssh-agent;否则 rsync 会提示输入密钥口令。
需要指定更多参数时,可以使用下列写法:
---
# ...
tasks:
- name: Send summary mail
local_action: community.general.mail
args:
subject: "Summary Mail"
to: "{{ mail_recipient }}"
body: "{{ mail_body }}"
run_once: True
这里的邮件任务只是展示参数和 run_once 的用法,并没有实际发送邮件。执行前需要根据环境准备所用 collection、邮件参数和真实收件人。
委派时有两个变量规则需要注意:
ansible_host以及其他连接变量如果存在,反映的是受委派主机的信息,而不是inventory_hostname指向的主机。- 受委派主机不会继承发起委派的主机的变量。
虽然 delegate_to 可以指向 inventory 中不存在的主机,例如一个 IP 地址、DNS 名称或连接插件接受的其他标识,但这样做不会自动把该主机加入 inventory,可能引起问题。这类主机可以继承 “all” 组的变量,前提是 VARIABLE_PRECEDENCE 包含 all_inventory。必须委派给 inventory 之外的主机时,应使用 add_host 模块 把它加入 inventory。
委派上下文中的模板求值
任务被委派后,执行解释器(通常为 Python)、connection、become 和 shell 插件选项的模板求值,会使用受委派主机的变量值。除了 inventory_hostname,这些选项使用的变量来自受委派主机,而不是原始任务主机。
如果这些选项需要原始主机的变量,必须明确通过 hostvars[inventory_hostname]['varname'] 访问。甚至 inventory_hostname_short 也会指向受委派主机,不能依靠它代替原主机的短名称。
委派与并行执行
Ansible 默认并行执行任务。委派不会改变这一点,也不会自动解决并发问题,例如多个 fork 同时写同一个文件。
常见场景是:所有目标主机都委派到同一台机器,并使用 copy、template 或 lineinfile 更新这台机器上的单个文件。任务仍在多个并行 fork 中执行,默认数量为 5,写入可能互相覆盖。
一种处理方式,是让任务只执行一次,再循环处理所有主机:
- name: "handle concurrency with a loop on the hosts with `run_once: true`"
lineinfile: "<options here>"
run_once: true
loop: '{{ ansible_play_hosts_all }}'
这里的 <options here> 是待补充的参数占位符,并不是可以直接用于实际配置的 lineinfile 参数。
还可以增加一个中间 play,并设置 serial: 1,或在任务级设置 throttle: 1。更多执行控制方法见 控制 playbook 执行:策略等。
委派 facts
委派任务可以类比生活中的代办:即使别人把买好的杂货送到你家,杂货仍然属于你。类似地,委派任务收集的 facts 默认归属于 inventory_hostname,也就是当前 play 正在处理的主机,而不是实际产生这些 facts 的受委派主机。
要把收集到的 facts 归给受委派主机,可以设置 delegate_facts: true:
---
- hosts: app_servers
tasks:
- name: Gather facts from db servers
ansible.builtin.setup:
delegate_to: "{{ item }}"
delegate_facts: true
loop: "{{ groups['dbservers'] }}"
这个任务收集 dbservers 组中机器的 facts,并把它们归属于对应的数据库服务器,尽管 play 的目标组是 app_servers。这样,即使 dbservers 没有出现在本次 play 中,或者被 --limit 排除,也能查询 hostvars['dbhost1']['ansible_default_ipv4']['address']。
本地 playbook
有时需要让远程主机在自身本地运行 playbook,而不通过 SSH 连接执行。例如,把 playbook 加入 crontab,可以定期确保系统配置符合要求;也可以在操作系统安装程序中运行 playbook,例如 Anaconda kickstart。
要让整个 playbook 本地执行,把 hosts: 设置为 hosts: 127.0.0.1,然后运行:
ansible-playbook playbook.yml --connection=local
也可以只让某个 play 使用本地连接,即使同一 playbook 的其他 play 使用默认的远程连接类型:
---
- hosts: 127.0.0.1
connection: local
需要注意:设置 connection: local 而没有设置 ansible_python_interpreter 时,模块会使用 /usr/bin/python,而不会使用 {{ ansible_playbook_python }}。例如,可以在 host_vars/localhost.yml 中设置 ansible_python_interpreter: "{{ ansible_playbook_python }}"。使用 local_action 或 delegate_to: localhost 可以避免这里描述的解释器选择问题。
进一步阅读
- Ansible playbooks:playbook 入门。
- 控制 playbook 执行:策略等:更多控制 Ansible 在何处、以何种方式执行的方法。
- Ansible 沟通指南:提问、寻求帮助和交流想法的入口。
示例边界与来源许可
本页 8 组代码保留了官方页面的任务名、参数、注释和缩进。示例中的 take_out_of_pool、add_back_to_pool、acme-web-stack、路径及邮件变量需要由实际环境提供;带省略号的片段没有包含完整 playbook。本次没有运行 Ansible、修改服务器、执行 rsync 或发送邮件。
来源为 Ansible 社区文档 Controlling where tasks run: delegation and local actions。对应正文的 RST 源文件 位于 Ansible 官方文档仓库,版权属于原文的 Ansible 文档贡献者;仓库 COPYING 提供 GNU General Public License 第 3 版。本地包保留完整 LICENSE-GPL-3.0.txt,本整理稿及其可编辑 Markdown 按 GPLv3 提供,适用原许可中的无担保条款。
修改日期:2026-10-03。修改内容:完整汉化六个主要小节及相关链接,保留 8 组原代码,补充占位符、片段和未执行范围说明。对外分发时应同时提供上述完整许可和可编辑稿,保留署名及修改说明。










暂无评论内容