Ansible Playbook 执行调优
优化执行速度
优化执行速度有以下几个方式:
- 优化基础架构,保证控制节点和主机之间网络延迟足够低,带宽足够高
- 禁用事实变量收集
- 利用事实变量缓存重复利用事实变量
- 限制事实变量收集
- 增加并行
- 部分模块避免使用循环
- 使用回调插件分析 Playbook 执行性能
- 使用
ansible.posix.synchronize复制大量文件 - 使用模板复制文件
- 启用流水线
优化基础架构
这个比较简单就是提升硬件配置。
禁用事实变量收集
---
- name: Playbook
hosts: all
gather_facts: false
Ansible 的配置文件里也可以设置禁用。
利用事实变量缓存重复利用事实变量
可以让 Ansible 在执行 Playbook 时先收集事实变量并缓存起来,后续的 Playbook 则设置 gather_facts: false 以禁用事实变量收集,让后续的 Playbook 使用已缓存的事实变量。
---
- name: Cache
hosts: all
gather_facts: true
tasks: []
- name: Playbook
hosts: all
gather_facts: false
tasks:
- name: Debug
ansible.builtin.debug:
var: ansible_facts
Ansible 在配置文件中设置 gathering=smart 的话就可以保证在上述 Playbook 删除两行 gather_facts 也有同样的效果。
[defaults]
gathering=smart
Ansible 默认将变量缓存到内存中,执行完成后丢弃,将事实变量缓存到本地文件或其他可以存储缓存数据的数据库(如 redis)中也是可以提高效率的办法。
限制事实变量收集
ansible.builtin.setup 默认收集所有变量,通过 gather_subset 设置只收集部分变量。
关于 ansible.builtin.setup 子集,参考 ansible-doc ansible.builtin.setup。
- name: Playbook
hosts: all
gather_facts: false
tasks:
- name: Setup
ansible.builtin.setup:
gather_subset:
- '!all'
- '!min'
- network
增加并行
通过 forks 增加同时执行任务的主机数量。
[defaults]
forks=100
forks 也可以在其它地方设置,如 Playbook,Ad Hoc。
部分模块避免使用循环
这里以 ansible.builtin.dnf 模块为例,先列出两种写法:
---
- name: Install packages
hosts: all
become: true
gather_facts: false
tasks:
- name: Install packages
ansible.builtin.dnf:
name:
- httpd
- mod_ssl
- httpd-tools
- mariadb-server
- mariadb
- php
- php-mysqlnd
state: present
---
- name: Install packages
hosts: all
become: true
gather_facts: false
tasks:
- name: Install packages
ansible.builtin.dnf:
name: "{{ item }}"
state: present
loop:
- httpd
- mod_ssl
- httpd-tools
- mariadb-server
- mariadb
- php
- php-mysqlnd
上边两种写法都能实现软件包的安装,但是两种写法所消耗的时间不一样。
第一种写法等效于:
[root@remote-host ~]# dnf install httpd mod_ssl httpd-tools \
mariadb-server mariadb php php-mysqlnd
第二种写法等效于:
[root@remote-host ~]# dnf install httpd
[root@remote-host ~]# dnf install mod_ssl
[root@remote-host ~]# dnf install httpd-tools
[root@remote-host ~]# dnf install mariadb-server
[root@remote-host ~]# dnf install mariadb
[root@remote-host ~]# dnf install php
[root@remote-host ~]# dnf install php-mysqlnd
所以对于 ansible.builtin.dnf 模块,循环并不高效,应直接通过数组的方式列出所有要安装的软件包。
使用回调插件分析 Playbook 执行性能
Ansible 自带一些回调插件,也可以安装一些回调插件,下边是 quay.io/ansible/awx-ee:latest 执行环境自带的回调插件。
[root@remote-host ansible]# ansible-navigator doc -t callback -l
[WARNING]: Collection azure.azcollection does not support Ansible version 2.15.13
[WARNING]: Collection google.cloud does not support Ansible version 2.15.13
[WARNING]: Collection community.vmware does not support Ansible version 2.15.13
amazon.aws.aws_resource_actions summarizes all "resource:actions" completed
ansible.builtin.default default Ansible screen output
ansible.builtin.junit write playbook output to a JUnit file
ansible.builtin.minimal minimal Ansible screen output
ansible.builtin.oneline oneline Ansible screen output
ansible.builtin.tree Save host events to files
ansible.legacy.awx_display Playbook event dispatcher for ansible-runner
ansible.posix.cgroup_perf_recap Profiles system activity of tasks and full execution using cgroups
ansible.posix.debug formatted stdout/stderr display
ansible.posix.json Ansible screen output as JSON
ansible.posix.jsonl Ansible screen output as JSONL (lines in json format)
ansible.posix.profile_roles adds timing information to roles
ansible.posix.profile_tasks adds time information to tasks
ansible.posix.timer Adds time to play stats
ovirt.ovirt.stdout Output the log of ansible
theforeman.foreman.foreman Sends events to Foreman
可以通过以下方式启用插件:
[defaults]
callbacks_enabled=ansible.posix.timer, ansible.posix.profile_tasks, ansible.posix.profile_roles
再来个示例:
[root@remote-host ansible]# grep callbacks_enabled ansible.cfg
callbacks_enabled=ansible.posix.timer, ansible.posix.profile_tasks, ansible.posix.profile_roles
[root@remote-host ansible]# ansible-navigator run setup.yml
PLAY [setup] *******************************************************************
TASK [upgrade system] **********************************************************
Sunday 14 December 2025 17:38:23 +0000 (0:00:00.052) 0:00:00.052 *******
Sunday 14 December 2025 17:38:23 +0000 (0:00:00.050) 0:00:00.050 *******
changed: [base-k8s-master-1]
changed: [base-k8s-worker-2]
changed: [base-k8s-worker-1]
PLAY RECAP *********************************************************************
base-k8s-master-1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-2 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAYBOOK RECAP *****************************************************************
Playbook run took 0 days, 0 hours, 24 minutes, 48 seconds
TASKS RECAP ********************************************************************
Sunday 14 December 2025 18:03:12 +0000 (0:24:48.951) 0:24:49.004 *******
===============================================================================
upgrade system ------------------------------------------------------- 1488.95s
ROLES RECAP ********************************************************************
Sunday 14 December 2025 18:03:12 +0000 (0:24:48.953) 0:24:49.003 *******
===============================================================================
ansible.builtin.dnf -------------------------------------------------- 1488.95s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total ---------------------------------------------------------------- 1488.95s
使用 ansible.posix.synchronize 复制大量文件
ansible.builtin.copy 模块可将文件和目录递归复制到被控主机。 当目录内含有许多文件时,复制可能需很长时间。如果是多次执行的话,后续只会复制不同的文件,使用时间会比第一次短。
如果要复制大量文件到被控主机时,使用 ansible.posix.synchronize 模块效率会更高。ansible.posix.synchronize 模块会在后台使用 rsync,速度比 ansible.builtin.copy 更快。将 delete 选项设置为 true 时,模块还可以从目标删除源主机上不再存在的文件。
ansible.posix.synchronize模块在ansible.posix集合中,Ansible-Core 默认不带这个集合,需要手动添加集合。
ansible.posix.synchronize模块要求被控节点安装rsync工具。
最后来个示例:
- 查看主机清单、Ansible 回调配置并创建 50 个文件:
[root@ansible-controller ansible]# cat inventory
base-k8s-master-1
base-k8s-worker-1
base-k8s-worker-2
[root@ansible-controller ansible]# grep -e '^callback' ansible.cfg
callbacks_enabled=ansible.posix.timer, ansible.posix.profile_tasks, ansible.posix.profile_roles
[root@ansible-controller ansible]# touch files/testfile{0..49}
- 使用
ansible.builtin.copy模块复制文件:
[root@ansible-controller ansible]# cat test.yml
---
- name: test
hosts: all
gather_facts: false
tasks:
- name: copy file
ansible.builtin.copy:
src: "./files/"
dest: "/tmp"
[root@ansible-controller ansible]# ansible-navigator run test.yml
PLAY [test] ********************************************************************
TASK [copy file] ***************************************************************
Sunday 11 January 2026 15:45:06 +0000 (0:00:00.011) 0:00:00.011 ********
Sunday 11 January 2026 15:45:06 +0000 (0:00:00.010) 0:00:00.010 ********
changed: [base-k8s-worker-2]
changed: [base-k8s-worker-1]
changed: [base-k8s-master-1]
PLAY RECAP *********************************************************************
base-k8s-master-1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-2 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAYBOOK RECAP *****************************************************************
Playbook run took 0 days, 0 hours, 0 minutes, 19 seconds
TASKS RECAP ********************************************************************
Sunday 11 January 2026 15:45:26 +0000 (0:00:19.801) 0:00:19.813 ********
===============================================================================
copy file -------------------------------------------------------------- 19.80s
ROLES RECAP ********************************************************************
Sunday 11 January 2026 15:45:26 +0000 (0:00:19.802) 0:00:19.813 ********
===============================================================================
ansible.builtin.copy --------------------------------------------------- 19.80s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total ------------------------------------------------------------------ 19.80s
可以看到复制总用时 19.80 秒。
- 使用
ansible.posix.synchronize模块复制文件:
[root@ansible-controller ansible]# cat test.yml
---
- name: test
hosts: all
gather_facts: false
tasks:
- name: copy file
ansible.posix.synchronize:
src: "./files/"
dest: "/tmp/"
[root@ansible-controller ansible]# ansible-navigator run test.yml
PLAY [test] ********************************************************************
TASK [copy file] ***************************************************************
Sunday 11 January 2026 15:56:42 +0000 (0:00:00.030) 0:00:00.030 ********
Sunday 11 January 2026 15:56:42 +0000 (0:00:00.029) 0:00:00.029 ********
changed: [base-k8s-worker-1]
changed: [base-k8s-worker-2]
changed: [base-k8s-master-1]
PLAY RECAP *********************************************************************
base-k8s-master-1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-2 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAYBOOK RECAP *****************************************************************
Playbook run took 0 days, 0 hours, 0 minutes, 0 seconds
TASKS RECAP ********************************************************************
Sunday 11 January 2026 15:56:43 +0000 (0:00:00.422) 0:00:00.453 ********
===============================================================================
copy file --------------------------------------------------------------- 0.42s
ROLES RECAP ********************************************************************
Sunday 11 January 2026 15:56:43 +0000 (0:00:00.423) 0:00:00.452 ********
===============================================================================
ansible.posix.synchronize ----------------------------------------------- 0.42s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total ------------------------------------------------------------------- 0.42s
可以看到总执行时间为 0.42 秒。
使用模板复制文件
如果只是修改一行文件,ansible.builtin.lineinfile 会比较方便,但是如果要修改多行文件 ansible.builtin.template 会比 ansible.builtin.lineinfile 更高效。
启用流水线
Ansible 默认禁用流水线,禁用流水线的 Task 执行流程如下:
控制端
|
| ssh
v
远端主机
|
| scp 模块代码 -> /tmp/ansible_xxx.py
|
| ssh sudo python /tmp/ansible_xxx.py
|
| 执行
- 模块先拷贝到远端磁盘
- 再 sudo 执行
- 至少 2~3 次 SSH 往返
通过在 ansible.cfg 设置 pipelining=True 可以开启流水线,开启后的 Task 执行流程如下:
控制端
|
| ssh(打开一次连接)
| ├── stdin:模块代码
| └── stdout/stderr:执行结果
v
远端主机
|
| sudo python (从 stdin 读模块)
|
| 执行
等效于:
ssh host "sudo python3 -" < ansible_module.py
python3 -: 👉 表示 从 stdin 读取代码< ansible_module.py:
👉 把模块内容通过 stdin 送过去- 为什么流水线方式会快
- 默认模式
- ssh ×2
- scp ×1
- 磁盘 IO
- sudo fork
- pipelining
- ssh ×1
- 0 次 scp
- 0 次磁盘 IO
- sudo 直接读 stdin
这一段问的 AI,不知道解释的有没有问题。
开启流水线要求 sudo 禁用 requiretty 选项。
Defaults:ansible !requiretty
ansible ALL=(ALL) NOPASSWD: ALL
可以通过 visudo 命令修改
[root@host ~]# visudo
...output omitted...
Defaults !requiretty
在红帽企业 Linux 8 上(应该也算及以上),这个
sudo选项默认为禁用,但在其他系统中可能为启用状态。
最后再来个示例:
- 查看主机清单、Ansible 回调配置和 Playbook:
# 3 个被控主机
[root@ansible-controller ansible]# cat inventory
base-k8s-master-1
base-k8s-worker-1
base-k8s-worker-2
[root@ansible-controller ansible]# grep '^callback' ansible.cfg
callbacks_enabled=ansible.posix.timer, ansible.posix.profile_tasks, ansible.posix.profile_roles
# 在所有被控主机的 /tmp 目录创建 50 个文件并对每个文件添加一行内容
[root@ansible-controller ansible]# cat test.yml
---
- name: test
hosts: all
gather_facts: false
tasks:
- name: create file
ansible.builtin.file:
path: "/tmp/testilfile-{{ item }}"
state: touch
loop: "{{ range(0,50) | list }}"
- name: lineinfile
ansible.builtin.lineinfile:
line: "line {{ item }}"
path: "/tmp/testilfile-{{ item }}"
loop: "{{ range(0,50) | list }}"
- 未开启流水线的情况:
[root@ansible-controller ansible]# grep 'pipelining=' ansible.cfg
pipelining=False
[root@ansible-controller ansible]# ansible-navigator run test.yml
PLAY [test] ********************************************************************
TASK [create file] *************************************************************
Sunday 11 January 2026 15:32:31 +0000 (0:00:00.008) 0:00:00.008 ********
Sunday 11 January 2026 15:32:31 +0000 (0:00:00.007) 0:00:00.007 ********
changed: [base-k8s-worker-1] => (item=0)
changed: [base-k8s-worker-2] => (item=0)
changed: [base-k8s-master-1] => (item=0)
...output omitted...
ok: [base-k8s-worker-2] => (item=49)
ok: [base-k8s-master-1] => (item=48)
ok: [base-k8s-master-1] => (item=49)
PLAY RECAP *********************************************************************
base-k8s-master-1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-2 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAYBOOK RECAP *****************************************************************
Playbook run took 0 days, 0 hours, 0 minutes, 31 seconds
TASKS RECAP ********************************************************************
Sunday 11 January 2026 15:33:03 +0000 (0:00:15.644) 0:00:31.637 ********
===============================================================================
create file ------------------------------------------------------------ 15.98s
lineinfile ------------------------------------------------------------- 15.64s
ROLES RECAP ********************************************************************
Sunday 11 January 2026 15:33:03 +0000 (0:00:15.645) 0:00:31.636 ********
===============================================================================
ansible.builtin.file --------------------------------------------------- 15.98s
ansible.builtin.lineinfile --------------------------------------------- 15.65s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total ------------------------------------------------------------------ 31.63s
可以看到两个任务都是 15 秒多,总时间 31.63 秒。
- 开启流水线的情况:
[root@ansible-controller ansible]# grep 'pipelining=' ansible.cfg
pipelining=True
[root@ansible-controller ansible]# ansible-navigator run test.yml
PLAY [test] ********************************************************************
TASK [create file] *************************************************************
Sunday 11 January 2026 15:38:58 +0000 (0:00:00.007) 0:00:00.007 ********
Sunday 11 January 2026 15:38:58 +0000 (0:00:00.006) 0:00:00.006 ********
changed: [base-k8s-worker-2] => (item=0)
changed: [base-k8s-master-1] => (item=0)
changed: [base-k8s-worker-1] => (item=0)
...output omitted...
ok: [base-k8s-worker-2] => (item=49)
ok: [base-k8s-master-1] => (item=48)
ok: [base-k8s-master-1] => (item=49)
PLAY RECAP *********************************************************************
base-k8s-master-1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-2 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
PLAYBOOK RECAP *****************************************************************
Playbook run took 0 days, 0 hours, 0 minutes, 18 seconds
TASKS RECAP ********************************************************************
Sunday 11 January 2026 15:39:16 +0000 (0:00:08.838) 0:00:18.504 ********
===============================================================================
create file ------------------------------------------------------------- 9.66s
lineinfile -------------------------------------------------------------- 8.84s
ROLES RECAP ********************************************************************
Sunday 11 January 2026 15:39:16 +0000 (0:00:08.838) 0:00:18.504 ********
===============================================================================
ansible.builtin.file ---------------------------------------------------- 9.66s
ansible.builtin.lineinfile ---------------------------------------------- 8.84s
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
total ------------------------------------------------------------------ 18.50s
可以看到两个任务都是 9 秒左右,总时间 18.50 秒。