如何做好 Ansible 的配置优化?

2021-08-03 10:45:28 浏览数 (1)

前言

Ansible服务端使用了默认的SSH无代理方式与客户端通信,效率远低于有代理的通讯方式,然而通过本文,您将学习到如何通过配置优化,来加速Ansible的执行速度,以提高自动化运维的工作效率,做到等同于有代理通讯方式的效率,包括:

  • 如何开启SSH长连接,避免SSH短连接的通信开销;
  • 开启Pipelin-ing,利用SSH会话来缩短PUT脚本至远端客户端执行的流程;
  • 开启Accelerate模式,利用远端客户端上运行的守护进程,优化Ansible的执行流程;
  • 在Playbook中修改Ansible的执行策略,减少多主机批量执行过程的等待;
  • 在Playbook中使用异步特性,减少执行任务的同步等待;
  • 设置Facts缓存,通过提前将Facts信息缓存于本地JSON文件、Redis或Memcached内存数据库中,来提高PlayBook的执行效率。

1.Ansible执行速度过慢问题

在测试连通性的过程中,利用默认配置,发现有一个问题非常困扰,就是通过Ansible服务端向客户端执行命令非常缓慢,主要体现在创建SSH的通道很慢,虽然Ansible在同一个task里面是并行的控制多台受管端,但是每一个task都需要和受控端创建SSH通道,非常影响效率。由下面也能看出,一个简单的PING命令都要耗时13秒以上。

代码语言:javascript复制
time ansible test -m pingX.X.X.X | SUCCESS => {"changed": false,"ping": "pong"}real 0m13.030suser 0m1.147ssys 0m0.263s1.1.1.1.1.1.1.1.

这种执行效率显然是无法接受的,那么通过配置优化来提升Ansible的执行效率势在必行。

2.开启SSH长连接

我们知道Ansible模式是使用SSH和远端主机进行通信,所以Ansible对于SSH的依赖性非常强,下面我们第一步就从SSH入手来优化Ansible。OpenSSH在5.6版本以后支持了Multiplexing(多路复用)功能,它支持同一主机的多个SSH会话共享单一TCP连接进行通讯,一旦第一个连接建立,后续连接就不再需要凭证,从而消除了每次连接同一机器都需要键入密码的麻烦并且大幅度节省了服务器端的资源,这种功能也称作长连接机制。

如果Ansible中控机的SSH -V版本高于5.6时,我们可以直接在ansible.cfg文件中设置SSH长连接即可。设置参数如下:

代码语言:javascript复制
[ssh_connection]ssh_args = -o ControlMaster=auto -o ControlPersist=5d1.1.

Contro1Persist=5d这个参数是设置整个长连接的保持时间,默认值为60秒,这里设置为5天,如果开启后,通过SSH连接过的设备都会在用户当前目录的~/ansible/cp/目录下生成一个socket文件,每个会话一个文件,以便相同连接复用,也可以通过netstat命令查看,会发现有一个ESTABLISHED状态的连接一直与远端设备进行着TCP连接。如果长连接时间超过Contro1Persist的设定值,连接将被断开,socket文件也将被清理。另外,如果不仅仅需要Ansible开启SSH长连接,SSH命令也保持长连接的话,可以修改Ansible中控端的SSH CLIENT的全局性配置文件:/etc/ssh/ssh_config,或者修改当前用户的~/.ssh/config文件,使本用户生效:

代码语言:javascript复制
ServerAliveInterval 30ServerAliveCountMax 3ControlMaster autoControlPath ~/.ssh/sockets/%r@%h-%pControlPersist 5d1.1.1.1.1.

其中ServerAliveInterval表示client每隔30秒发送一次请求给server,然后server响应,从而保持连接;ServerAliveCountMax表示client发出请求后,服务器端没有响应的次数达到3,就自动断开连接;ControlPath表示socket的存放和复用路径。

如果我们的Ansible中控机SSH-V版本低于5.6时,则需要升级到5.6版本后才能启用SSH Multiplex-ing特性。

通过SSH-V命令查看了下当前演示环境的OpenSSH的版本为:OpenSSH_5.3p1,这次需要将openssh版本升级到OpenSSH_6.7p1,安装包的下载地址为:

http://www.linuxfromscratch.org/blfs/view/7.7/postlfs/openssh.html

升级过程如下:

  • 查看是否缺少依赖包:
代码语言:javascript复制
rpm -qa|egrep “gcc|make|perl|pam|pam-devel”1.
  • 缺少了依赖包,可以通过YUM源直接安装这些缺少的包:
