Ansible自动化(十六) 优化Playbook执⾏速度
1 | 作者:李晓辉 |
优化基础架构
每个版本的自动化执行环境都会带来功能上的提升和优化。保持在最新版本的红帽 Ansible 自动化平台上,不仅能让你体验到更快的 playbook 执行速度,还能享受到 Ansible 自带模块的定期优化和升级。
其中一种提升性能的方式就是把控制节点放得更靠近受管节点。因为 Ansible 对网络通信和数据传输有很高的依赖,如果控制节点和受管主机之间的网络连接有高延迟或带宽不足,那么执行 playbook 的效率就会大打折扣。
优化facts
禁⽤facts收集
在每个 play 的开始阶段,会自动运行一个隐藏任务,使用 ansible.builtin.setup
模块从各主机中收集facts。这些facts通过 ansible_facts
变量提供节点的相关信息。
需要注意的是,收集远程主机的facts会占用一定时间。如果 play 中不需要使用这些facts,可以通过将 gather_facts
设置为 false
来跳过此过程,从而提升执行效率。
1 |
|
运行后可以看到不再有收集facts的任务
1 | ansible-navigator run playbook.yml -m stdout -i inventory |
强制收集facts
即使已禁用自动facts收集,也可以在需要时通过任务手动触发facts收集。通过运行 ansible.builtin.setup
模块,能够动态获取节点信息,这些信息随后可以在 playbook 的后续阶段灵活使用,从而提升自动化任务的适应性。
1 |
|
一般来说,Playbook 会用一些变量,比如 ansible_facts['hostname']
、ansible_hostname
或 ansible_facts['nodename']
来指代当前的主机名。但这些变量是靠facts收集得来的。为了更简单快捷,你其实可以用 inventory_hostname
和 inventory_hostname_short
这两个魔法变量,完全不用开facts收集的功能。这样既省事,还能让你的 Playbook 跑得更快。
1 |
|
facts缓存
Ansible 有个挺实用的功能,就是用缓存插件存储 play 里收集的facts和清单数据。通过这个facts缓存,你可以反复利用之前收集的信息,省掉一些重复收集的时间。
其实默认情况下,facts缓存一直是开启的,而且一次只能用一个缓存插件。如果你没改过 ansible-navigator
的配置,系统会用默认的 memory
缓存插件。这个插件会在当前 Ansible 运行的过程中保存收集到的facts。
这个功能对性能提升很有帮助,尤其是你的 playbook 有多个 play 时。比如,第一个 play 可以负责收集所有主机的facts,后面的 play 就不用重复收集了,直接用缓存里现成的数据。这样不仅省时,还能让你的 playbook 跑得更快。
默认情况下启⽤ memory 缓存插件
1 |
|
在第一个 play 中开启了
gather_facts: true
,从所有主机收集并缓存facts。在后续的 play 中,通过
gather_facts: false
禁用facts收集,直接利用缓存中的数据。使用
ansible.builtin.debug
和ansible.builtin.shell
模块演示如何访问缓存的facts数据。
这样设置既能高效利用facts缓存,又能减少多次收集facts的开销。
想让 Ansible 聪明点收集facts?可以用 smart 收集 模式,这会让它更高效地判断什么时候该收集,什么时候直接用缓存,省时省力。你只需要在
ansible.cfg
文件里稍微调整一下配置就行:
1 | [defaults] |
启用了 smart 收集后,Ansible 会自动优化facts收集的频率。它会在每个新主机上
收集facts,但如果多个 play 用到同一台主机,就不会重复收集
,这样可以省掉很多不必要的操作。
比如说,在前面那个 playbook 示例里,你其实可以直接删掉那两个 gather_facts
行,结果是完全一样的。第一个 play 会把所有主机的facts都收集好,而第二个 play 就不会重复收集了,因为这些数据已经有了。
限制facts收集
如果你不想每次都收集所有的facts,可以有选择性地进行facts收集。简单来说,你可以关闭自动facts收集功能,然后在需要的时候,通过运行 ansible.builtin.setup
模块手动收集特定的子集数据。使用 gather_subset
选项,你可以控制要收集哪些子集,这种方式通常比收集所有可用facts要快得多。
可以选择的子集包括:
all
、min
、hardware
、network
、virtual
、ohai
和facter
。需要注意的是,如果你排除了all
子集,系统仍然会默认收集min
子集。
1 |
|
在这个示例中:
禁用了自动facts收集(
gather_facts: false
)。通过
gather_subset
指定要收集的子集,比如min
、hardware
和network
。使用
debug
模块查看收集到的facts信息。
这种方法更高效,你可以根据实际需求挑选要收集的内容,避免浪费资源。
增加任务并发数量
当 Ansible 执行 play 的时候,它会按批次处理主机。比如说,它会先在当前批次的每台主机上运行第一项任务,然后再逐个运行第二项任务,以此类推,直到整个 play 完成。
这里有个参数叫 forks,它决定了 Ansible 一次能同时处理多少个连接。默认值是 5,意思就是无论当前任务涉及多少台主机,Ansible 都会每次挑 5 台来通讯。等这 5 台搞定了,再继续处理剩下的主机,直到完成所有任务。如果你增大 forks
的值,比如设为 20,那么 Ansible 就可以同时尝试连接到 20 台主机。这样任务完成的速度会更快,但对控制节点的压力也会更大。
你可以通过修改 Ansible 配置文件(ansible.cfg
)来设置需要的forks数,或者直接在运行命令时,用 -f
选项指定,比如这样:
1 | ansible-navigator run playbook.yml -i inventory -f 20 |
或者
1 | [defaults] |
能用列表就别用循环
有些模块可以直接处理一个列表的项目,这样你就不用写循环了。这种方法很高效,因为模块只会调用一次,而不是每个项目调用一次。
比如,用来管理操作系统软件包的模块就可以这么用。以下是用 ansible.builtin.yum
模块在一次事务中安装多个软件包的代码示例,这是安装一组软件包最快的方法:
循环写法:
1 |
|
循环的方式等同于:
1 | yum install -y httpd |
列表写法:
1 |
|
列表的方式等同于:
1 | yum install -y httpd mariadb-server php |
总结一下:
直接用列表:
模块(例如
ansible.builtin.yum
)直接接受一组列表作为参数,一次性处理所有项目。模块只被调用一次,因此执行效率更高,适合大规模操作,比如安装多个软件包。
优点是简洁高效,代码短,不需要多余的循环。
使用 loop
循环:
利用
loop
依次处理列表中的每个项目,每个项目都会单独调用一次模块。虽然效率略低,但灵活性更高,可以为每个项目设置不同的参数或条件。
适合需要针对不同项目进行个性化处理的场景。
注意
不是所有的 Ansible 模块都能直接接收列表,比如 ansible.builtin.service
模块就只能接受单个 name
值。如果你想操作多个服务,就得用循环一个个来处理了。
如果你想知道某个模块的参数能接受哪些类型的值,可以用这条命令看看详细说明:
1 | ansible-navigator doc ansible.builtin.service |
打开后,可以看具体参数的介绍以及type
参数
⾼效复制⽂件
ansible.builtin.copy
模块适合用来复制文件和目录,但如果你要复制的东西特别多,像很大的目录或者几百个文件,那速度就会慢点。不过,它有个优点,运行多次 playbook 的时候,只会拷贝那些改动过的文件,所以后续会快不少。
不过如果是大量文件操作,用 ansible.posix.synchronize
模块会更快,因为它底层用的是 rsync
工具,专门优化过文件同步的速度。更棒的是,你可以加个 delete: true
,这样就能自动删掉目标目录里那些源头没有了的文件,省了不少麻烦。
总结一下,小文件、小任务就用 copy
,大批量复制、讲究效率的话就用 synchronize
,这样用起来省时又高效。
copy版本如下:
1 |
|
synchronize版本如下:
1 |
|
多使⽤template
ansible.builtin.lineinfile
模块挺适合在文件里插入或删除某些行,比如修改配置文件里的指令啥的。不过呢,有大量参数需要在不同的地方修改的时候,这个模块就不太给力了,不仅效率低,还容易出问题。如果遇到这种情况,建议直接用 ansible.builtin.template
或 ansible.builtin.copy
lineinfile版本:
1 |
|
template 版本:
假设我们的Jinja2文件内容如下:
1 | # Apache HTTP Server Configuration Template |
那么我们的playbook就是这样:
1 |
|
copy版本:
1 |
|
启⽤Pipelining
运行任务时,Ansible 会通过多个 SSH 连接把模块和相关数据复制到远程节点上再执行。如果任务多了,SSH 操作也会变得频繁,从而拖慢速度。
为了解决这个问题,你可以启用流水线功能。这个功能能减少 Ansible 建立的 SSH 连接数量,从而加快运行速度。
启用流水线功能后,Ansible 的运行速度提升主要体现在以下几点:
减少 SSH 连接次数:
通常,Ansible 为每个任务都要建立一次独立的 SSH 连接。而启用流水线后,Ansible 可以复用现有的 SSH 连接,避免频繁的连接与断开,大大节省了时间。优化远程操作:
流水线模式下,Ansible 会更高效地将模块数据和任务指令发送到远程主机。相比起传统方式逐步传输模块文件,流水线模式以更精简的方式实现操作交付。减少模块调用开销:
没有流水线时,Ansible 每次调用模块都需要重复一些初始化步骤。启用流水线后,这些开销被显著降低,因为多个任务可以共享一些通用的运行环境。
举个例子:
未启用流水线:
每个任务都需要新建 SSH 连接、传输模块文件、初始化模块和运行任务。如果有 10 个任务,每台主机都需要重复这些动作 10 次。启用流水线:
任务复用已有的 SSH 连接,模块文件和初始化步骤也可以合并到一次操作中。这样,即使有 10 个任务,也只需要一次连接和初始化,大大减少了执行时间。
启用流水线的方法很简单:你只需要在 ansible-navigator.yml
配置文件的 execution-environment
部分,设置环境变量 ANSIBLE_PIPELINING
为 true
,比如这样:
1 | ansible-navigator: |
默认情况下,Ansible 不启用流水线功能,因为这个功能要求远程节点上的 sudo
配置中关闭 requiretty
选项。如果 requiretty
是启用的,Ansible 就没法正确运行流水线功能。
在 红帽8 以上的系统中,requiretty
默认是关闭的,所以流水线可以直接用;但在其他系统中,requiretty
可能是开启状态,你需要手动禁用它。
可以通过以下方法禁用 requiretty
:
编辑远程主机的 /etc/sudoers
文件:
1 | Defaults !requiretty |
使⽤回调插件分析 Playbook 执⾏性能
回调插件可以用来扩展 Ansible,通过调整对各种事件的响应,让它变得更强大。比如,有些插件可以直接修改命令行工具(像 ansible-navigator
命令)的输出,给你展示更多有用的信息。
拿 timer 插件 来说,它会在 ansible-navigator
的输出中显示 Playbook 的执行时间,这样你就能轻松知道每次任务用了多久,非常方便!
你可以通过在 ansible.cfg
文件中使用 callbacks_enabled
指令来启用需要的回调插件。这里是一个简单的示例:
1 | [defaults] |
这些回调插件的作用如下:
timer
:显示整个 Playbook 的执行时间,帮助你了解总耗时。profile_tasks
:分析每个任务的执行时间,方便找出性能瓶颈或耗时较长的任务。cgroup_perf_recap
:监控性能资源,比如 CPU 和内存的使用情况,这对于追踪系统资源消耗非常有用。
启用这些插件后,运行 Playbook 时输出信息会更详细,让你对任务执行的时间分布和资源消耗有更清晰的了解。
若想列出所有可用的回调插件,可以运行以下命令:
1 | ansible-navigator doc -t callback -l -m stdout |
解释一下:
-t callback
:指定查看回调插件类型。-l
:列出所有插件的名称。-m stdout
:输出格式为标准输出,方便查看。
运行这个命令后,你会看到一份完整的回调插件列表,包括每个插件的名称和说明,供你选择启用。
若要查看某个特定回调插件的文档,你可以运行以下命令:
1 | ansible-navigator doc -t callback <plug-in-name> -m stdout |
解释:
-t callback
:指定查看的是回调插件类型。<plug-in-name>
:替换为你想要查看的插件的名字,例如timer
。-m stdout
:以标准输出的方式展示文档内容。
例如,如果你想查看 timer
插件的文档,可以运行:
1 | ansible-navigator doc -t callback timer -m stdout |
你可以用 timer
、profile_tasks
和 profile_roles
这些回调插件来帮忙找出执行速度慢的任务和角色,方便优化你的 Playbook。
timer
插件:会显示整个 Playbook 的运行时间,这样你可以快速了解总共花了多久。profile_tasks
插件:在 Playbook 运行结束后,列出每个任务的开始时间以及用时,还会按用时从长到短排序,帮你找到最耗时的任务。profile_roles
插件:最后会列出每个角色的总用时,同样按用时从长到短排列,非常适合分析不同角色的性能表现。
要启用这些插件,只需要在 ansible.cfg
文件里用 callbacks_enabled
配置一下,比如这样:
1 | [defaults] |
运行后将有运行用时信息:
1 | ansible-navigator run playbook.yml -m stdout |