Ansible是现在很流行的自动化运维工具简单易用功能强大很多公司都在用。

但是当管理的服务器数量多了之后Ansible的执行速度会变得很慢一个简单的操作可能要跑几十分钟甚至几个小时严重影响效率。

我最近在工作中遇到了Ansible性能问题我们有几百台服务器用Ansible做配置管理和自动化部署,但是每次跑playbook都要等很久特别是批量更新配置的时候,跑一次要半个多小时很痛苦。

后来我花了一些时间研究Ansible的性能优化通过一系列优化把执行时间从几十分钟降到了几分钟效果很明显。

今天想分享一下Ansible性能优化的实战经验帮大家把Ansible从慢变快。

一、性能瓶颈分析

在优化之前,先分析一下Ansible的性能瓶颈在哪里这样才能针对性地优化。

Ansible的工作原理是通过SSH连接到远程服务器把模块文件传到远程服务器执行,然后把结果传回来。所以Ansible的性能瓶颈主要在以下几个方面:

1. SSH连接开销

Ansible默认每个任务都会建立新的SSH连接,或者复用连接,但是,即使复用连接也有一定的开销。当服务器数量多任务多的时候SSH连接的开销会很大是主要的性能瓶颈之一。

2. 并行度不够

Ansible默认的并行数是5也就是,同时只对5台服务器执行任务。如果有几百台服务器5台一批要跑很多批速度自然慢。

3. 事实收集慢

Ansible默认每个playbook开始的时候,都会收集远程服务器的事实(facts)包括系统信息网络信息硬件信息等等这个过程比较慢特别是服务器数量多的时候,收集事实就要花很多时间。

4. 任务设计不合理

很多时候playbook的任务设计不合理也会导致性能差。比如用了太多的循环每个循环项都建立一次连接执行一次模块效率很低。或者用了不合适的模块,比如用shell模块执行简单的操作而不用专门的模块效率也低。

5. 网络带宽限制

当需要传输大文件的时候,比如部署应用包拷贝大的配置文件等等网络带宽会成为瓶颈传输慢导致整体执行慢。

分析了性能瓶颈之后,我们就可以针对性地优化了。

二、SSH优化

SSH连接是Ansible最主要的性能瓶颈之一优化SSH能显著提升速度。

1. 启用SSH连接复用(ControlPersist)

Ansible默认会尝试复用SSH连接,但是需要配置SSH的ControlPersist选项才能真正复用连接。

在ansible.cfg中配置:

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

ControlMaster=auto表示自动创建控制连接ControlPersist=60s表示控制连接在,后台保持60秒在这60秒内后续的SSH连接会复用这个连接不用重新建立连接能节省很多连接开销。

这个优化效果非常明显特别是任务多的时候,能节省大量的SSH连接时间。

2. 禁用SSH的DNS反查

SSH默认会对连接的IP做DNS反查这个过程比较慢特别是DNS配置不好的时候,会超时等待很久。

在sshd_config中配置:

UseDNS no

禁用DNS反查能加快SSH连接速度。

同时在ansible.cfg中也可以配置SSH参数禁用反查:

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o GSSAPIAuthentication=no

GSSAPIAuthentication=no禁用GSSAPI认证也能加快连接。

3. 使用SSH密钥认证

确保Ansible用SSH密钥认证而不是密码认证。密钥认证比密码认证快,而且更安全也不用每次输入密码。

在ansible.cfg中配置私钥路径:

[defaults]
private_key_file = ~/.ssh/id_rsa

或者在inventory中配置ansiblesshprivatekeyfile。

4. 禁用不必要的SSH认证方式

SSH默认会尝试多种认证方式,比如公钥密码GSSAPI等等每种认证方式都要尝试一遍会浪费时间。

在ansible.cfg中配置只用公钥认证:

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o GSSAPIAuthentication=no -o PreferredAuthentications=publickey

PreferredAuthentications=publickey指定只用公钥认证不用尝试其他认证方式能加快连接。

三、并行度优化

Ansible默认的并行数是5对于大量服务器来说太少了提高并行数能显著提升速度。

1. 调整forks参数

在ansible.cfg中配置forks参数提高并行数:

[defaults]
forks = 50

forks就是Ansible同时连接的服务器数量默认是5改成50或者,100根据自己的机器性能和网络情况调整。

但是也不是越大越好forks太大会占用太多的CPU和内存也会对网络造成压力可能反而变慢,或者出错。建议根据实际情况测试找到最优的值一般50-100比较合适。