代码语言:javascript复制
yum –y install gcc* make perl pam pam-devel1.
  • 上传安装包:
代码语言:javascript复制
openssh-6.7p1.tar.gz1.
  • 备份ssh:
代码语言:javascript复制
mv /etc/ssh /etc/ssh.bak1.
  • 编译安装新版本OpenSSH:
代码语言:javascript复制
gzip -dc openssh-6.7p1.tar.gz|tar -xvf –cd openssh-6.7p1./configure --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-zlib --with-md5-passwordsmake1.1.1.1.
  • 卸载旧版本OpenSSH:
代码语言:javascript复制
rpm -a|grep opensshrpm -e `rpm –qa|grep openssh` --nodeps1.1.
  • 安装新版本OpenSSH:
代码语言:javascript复制
make install1.
  • 配置OpenSSH,主要是保证ssh-copy-id等命令能够正常使用:
代码语言:javascript复制
cd openssh-6.7p1install -v -m755 contrib/ssh-copy-id /usr/bininstall -v -m644 contrib/ssh-copy-id.1 /usr/share/man/man1install -v -m755 -d /usr/share/doc/openssh-6.7p1install -v -m644 INSTALL LICENCE OVERVIEW README* /usr/share/doc/openssh-6.7p11.1.1.1.1.
  • 查看是否升级到了新版本:
代码语言:javascript复制
ssh –VOpenSSH_6.7p1, OpenSSL 1.0.1e-fips 11 Feb 20131.1.
  • 复制启动脚本到/etc/init.d:
代码语言:javascript复制
cp /root/openssh-6.7p1/contrib/redHat/sshd.init /etc/init.d/sshd1.
  • 将sshd加入开机自启动:
代码语言:javascript复制
chkconfig --add sshd1.
  • 重新启动sshd
代码语言:javascript复制
service sshd restart1.
  • 执行速度验证:
代码语言:javascript复制
time ansible X.X.X.X -a uptimeX.X.X.X | SUCCESS | rc=0 >>15:37:17 up 881 days, 17:12, 1 user, load average: 0.00, 0.00, 0.00real 0m4.228suser 0m2.591ssys 0m0.327s1.1.1.1.1.1.

通过OpenSSH的版本升级,我们发现速度优化较为明显,执行速度为4秒左右。我们当然不然满足于此,看看按照前面介绍的方式,开启Multiplexing功能特性后的Ansible执行速度又如何:

代码语言:javascript复制
time ansible X.X.X.X -a uptimeX.X.X.X | SUCCESS | rc=0 >>15:37:31 up 881 days, 17:12, 1 user, load average: 0.00, 0.00, 0.00real 0m3.075suser 0m2.532ssys 0m0.310s1.1.1.1.1.1.

执行速度又降低了1秒,看来复用了长连接,减少SSH的TCP通信开销还是有效果的。

3.开启Pipelining

Pipelining也是OpenSSH的一个特性,在Ansible的整个执行流程中,包含三个步骤:

  • 首先,基于调用的模块生成一个Python脚本
  • 其次,再将Python脚本复制到主机上
  • 最后,在远端服务器上执行这个Python脚本

我们可以看到,其中有一个流程就是把生成好的本地Python脚本PUT到远端服务器执行,如果开启了Pipelining,Ansible执行Python脚本的时候并不会复制它,而是通过管道传递给SSH会话,Ansible使用的SSH会话将减少到一个,这样可以大大提高整个执行效率。尤其是在在部署大规模服务器或引用模块非常多时,开启Pipelining特性会给Ansible带来更加显著的性能提升。下面我们通过一个示例展示整个过程。首先在ansible.cfg配置文件中设置Pipelining。

代码语言:javascript复制
[ssh_connection]pipelining = True1.1.

再来看开启了Pipelining之后整个Ansible的执行流程有什么变化:

我们可以看到开启了Pipelining之后整个流程少了一个PUT脚本和SFTP去远端服务器的流程,且大量减少了SSH连接次数,第一步就是直接调用ssh执行脚本。那么经过优化后的耗时又如何呢:

代码语言:javascript复制
time ansible X.X.X.X -a uptimeX.X.X.X | SUCCESS | rc=0 >>15:38:26 up 881 days, 17:13, 0 users, load average: 0.00,0.00, 0.00real 0m1.741suser 0m1.429ssys 0m0.176s1.1.1.1.1.1.