也可以在执行ansible-playbook的时候,用-f参数指定并行数:

ansible-playbook -f 50 playbook.yml

2. 使用策略插件(strategy)

Ansible默认的策略是linear也就是所有主机都完成当前任务后才进入下一个任务。如果有一台主机很慢其他主机都要等它会浪费时间。

可以改用free策略每台主机完成当前任务后立即进入下一个任务不用等其他主机能提高整体速度。

在playbook中配置:

- hosts: all
  strategy: free
  tasks:
    ...

free策略适合任务之间,没有依赖的场景,如果任务之间,有依赖还是用linear策略比较好。

四、事实收集优化

Ansible默认每个playbook都会收集事实这个过程比较慢优化事实收集能节省很多时间。

1. 禁用不必要的事实收集

如果playbook中没有用到事实变量可以禁用事实收集:

- hosts: all
  gather_facts: no
  tasks:
    ...

禁用事实收集能节省大量的时间特别是服务器数量多的时候。

如果部分任务需要事实可以在需要的时候,手动收集:

- name: Gather facts
  setup:

2. 使用事实缓存

如果需要用事实,但是事实变化不频繁可以用事实缓存把收集到的事实缓存起来下次执行的时候,直接用缓存不用重新收集。

Ansible支持多种缓存后端,比如jsonfileredismemcached等等。

用jsonfile缓存的配置:

[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400

gathering = smart表示,只有缓存不存在,或者过期的时候,才收集事实,否则用缓存。factcachingtimeout = 86400缓存有效期86400秒也就是1天。

用redis缓存的配置:

[defaults]
gathering = smart
fact_caching = redis
fact_caching_connection = localhost:6379:0
fact_caching_timeout = 86400

事实缓存效果很明显第二次执行playbook的时候,不用收集事实能节省很多时间。

3. 只收集需要的事实

如果只需要部分事实可以用setup模块的filter参数只收集需要的事实能加快收集速度:

- name: Gather only needed facts
  setup:
    filter:
      - ansible_distribution
      - ansible_distribution_version
      - ansible_memtotal_mb

这样只收集指定的事实不用收集全部事实速度会快一些。

五、任务优化

任务设计不合理也会导致性能差优化任务能提升速度。

1. 减少任务数量

每个任务都有一定的开销包括SSH连接模块传输执行结果返回等等任务越少开销越小。

可以把多个相关的操作合并到一个任务中,比如用yum模块一次安装多个包:

# 不好的写法每个包一个任务
- name: Install package1
  yum: name=package1 state=present
- name: Install package2
  yum: name=package2 state=present

# 好的写法一次安装多个包
- name: Install packages
  yum:
    name:
      - package1
      - package2
    state: present

2. 合理使用循环

Ansible的循环with_items等会对每个循环项都执行一次模块调用,如果循环项多开销会很大。

尽量用模块本身支持批量操作的功能,比如yum模块支持一次安装多个包file模块支持创建多个文件等等不用循环。

如果必须用循环尽量减少循环项的数量,或者用with_flattened等把列表展开减少嵌套。

3. 使用合适的模块

尽量用Ansible专门的模块而不是用shell或者,command模块执行命令。专门的模块是幂等的,而且执行效率更高也更好维护。

比如安装包用yum或者apt模块不用shell执行yum install。拷贝文件用copy或者template模块不用shell执行scp。管理服务用service模块不用shell执行systemctl。

4. 异步执行长任务

对于执行时间长的任务,比如编译安装大文件传输等等可以用异步执行不用等任务完成继续执行其他任务最后再检查结果。

- name: Long running task
  command: /opt/long_running_script.sh
  async: 3600
  poll: 0
  register: long_task

- name: Check long task result
  async_status:
    jid: "{{ long_task.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 30
  delay: 60

async: 3600表示最长运行3600秒poll: 0表示不轮询立即返回继续下一个任务。后面用async_status检查任务是否完成。

异步执行能让长任务在后台跑不阻塞其他任务提高整体效率。

六、网络传输优化

当需要传输大文件的时候,网络会成为瓶颈优化网络传输能提升速度。

1. 使用压缩传输

Ansible默认会压缩传输的数据,但是可以调整压缩级别,或者用更快的压缩算法。

在ansible.cfg中配置:

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o Compression=yes -o CompressionLevel=6

Compression=yes启用压缩CompressionLevel=6压缩级别1-91最快压缩率低9最慢压缩率高一般6比较均衡。

2. 使用pipelining

Ansible的pipelining功能能减少SSH连接上的文件传输操作把模块直接通过管道传给远程Python执行不用先把模块文件传到远程再执行能提高速度。

在ansible.cfg中配置:

[ssh_connection]
pipelining = True

启用pipelining需要远程服务器的sudoers配置中禁用requiretty否则会报错。在远程服务器的/etc/sudoers中注释掉Defaults requiretty或者,改成Defaults !requiretty。

pipelining效果很明显特别是任务多的时候,能显著提升速度。

3. 本地传输大文件

如果需要传输大文件到多台服务器可以先把文件传到一个中间服务器,然后从中间服务器分发给其他服务器,或者用BitTorrent等P2P方式分发能减轻Ansible控制机的网络压力提高传输速度。

也可以把大文件放到内网的文件服务器,或者对象存储上远程服务器直接从文件服务器下载不用通过Ansible传输能提高速度。

七、其他优化

除了上面的优化,还有一些其他的优化技巧。

1. 使用加速模式(accelerate)

Ansible有一个accelerate模式能在远程服务器上启动一个守护进程后续的任务通过这个守护进程执行不用每次都建立SSH连接传输模块能显著提升速度。

但是accelerate模式需要在远程服务器上开放端口运行守护进程有一定的安全风险,而且现在Ansible的SSH优化已经很好了accelerate模式用得越来越少了。如果需要极致性能可以考虑用。

2. 优化inventory

如果inventory很大,或者用了动态inventory每次执行都要拉取inventory会比较慢。可以把inventory缓存起来,或者用静态inventory减少拉取时间。

也可以用--limit参数只对部分主机执行不用每次都对全部主机执行能节省时间。

3. 使用--check模式测试

在正式执行之前,用--check模式测试playbook是否正确不用真正执行能快速发现问题避免执行到一半出错浪费时间。

ansible-playbook --check playbook.yml

4. 合理组织playbook

把大的playbook拆成小的role或者include按需执行不用每次都执行全部任务。用tag标记任务执行的时候,用--tags只执行指定的任务能节省时间。

- name: Install nginx
  yum: name=nginx state=present
  tags:
    - nginx
    - install
ansible-playbook --tags=nginx playbook.yml

八、优化效果

通过上面的一系列优化我们的Ansible执行速度提升非常明显。

优化前对300台服务器执行一个包含20个任务的playbook需要大约35分钟其中收集事实就要8分钟SSH连接开销也很大。

优化后同样的playbook同样的300台服务器只需要大约4分钟提升了将近9倍效果非常明显。

主要的提升来自:

  1. SSH连接复用节省了大量的连接时间
  2. 提高并行数从5到50同时处理更多服务器
  3. 事实缓存第二次执行不用收集事实
  4. pipelining减少了文件传输开销
  5. 任务优化减少了任务数量和循环

当然具体的提升效果因人而异取决于服务器数量任务复杂度网络情况等等,但是,只要按照上面的方法优化肯定能有明显的提升。

九、注意事项

在优化的过程中也要注意一些问题。

1. 不要盲目提高并行数

forks不是越大越好太大会占用太多的资源也会对远程服务器造成压力可能导致SSH连接失败,或者远程服务器负载过高。建议逐步测试找到最优的值。

2. 注意幂等性

优化的时候,不要为了速度牺牲幂等性。Ansible的优势之一就是幂等多次执行结果一样。用shell模块等的时候,要注意保证幂等性,或者用changed_when等参数正确报告变化。

3. 测试验证

优化后要充分测试确保playbook的功能正确没有,因为优化引入bug。特别是改了策略缓存等配置后要仔细测试。

4. 安全

一些优化配置可能会影响安全,比如禁用requiretty开放accelerate端口等等要评估安全风险在安全和性能之间,找到平衡。

十、写在最后

以上就是Ansible性能优化的实战经验分享。

Ansible的性能优化是一个系统工程需要从SSH并行事实任务网络等多个方面入手综合优化才能达到好的效果。

不要等到Ansible慢得受不了了才想优化在平时写playbook的时候,就要注意性能合理设计任务用合适的模块减少不必要的操作这样Ansible才能一直保持快的速度。

希望我的经验能帮大家把Ansible从慢变快提升运维效率。

如果有什么问题,或者更好的优化技巧欢迎在评论区留言我们一起交流。

最后用一句话结束这篇文章:"性能优化没有最好,只有更好持续优化才能持续提升。"

愿大家的Ansible都能跑得飞快运维工作越来越高效。