耗时又降低了1.3秒。

但是我们要注意,如果在Ansible中使用sudo命令的话,例如:ssh user@host sudo cmd,需要在被控节点的/etc/sudoers中禁用"requiretty"。这是因为ssh远程执行命令时,它的环境是非登录式非交互式 shell,默认不会分配tty,没有tty,ssh的sudo就无法关闭密码回显(业可以在Ansible命令行中使用"-tt"参数来强制SSH分配tty)。所以出于安全考虑,/etc/sudoers中默认是开启requiretty的,它要求只有拥有tty的用户才能使用sudo,也就是说ssh连接过去不允许执行sudo。所以我们需要编辑sudo配置文件,通过注释该选项来禁用它。如果我们在Playbook中不适用sudo越权功能,则不需要在被控节点的sudo配置文件中禁用该选项。

4.开启Accelerate模式

除了OpenSSH上述两个功能之外,Ansible还有一个Accelerate模式,这和前面SSH Multiplexing功能有点类似,因为都依赖Ansible中控机跟远端机器有一个长连接。但是Accelerate是使用Python程序在远端机器上运行一个守护进程,然后Ansible会通过这个守护进程监听的端口进行通信。开启Accelerate模式很简单,只要在Playbook中配置accelerate:true即可开启。但是需要注意,如果开启Accelerate模式,则需要在Ansible中控机与远端机器都安装python-keyczar软件包,软件包的下载地址如下:

http://rpm.pbone.net/index.php3/stat/4/idpl/49957349/dir/redhat_el_6/com/python-keyczar-0.71c-1.el6.noarch.rpm.html

安装python-keyczar软件包,中控机和远端都需要安装。远程批量安装可以参考采用之前所介绍的方式实施。

代码语言:javascript复制
yum install python-pyasn1rpm -ivh python-keyczar-0.71c-1.el6.noarch.rpm1.1.

定义ansible.cfg文件中Accelerate参数,例如远端机器的监昕端口以及timeout设置。当然这些参数也可以在写playbook的时候再定义:

代码语言:javascript复制
[accelerate]accelerate_port = 5099accelerate_timeout= 30accelerate_connect_timeout= 5.01.1.1.1.

在Playbook中定义Accelerate,例如:

代码语言:javascript复制
---- hosts: allaccelerate: trueaccelerate_port: 50991.1.1.1.

然而,RedHat官方目前不赞成使用Accelerate模式。因为在启用ControlPersist和Pipelining管道的情况下,Ansible的执行效率比较高。Accelerate功能在后面的Ansible版本中将被删除。

5.修改Ansible执行策略

默认Ansible在远程执行任务是按批并行执行的,一批控制多少台主机由命令行的-f参数或ansible.cfg配置中的--forks选项控制。例如,默认的并行进程数是5,如果有20台被控主机,那么只有在每5台全部执行完一个任务才继续下一批的5台执行该任务,即使中间某台机器性能较好,完成速度较快,它也会空闲地等待在那,直到所有20台主机都执行完该任务才会以同样的方式继续下一个任务。

在Ansible 2.0版本后,添加了一个策略控制选项strategy,默认值为"linear",即上面按批并行处理的方式。我们还可以设置strategy的值为"free"。在free模式下,Ansible会尽可能快的切入到下一个主机。同样是上面的例子,首先每5台并行执行一个任务,当其中某一台机器由于性能较好提前完成了该任务,它不会等待其他4台完成,而是会跳出该任务让Ansible切入到下一台机器来执行该任务。也就是说,这种模式下,一台主机完成一个任务后,另一台主机会立即执行任务,它是"前赴后继"的方式。所以这种策略的执行结果给人感觉是无序的甚至是杂乱无章的,而且每次执行结果的Task显示顺序很可能不一样。利用Playbook,这两种策略的设置的方式如下:

代码语言:javascript复制
---- hosts: all   strategy: freetasks:...1.1.1.1.1.

6.任务执行优化

默认情况下Playbook中的任务在执行时会一直保持连接,直到该任务在每个主机节点都执行完毕,下一个任务才会开始执行。有时这是不必要的,比如有些操作运行时间比SSH超时时间还要长。解决该问题最简单的方式是一起执行它们,然后轮询直到任务执行完毕,简单的意思就是,像下面的例子,执行任务后,Ansible就不等它了,往下执行下一个任务,然后每隔5秒钟去看看它执行完成没,超时时间为45秒,async参数值代表了这个任务执行时间的上限值。即任务执行所用时间如果超出这个时间,则认为任务失败。如果async参数若未设置,则为同步执行。我们也可以对执行时间非常长(有可能遭遇超时)的操作使用异步模式。为了异步启动一个任务,可以指定其最大超时时间以及轮询其状态的频率。如果没有为 poll 指定值,那么默认的轮询频率是10秒钟。

代码语言:javascript复制
---  - hosts: all   remote_user: root   tasks:      - name: simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec      command: /bin/sleep 15      async: 45      poll: 51.1.1.1.1.1.1.1.

在异步执行任务时,需要注意那些有依赖性的任务。对于那些对资源要求占有排它锁的任务,如yum,不应该将Poll的间隔设置为0。如果设置为0,很可能会导致资源阻塞。总结来说,大概有以下一些场景需要使用到Ansible的异步特性:

  • 某个Task需要运行很长的时间,这个Task很可能会达到ssh连接的Timeout。
  • 没有任务是需要等待它才能完成的,即没有任务依赖此任务是否完成的状态。
  • 需要尽快返回当前shell的。

当然也有一些场景不适合使用异步特性:

  • 这个任务是需要运行完后才能继续另外的任务的。
  • 申请排它锁的任务。

7.设置Facts缓存

当我们用ansible-playbook执行Playbook的时候,我们会发现,默认第一个Task都是GATHERING FACTS,这个过程就是Ansible收集每台主机的Facts信息,方便我们在Playbook中直接引用Facts里的信息。当然如果我们的Playbook中不需要Facts信息,可以在Playbook中设置gather_facts: False来提高Playbook的效率:

代码语言:javascript复制
---- hosts: 10.0.108.2gather_facts: notasks:...1.1.1.1.1.

也可以在ansible.cfg文件中添加如下配置来默认禁用Facts采集:

代码语言:javascript复制
[defaults]gathering = explicit1.1.

但是如果我们既想每次执行Playbook的时候都能收集Facts信息,又想加速这个收集过程,这时候可以设置Facts的缓存。例如,在空闲的时候收集Facts,缓存下来,在需要的时候直接读取缓存进行引用,直到缓存过期。目前Ansible支持使用Json文件,Redis、Memcached内存数据库来存储Facts信息。

  • Json文件缓存Fact信息

使用Json文件作为Fact缓存后端时,Ansible将会把采集的Fact写入Ansible中控机的上的文件中。如果系统中已经存在这个文件,那么Ansible将使用这个文件中的数据,而不再连接到主机去采集Fact。下面我们首先通过示例来了解如何使用Json文件存储Facts信息,在ansible.cfg文件中添加:

代码语言:javascript复制
[defaults]gathering = smartfact_caching_timeout = 86400fact_caching = jsonfilefact_caching_connection = /tmp/ansible_fact_cache1.1.1.1.1.

Ansible的配置文件中可以修改gathering的值为smart、implicit或者explicit。

smart表示默认收集Facts,但Facts已有的情况下不会收集,即使用缓存Facts;

implicit表示默认收集Facts,要禁止收集,必须使用gather_facts: False;

explicit表示默认不收集,要显式收集,必须使用gather_facts: Ture;

在使用Facts缓存时(即设置为smart),Ansible支持两种Facts缓存:redis和jsonfile。这里设置Facts过期时间为86400秒(会根据文件的最后修改时间来确定Facts信息是否过期),Json文件存放在/tmp/ansi-ble_fact_ cache下,下面我们执行一下Playbook:

我们再执行Playbook的时候就没有Facts收集这个过程了。直接从Json文件中读取Facts缓存信息:

可以看到10台受控机的执行耗时仅1.8秒,效率是非常不错的。

  • Redis缓存Fact信息

由于目前Ansible Facts缓存还不支持远端,所以需要在 Ansible中控机上安装Redis服务,并安装Redis Python模块。

如何安装,这里不再赘述,有兴趣可以去下载安装包安装。Redis配置ansible.cfg文件如下所示:

代码语言:javascript复制
[defaults]gathering = smartfact_caching_timeout = 86400fact_caching = redis1.1.1.1.
  • Memcache缓存Fact信息

Ansible Facts存储还支持Memcached 存储,配置方法也很简单,在安装完Memcached服务并运行后,同时安装Python的Memcached依赖包,最后配置ansible.cfg即可:

代码语言:javascript复制
[defaults]gathering = smartfact_caching_timeout = 86400fact_caching = memcached

0 人点赞