Ansible 平常使用技巧 - 运维小结

 

Ansible默认只会建立5个进程并发执行任务,因此一次任务只能同时控制5台机器执行。若是有大量的机器须要控制,例如20台,Ansible执行一个任务时会先在其中5台上执行,执行成功后再执行下一批5台,直到所有机器执行完毕。使用-f选项能够指定进程数,指定的进程数量多一些,不只会实现全并发,对异步的轮训poll也会有正面影响。python

Ansible默认是同步阻塞模式,它会等待全部的机器都执行完毕才会在前台返回。Ansible能够采起异步执行模式。异步模式下,Ansible会将节点的任务丢在后台,每台被控制的机器都有一个job_id,Ansible会根据这个job_id去轮训该机器上任务的执行状况,例如某机器上此任务中的某一个阶段是否完成,是否进入下一个阶段等。即便任务早就结束了,但只有轮训检查到任务结束后才认为该job结束。Ansible能够指定任务检查的时间间隔,默认是10秒。除非指定任务检查的间隔为0,不然会等待全部任务都完成后,Ansible端才会释放占用的shell。若是指定时间间隔为0,则Ansible会当即返回(至少得链接上目标主机,任务发布成功以后当即返回),并不会去检查它的任务进度。web

Ansible的同步模式与异步模式
同步模式: 若是节点数太多,ansible没法一次在全部远程节点上执行任务,那么将先在一部分节点上执行一个任务(每一批节点的数量取决于fork进程数量,默认为5个,可设置),直到这一批全部节点上该任务彻底执行完毕才会接入下一个批节点,直到全部节点将该任务都执行完毕,而后从新回到第一批节点开始执行第二个任务。依次类推,直到全部节点执行完全部任务,ansible端才会释放shell。这是默认同步模式,也就是说在未执行完毕时,ansible是占用当前shell的,任务执行完后,释放shell了才能够输入其余命令作其余动做。redis

异步模式:假如fork控制的并发进程数为5,远程控制节点为24个,则ansible一开始会将5个节点的任务扔在后台,并每隔一段时间去检查这些节点的任务完成状况,当某节点完成不会当即返回,而是继续等待直到5个进程都空闲了,才会将这5个节点的结果返回给ansible端,ansible会继续将下一批5个节点的任务扔在后台并每隔一段时间进行检查,依次类推,直到完成全部任务。shell

在异步模式下,若是设置的检查时间间隔为0,在将每一批节点的任务丢到后台后都会当即返回ansible,并当即将下一批节点的任务丢到后台,直到全部任务都丢到后台完后,才返回ansible端,ansible才会当即释放占用的shell。即此时ansible是不会管各个节点任务执行状况的,无论执行成功或失败。所以在轮训检查时间内,ansible仍然正在运行(尽管某批任务已经被放到后台执行了),当前shell进程仍被占用处于睡眠状态,只有指定的检查时间间隔为0,才会尽快将全部任务放到后台并释放shell。json

1、Ansible的异步和轮询  [ async、poll ]
Ansible有时候要执行等待时间很长的操做,这个操做可能要持续很长时间,设置超过ssh的timeout。这种状况下能够选择在step中指定async和poll来实现异步操做。其中:async表示这个step的最长等待时长, 若是设置为0, 表示一直等待下去直到动做完成poll表示检查step操做结果的间隔时长vim

ansible默认的清单文件是/etc/ansible/hosts,也就是ansible和ansible-ploybook执行时默认读的清单文件。这个能够自行定义。
[root@hostname ~]# cat /etc/ansible/ansible.cfg|grep inventory
#inventory      = /etc/ansible/hosts

[root@hostname ~]# cat /etc/ansible/hosts|tail -2             
[test_server]                   #组名最好不要使用"-",可使用"_"
172.16.60.241

1)先来看下面初始配置
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts : test_server
  tasks :
    - name : ansible-test
      shell : sleep 10
      #async表示上述shell命令的等待时间,设置为0时会一直等待命令结束
      async : 5
      #poll表示检查step操做结果的间隔时长,设置为0表示 不用等待结果,继续作下面的操做,咱们能够在下面的step中来验证这个命令是否成功执行.
      poll : 2

执行下看看是否成功:
[root@hostname ~]# ansible-playbook /etc/ansible/test.yml

PLAY [test_server] *******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.241]

TASK [ansible-test] ******************************************************************************************************************************
fatal: [172.16.60.241]: FAILED! => {"changed": false, "msg": "async task did not complete within the requested time - 5s"}

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0 

如上,这个step失败, 由于ansible的任务(就是上面配置中的shell动做)操做时间(10s)超过了最大等待时长(5s)

2)若是将上面的async异步等待时间设置为大于10s,好比12s,则执行就成功了!
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts : test_server
  tasks :
    - name : ansible-test
      shell : sleep 10
      #async表示上述shell命令的等待时间,设置为0时会一直等待命令结束
      async : 12
      #poll表示检查step操做结果的间隔时长,设置为0表示 不用等待结果,继续作下面的操做,咱们能够在下面的step中来验证这个命令是否成功执行.
      poll : 2

[root@hostname ~]# ansible-playbook /etc/ansible/test.yml             

PLAY [test_server] *******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.241]

TASK [ansible-test] ******************************************************************************************************************************
changed: [172.16.60.241]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

这时候就不怕任务超时了。能够执行一个12s的任务(大于上面shell执行的时间)。另外,若是poll为0,就至关于一个不关心结果的任务。

3)或者将上面的poll数值设置为0,即不用等待ansible任务执行的结果,当即执行下一个step。
即只须要将任务命令推送到ansible客户机上,不须要等待任务执行完成就当即执行下一个step。

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts : test_server
  tasks :
    - name : ansible-test
      shell : sleep 10
      #async表示上述shell命令的等待时间,设置为0时会一直等待命令结束
      async : 5
      #poll表示检查step操做结果的间隔时长,设置为0表示 不用等待结果,继续作下面的操做,咱们能够在下面的step中来验证这个命令是否成功执行.
      poll : 0

[root@hostname ~]# ansible-playbook /etc/ansible/test.yml

PLAY [test_server] *******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.241]

TASK [ansible-test] ******************************************************************************************************************************
changed: [172.16.60.241]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

4)若是还想要更方便地看轮询结果,ansible还提供了这个模块async_status。
[root@hostname ~]# cat /etc/ansible/test.yml             
- hosts : test_server
  tasks :
    - name : ansible-test
      shell : sleep 3
      async : 8
      poll : 2
      register: kevin_result

    - name: 'check ansible-test task polling results '
      async_status: jid={{ kevin_result.ansible_job_id }}
      register: job_result
      until: job_result.finished
      retries: 10

[root@hostname ~]# ansible-playbook /etc/ansible/test.yml

PLAY [test_server] *******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.241]

TASK [ansible-test] ******************************************************************************************************************************
changed: [172.16.60.241]

TASK [check ansible-test task polling results] ***************************************************************************************************
changed: [172.16.60.241]

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

第一个job执行异步任务sleep,而且注册了一个名字叫kevin-result的register变量,用于提供给第二个job做为轮询对象,而且它本身poll设为2 (即本身轮询2次)。
register用于在ansible的playbook中task之间的相互传递变量,
register 这个功能很是有用。当咱们须要判断对执行了某个操做或者某个命令后,如何作相应的响应处理(执行其余 ansible 语句),则通常会用到register 。
until表示循环。

第二个job使用async_status模块,进行轮询并返回轮询结果。准备检查10次。

async参数值表明了这个任务执行时间的上限值。即任务执行所用时间若是超出这个时间,则认为任务失败。此参数若未设置,则为同步执行。
poll参数值:表明了任务异步执行时轮询的时间间隔。centos

2、Ansible的并发限制  [ serial、max_fail_percentage ]
当ansible清单文件里设置的组里有不少机器,能够限制一下ansible任务的并发。ansible的并发功能能够在ansible.cfg里修改配置,也能够在playbook中限制服务端的并发数量,这是ansible常常用到的一个关键功能。ansible默认状况下只会建立5个进程,因此一次任务只能同时控制5台机器执行。若是有大量的机器须要控制,或者但愿减小进程数,那就能够采起异步执行(async),ansible的模块能够把task放进后台,而后轮询它(poll)。缓存

使用async和poll这两个关键字即可以并行运行一个任务,即在全部机器上一次性运行。async这个关键字会触发ansible并行运做任务,async的值是ansible等待运行这个任务的最大超时值(若是执行超时任务会强制中断致使失败),而poll就是ansible检查这个任务是否完成的频率时间。安全

1) serial参数设置并发数
=====================================================================
通常状况下, ansible会同时在全部服务器上执行用户定义的操做, 可是用户能够经过serial参数来定义同时能够在多少太机器上执行操做。

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts : test_server
  serial: 3

  tasks :
    - name: Install telnet
      yum: name=telnet state=installed

即test_server组内的3台机器彻底执行完成play后, 其余机器才能开始执行。

接着看下面的配置
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts : all
  serial: 7

  tasks :
    - name: Install telnet
      yum: name=telnet state=installed

    - name : Run Serverstart.sh
      command : /bin/bash /opt/scripts/Serverstart.sh
      async : 300
      poll : 10
      register: kevin_result

如上配置,发现当ansible配置控制超过5台机器时,上面ansible中:
a)yum模块会先在5台机器上跑,完成后再继续剩余2台的机器;
b)command模块的任务会一次性在全部机器上都执行了,而后监听它的回调结果;

这里须要注意下面两种状况
a)状况一: 设置poll=0
若是上面command模块是控制机器开启一个进程放到后台,那就不须要检查这个任务是否完成了,只须要继续其余的动做, 
最后再使用wait_for这个模块去检查以前的进程是否按预期中开启了即可。
这时只须要把poll这个值设置为0, 即可以按上面的要求配置ansible不等待job的完成。
b)状况二: 设置async=0
若是有一种需求是有一个task它是须要运行很长的时间,那就须要设置一直等待这个job完成。
这个时候只须要把async的值设成0即可。

简单总结下,适合使用到ansible的polling特性的场景
- 有一个task须要运行很长的时间,这个task极可能会达到timeout;
- 有一个任务须要在大量的机器上面运行;
- 有一个任务是不须要等待它完成的;

不适合使用polling特性的场景
- task任务是须要运行完后才能继续另外的任务的;
- task任务能很快的完成;

2) max_fail_percentage:最大失败百分比
=====================================================================
默认状况下, 只要ansible的group中还有server没有失败, ansible就是继续执行tasks。实际上, 用户能够经过max_fail_percentage(最大失败百分比)来限制ansible的并发执行。
只要超过max_fail_percentage的server失败, ansible就能够停止tasks的执行。serial参数在ansible-1.8之后就开始支持百分比功能了!!

试想一下若是group组里有200台机器,那么若是使用serial来限制并发数量,好比设置serial=10,意思就是一次只执行10台,一直到200台完成。
只要组内还有server没有失败, ansible就是继续执行tasks。这样就显得效率很低了,很不方便!这时就可使用相似控制流的max_fail_percentage功能了!!

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts : all
  max_fail_percentage: 30
  serial: 10

  tasks :
    - name: Install telnet
      yum: name=telnet state=installed

    - name : Run Serverstart.sh
      command : /bin/bash /opt/scripts/Serverstart.sh
      async : 300
      poll : 10
      register: kevin_result

如上配置,即10台机器里有30%的机器执行yum模块的task任务失败,那么就终止这个10台机器的task任务的执行,接着执行下一组10台机器的task任务,这样效果就很棒了。

舒适提示:
实际失败机器必须大于这个百分比时, tasks任务才会被停止;若是等于这个百分比时,task任务是不会被终止的!

踩坑经验Ansible并发失败(fork=100. 可是真正执行playbook时并无实现并发)性能优化

[root@hostname ~]# cd /usr/lib/python2.7/site-packages/ansible/
[root@hostname ansible]# find . -name ssh.py
./plugins/connection/ssh.py

[root@hostname ansible]# vim plugins/connection/ssh.py
.........
.........
   if C.HOST_KEY_CHECKING and not_in_host_file:
        # lock around the initial SSH connectivity so the user prompt about whether to add
        # the host to known hosts is not intermingled with multiprocess output.
        fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX)
        fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX)

   # create process
  (p, stdin) = self._run(ssh_cmd, in_data)
.........
.........

经过以上文件代码能够看出:
若是ansible配置"HOST_KEY_CHECKING=True", 而且ansible客户机信息没有在ansible服务端的~/.ssh/known_hosts里面, 一个进程就会锁死~/.ssh/known_hosts文件。
这样ansible就不能实现并发!

解决方案:
在ansible服务端的/etc/ansible/ansible.cfg文件里配置"host_key_checking = False"    [其实ansible.cfg文件里该项默认配置的就是False]

3、Ansible的任务委托  [ delegate_to、delegate_facts、run_once ]
默认状况下,ansible的全部任务都是在指定的机器上运行的。当在一个独立的群集环境中配置时,只是想操做其中的某一台主机,或者在特定的主机上运行task任务,此时就须要用到ansible的任务委托功能。使用delegate_to关键字能够配置task任务在指定的机器上执行就是说其余的task任务仍是在hosts关键字配置的机器上运行,到了这个关键字所在的任务时,就使用委托的机器运行。

1)委托
=====================================================================
经过"delegate_to", ansible能够把某一个task任务放在委托的机器上执行。即在指定的组内的某一台或多台机器上执行task任务。

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts : test_server
  serial: 10

  tasks :
    - name: test-haha
      shell: echo "test" > /root/test.list
      delegate_to: 172.16.60.245

则上面的shell模块的task任务只会在172.16.60.245这台节点上执行,test_server组内其余的机器不会执行shell任务。

---------------------
若是 "delegate_to: 127.0.0.1" 则能够用local_action来代替。即下面两个配置效果是同样的!!
[root@hostname ~]# cat /etc/ansible/test.yml 
- hosts : test_server
  serial: 10

  tasks :
    - name: test-haha
      shell: echo "test" > /root/test.list
      delegate_to: 127.0.0.1

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts : test_server
  serial: 10

  tasks :
    - name: test-haha
      local_action: shell echo "test" > /root/test.list

-------------------
若是设置了多个delegate_to,则执行时只会匹配最下面那个。
例以下面配置中,只会执行"delegate_to: 172.16.60.245", 上面那个"delegate_to: 172.16.60.241"就会被忽略了。
[root@hostname ansible]# cat /etc/ansible/test.yml
- hosts : all
  serial: 10

  tasks :
    - name: test-haha
      shell: echo "test" > /root/test.list
      delegate_to: 172.16.60.241
      delegate_to: 172.16.60.245

-------------------
delegate_to默认后面只能跟一个主机ip,不能跟多个主机ip。即默认委托到单个主机。
若是有多个ip须要委托,则能够将这些ip从新放一个group,而后delegate_to委托给group组。
delegate_to委托到组的方式:经过items变量方式!!!

[root@hostname ansible]# cat /etc/ansible/hosts |tail -8
[test_server]
172.16.60.241
172.16.60.245
172.16.60.246
127.0.0.1

[kevin_server]
172.16.60.246
127.0.0.1

[root@hostname ansible]# cat /etc/ansible/test.yml 
- hosts: all
  tasks:
    - name: test-haha
      shell: echo "test" > /root/test.list
      delegate_to: "{{item}}"
      with_items: "{{groups['kevin_server']}}"

即将shell这个task任务委托给kevin_server组内的机器执行。


2)委托者的facts
=====================================================================
默认状况下, ansible委托任务的facts是inventory_hostname中主机的facts, 而不是被委托机器的facts。

a) delegate_facts
在ansible 2.0 中, 经过设置"delegate_facts: True"可让task任务去收集被委托机器的facts。
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: test_server
  tasks:
    - name: test-haha
      shell: echo "test" > /root/test.list
      delegate_to: "{{item}}"
      delegate_facts: True
      with_items: "{{groups['kevin_server']}}"

如上配置,表示会收集kevin_server的facts并分配给这些机器, 而不会去收集test_server的facts

b)RUN ONCE
经过设置"run_once: true"来指定该task只能在委托的某一台机器或委托的组内机器上执行一次!!能够和delegate_to 结合使用。
若是没有delegate_to, 那么这个task默认就会在第一台机器上执行!!!
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: test_server
  tasks:
    - name: test-haha
      shell: echo "test" > /root/test.list
      delegate_to: "{{item}}"
      run_once: true
      delegate_facts: True
      with_items: "{{groups['kevin_server']}}"

4、Ansible的任务暂停  [ local_action、wait_for ]
当Ansible一些任务的运行须要等到一些状态的恢复,好比某一台主机或者应用刚刚重启,须要等待其某个端口开启,这个时候就须要用到Ansible的任务暂停功能。Ansible任务的暂停操做是经过local_action配合wait_for模块来完成的

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: test_server
  remote_user: root
  gather_facts: no

  tasks:
    - name: kevin_test
      local_action:
        module: wait_for           #模块名字
        port: 2379
        host: 172.16.60.241
        delay: 10
        timeout: 300
        state: started


使用local_action配合wait_for模块来完成任务的暂停操做。
上面配置说明kevin_test任务每隔10s检查指定主机上的2379端口是否开启,若是操做300s,2379端口任未开启,将返回失败信息。

上面host指定了一台机器,若是是须要指定多台机器呢?
能够将执行的多台机器放在一台新group内,而后经过变量去指定group。
[root@hostname ~]# cat /etc/ansible/test.yml             
- hosts: test_server
  remote_user: root
  gather_facts: no

  tasks:
    - name: kevin_test
      local_action:
        module: wait_for
        port: 2379
        host: "{{item}}"
        delay: 10
        timeout: 300
        state: started
      with_items: "{{groups['kevin_server']}}"


如上面配置,每间隔10s检查指定的kevin_server组内的主机的2379端口是否开启,若是操做300s,2379端口没开启,则返回失败信息。
注意:上面的"with_items"这一项配置要和"local_action"对齐!!不然会报错!

5、Ansible如何判断并中断执行  [ when、fail ]
在使用ansible-playbook在执行一个脚本时,如何根据脚本返回的内容判断是否继续往下执行仍是中断执行?查询官网能够发现使用register寄存器能够实现记录脚本输出,而且使用when+fail模块来判断是否往下继续执行或者中断

远端机器172.16.60.242有以下脚本:
[root@242 ~]# cat /mnt/scripts/test.sh 
#!/bin/bash

TEXT=$1

if [ $1 == "kevin" ];then
   echo "Success"
else
   echo "Failed"
fi

[root@242 ~]# /bin/bash /mnt/scripts/test.sh kevin
Success
[root@242 ~]# /bin/bash /mnt/scripts/test.sh kevin234
Failed

如今要求:
a)经过ansible执行172.16.60.242的test.sh脚本,当脚本返回Success时,在172.16.60.242机器上建立一个目录/opt/kevin。
b)经过ansible执行172.16.60.242的test.sh脚本,当脚本返回Failed时,则中断执行。


在ansible服务端配置yml文件,相关配置过程以下:
1)以下配置,将command模块的task任务委托给kevin_server组内的172.16.60.242机器执行。
   先使用了register寄存器,具体寄存了什么内容,可使用-v参数来查看输出

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root

  tasks :
    - name: an_bo
      command: /bin/bash /mnt/scripts/test.sh kevin
      delegate_to: 172.16.60.242
      register: result

[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml
Using /etc/ansible/ansible.cfg as config file

PLAY [kevin_server] ******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.242]
ok: [172.16.60.241]

TASK [an_bo] *************************************************************************************************************************************
changed: [172.16.60.242 -> 172.16.60.242] => {"changed": true, "cmd": ["/bin/bash", "/mnt/scripts/test.sh", "kevin"], "delta": "0:00:00.004078", 
"end": "2019-10-11 15:35:49.850430", "rc": 0, "start": "2019-10-11 15:35:49.846352", "stderr": "", "stderr_lines": [], "stdout": "Success", 
"stdout_lines": ["Success"]}
changed: [172.16.60.241 -> 172.16.60.242] => {"changed": true, "cmd": ["/bin/bash", "/mnt/scripts/test.sh", "kevin"], "delta": "0:00:00.004502", 
"end": "2019-10-11 15:35:49.852445", "rc": 0, "start": "2019-10-11 15:35:49.847943", "stderr": "", "stderr_lines": [], "stdout": "Success", 
"stdout_lines": ["Success"]}

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
172.16.60.242              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

能够看出:
register保存的信息就是上面执行结果中"=>"后面的字典信息,信息保存在result变量中。
而且看到"stdout"就是脚本的标准输出信息,这时可使用"when"来判断是否执行或者跳过。

2)使用"when"来判断是否执行或者跳过。
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root

  tasks :
    - name: an_bo
      command: /bin/bash /mnt/scripts/test.sh kevin
      delegate_to: 172.16.60.242
      register: result
 
    - name: ru_bo
      file: path=/opt/kevin state=directory
      delegate_to: 172.16.60.242
      when: result.stdout == 'Success'

查看执行结果:
[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml
Using /etc/ansible/ansible.cfg as config file

PLAY [kevin_server] ******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.241]
ok: [172.16.60.242]

TASK [an_bo] *************************************************************************************************************************************
changed: [172.16.60.242 -> 172.16.60.242] => {"changed": true, "cmd": ["/bin/bash", "/mnt/scripts/test.sh", "kevin"], "delta": "0:00:00.002337", "end": "2019-10-11 15:48:20.427582", "rc": 0, "start": "2019-10-11 15:48:20.425245", "stderr": "", "stderr_lines": [], "stdout": "Success", "stdout_lines": ["Success"]}
changed: [172.16.60.241 -> 172.16.60.242] => {"changed": true, "cmd": ["/bin/bash", "/mnt/scripts/test.sh", "kevin"], "delta": "0:00:00.002579", "end": "2019-10-11 15:48:20.425082", "rc": 0, "start": "2019-10-11 15:48:20.422503", "stderr": "", "stderr_lines": [], "stdout": "Success", "stdout_lines": ["Success"]}

TASK [ru_bo] *************************************************************************************************************************************
changed: [172.16.60.241 -> 172.16.60.242] => {"changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/opt/kevin", "size": 6, "state": "directory", "uid": 0}
ok: [172.16.60.242 -> 172.16.60.242] => {"changed": false, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/opt/kevin", "size": 6, "state": "directory", "uid": 0}

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
172.16.60.242              : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

能够发现,当脚本返回Success时,已经在172.16.60.242机器上建立一个目录/opt/kevin。

3)如今将脚本输出内容修改成"Failed" (即执行脚本时,$1为非kevin字符)
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root

  tasks :
    - name: an_bo
      command: /bin/bash /mnt/scripts/test.sh shibo
      delegate_to: 172.16.60.242
      register: result
 
    - name: if stdout 'Failed',Interrupt execution
      delegate_to: 172.16.60.242
      fail: msg="Check Failed"
      when: result.stdout == 'Failed'

    - name: ru_bo
      file: path=/opt/kevin state=directory
      delegate_to: 172.16.60.242
      when: result.stdout == 'Success'

查看执行结果:
[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml
Using /etc/ansible/ansible.cfg as config file

PLAY [kevin_server] ******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.242]
ok: [172.16.60.241]

TASK [an_bo] *************************************************************************************************************************************
changed: [172.16.60.241 -> 172.16.60.242] => {"changed": true, "cmd": ["/bin/bash", "/mnt/scripts/test.sh", "shibo"], "delta": "0:00:00.002767", "end": "2019-10-11 15:57:56.049142", "rc": 0, "start": "2019-10-11 15:57:56.046375", "stderr": "", "stderr_lines": [], "stdout": "Failed", "stdout_lines": ["Failed"]}
changed: [172.16.60.242 -> 172.16.60.242] => {"changed": true, "cmd": ["/bin/bash", "/mnt/scripts/test.sh", "shibo"], "delta": "0:00:00.002698", "end": "2019-10-11 15:57:56.051455", "rc": 0, "start": "2019-10-11 15:57:56.048757", "stderr": "", "stderr_lines": [], "stdout": "Failed", "stdout_lines": ["Failed"]}

TASK [if stdout 'Failed',Interrupt execution] ****************************************************************************************************
fatal: [172.16.60.241 -> 172.16.60.242]: FAILED! => {"changed": false, "msg": "Check Failed"}
fatal: [172.16.60.242 -> 172.16.60.242]: FAILED! => {"changed": false, "msg": "Check Failed"}

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
172.16.60.242              : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

能够看出:
playbook运行到第二个task时就会报错并抛出msg!根据第二个task任务,脚本输出结果为"Failed",直接中断任务执行。那么第三个task任务就不会被执行了。

注意:
result寄存器中的数据均可以拿来使用,如"rc","stderr"等。
固然也有不少种方法,文中的"Failed"是严格匹配,也可使用模糊查找,如"result.stdout.find('Failed') != -1"也能够达到相同的效果

[root@hostname ~]# cat /etc/ansible/test.yml                
- hosts: kevin_server
  remote_user: root

  tasks :
    - name: an_bo
      command: /bin/bash /mnt/scripts/test.sh shibo
      delegate_to: 172.16.60.242
      register: result
 
    - name: if stdout 'Failed',Interrupt execution
      delegate_to: 172.16.60.242
      fail: msg="Check Failed"
      when: result.stdout.find('Failed') != -1            # 等同于 when: result.stdout == 'Failed'

    - name: ru_bo
      file: path=/opt/kevin state=directory
      delegate_to: 172.16.60.242
      when: result.stdout == 'Success'

6、Ansible之条件判断  [ when ]
在平常运维工做中,在有的时候ansble-playbook的结果依赖于变量、fact或者是前一个任务的执行结果,从而须要使用到条件语句。使用ansible-playbook时,可能须要对某些条件进行判断,只有当知足条件才执行相应的tasks。有下面几种条件判断:

1)when条件判断:只条知足when的条件时才执行对应的tasks
=====================================================================
须要注意:when关键字后面跟着的是python的表达式,在表达式中咱们可以使用任何的变量或者facts。

另外注意:当须要用远程主机的一些信息时,gather_facts必需要开启,默认是开启状态!!!!!
[root@hostname ~]# cat /etc/ansible/hosts |tail -3
[kevin_server]
172.16.60.241
172.16.60.242

注意:下面debug中msg后面引用的变量都是在setup模块中查询出来的(可直接做为变量引用)
[root@hostname ~]# ansible 172.16.60.242 -m setup|grep ansible_fqdn
        "ansible_fqdn": "webserver02",

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root
  gather_facts: True

  tasks:
    - name: Host 172.16.60.242 run this task
      debug: 'msg=" {{ ansible_default_ipv4.address }}"'
      when: ansible_default_ipv4.address == "172.16.60.242"

    - name: memtotal < 500M and processor_cores == 2 run this task
      debug: 'msg="{{ ansible_fqdn }}"'
      when: ansible_memtotal_mb < 500 and ansible_processor_cores == 2

    - name: all host run this task
      shell: hostname
      register: info

    - name: Hostname is webserver01 Machie run this task
      debug: 'msg="{{ ansible_fqdn }}"'
      when: info['stdout'] == "webserver01"

    - name: Hostname is startswith l run this task
      debug: 'msg="{{ ansible_fqdn }}"'
      when: info['stdout'].startswith('l')

查看执行结果:
[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml
Using /etc/ansible/ansible.cfg as config file

PLAY [kevin_server] ******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.242]
ok: [172.16.60.241]

TASK [Host 172.16.60.242 run this task] **********************************************************************************************************
skipping: [172.16.60.241] => {}
ok: [172.16.60.242] => {
    "msg": " 172.16.60.242"
}

TASK [memtotal < 500M and processor_cores == 2 run this task] ************************************************************************************
skipping: [172.16.60.241] => {}
skipping: [172.16.60.242] => {}

TASK [all host run this task] ********************************************************************************************************************
changed: [172.16.60.241] => {"changed": true, "cmd": "hostname", "delta": "0:00:00.003661", "end": "2019-10-11 17:19:29.912525", "rc": 0, 
"start": "2019-10-11 17:19:29.908864", "stderr": "", "stderr_lines": [], "stdout": "webserver01", "stdout_lines": ["webserver01"]}
changed: [172.16.60.242] => {"changed": true, "cmd": "hostname", "delta": "0:00:00.004133", "end": "2019-10-11 17:19:29.922962", "rc": 0, 
"start": "2019-10-11 17:19:29.918829", "stderr": "", "stderr_lines": [], "stdout": "webserver02", "stdout_lines": ["webserver02"]}

TASK [Hostname is webserver01 Machie run this task] **********************************************************************************************
ok: [172.16.60.241] => {
    "msg": "k8s-master01"
}
skipping: [172.16.60.242] => {}

TASK [Hostname is startswith l run this task] ****************************************************************************************************
skipping: [172.16.60.241] => {}
skipping: [172.16.60.242] => {}

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=3    changed=1    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0   
172.16.60.242              : ok=3    changed=1    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0 

2)when条件判断之引用变量
=====================================================================
when变量引用错误提示:[WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}.

正确的引用方式:将{{}} or {% %} 改成()

错误写法示例:when: ansible_default_ipv4.address == {{ webserver01 }}
正确写法示例:when: ansible_default_ipv4.address == (webserver01)

[root@hostname ~]# cat /etc/ansible/test.yml                                              
- hosts: kevin_server
  remote_user: root
  gather_facts: True

  tasks:
    - name: Host 192.168.1.101 run this task
      #debug: 'msg=" {{ ansible_default_ipv4.address }}"'
      shell: hostname
      when: ansible_default_ipv4.address == (webserver02)

查看执行结果:
[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml -e "webserver02=172.16.60.242" 
Using /etc/ansible/ansible.cfg as config file

PLAY [kevin_server] ******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.241]
ok: [172.16.60.242]

TASK [Host 192.168.1.101 run this task] **********************************************************************************************************
skipping: [172.16.60.241] => {"changed": false, "skip_reason": "Conditional result was False"}
changed: [172.16.60.242] => {"changed": true, "cmd": "hostname", "delta": "0:00:00.004349", "end": "2019-10-11 17:23:39.961860", "rc": 0, 
"start": "2019-10-11 17:23:39.957511", "stderr": "", "stderr_lines": [], "stdout": "webserver02", "stdout_lines": ["webserver02"]}

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
172.16.60.242              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

3)changed_when:先执行task,并对task返回的值进行判断,当知足changed_when指定的条件时说明是执行成功的
=====================================================================
须要注意:默认状况下执行了命令的主机状态都为changed,本例对输出进行判断,包含是某个指定字符才能为changed; 

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root
  gather_facts: True

  tasks:
    - name: all host run this task
      shell: hostname
      register: info
      changed_when: '"webserver01" in info.stdout'

查看执行结果:
[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml 
Using /etc/ansible/ansible.cfg as config file

PLAY [kevin_server] ******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.241]
ok: [172.16.60.242]

TASK [all host run this task] ********************************************************************************************************************
changed: [172.16.60.241] => {"changed": true, "cmd": "hostname", "delta": "0:00:00.004531", "end": "2019-10-11 17:25:15.865591", "rc": 0, 
"start": "2019-10-11 17:25:15.861060", "stderr": "", "stderr_lines": [], "stdout": "webserver01", "stdout_lines": ["webserver01"]}
ok: [172.16.60.242] => {"changed": false, "cmd": "hostname", "delta": "0:00:00.004694", "end": "2019-10-11 17:25:15.872135", "rc": 0, 
"start": "2019-10-11 17:25:15.867441", "stderr": "", "stderr_lines": [], "stdout": "webserver02", "stdout_lines": ["webserver02"]}

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
172.16.60.242              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

4)failed_when
=====================================================================
failed_when:当执行失败后,会将信息存在register的stderr中,经过判断指定的字符是否在stderr中来肯定是否真的失败;

[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root
  gather_facts: True

  tasks:
    - name: this command prints FAILED when it fails
      command: echo "FAILED"
      register: command_result
      failed_when: "'FAILED' in command_result.stdout"

    - name: this is a test
      shell: echo "haha"

[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml
Using /etc/ansible/ansible.cfg as config file

PLAY [kevin_server] ******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.242]
ok: [172.16.60.241]

TASK [this command prints FAILED when it fails] **************************************************************************************************
fatal: [172.16.60.241]: FAILED! => {"changed": true, "cmd": ["echo", "FAILED"], "delta": "0:00:00.002550", "end": "2019-10-11 19:19:47.918921", "failed_when_result": true, "rc": 0, "start": "2019-10-11 19:19:47.916371", "stderr": "", "stderr_lines": [], "stdout": "FAILED", "stdout_lines": ["FAILED"]}
fatal: [172.16.60.242]: FAILED! => {"changed": true, "cmd": ["echo", "FAILED"], "delta": "0:00:00.002410", "end": "2019-10-11 19:19:47.943843", "failed_when_result": true, "rc": 0, "start": "2019-10-11 19:19:47.941433", "stderr": "", "stderr_lines": [], "stdout": "FAILED", "stdout_lines": ["FAILED"]}

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
172.16.60.242              : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0 

能够看出,第一个task任务的failed_when已经知足了,因此就此中止playbook的运行了,下面的task任务也不会执行了!

failed_when实际上是ansible的一种错误处理机制,是由fail模块使用了when条件语句的组合效果。
因此,上面的配置也能够调整成下面写法(上面第一个task能够调整为下面第1和第2个task的写法,是同样的效果):
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root
  gather_facts: True

  tasks:
    - name: this command prints FAILED when it fails
      command: echo "FAILED"
      register: command_result

    - name: fail the play if the previous command did not succeed
      fail: msg="the command failed"
      when: "'FAILED' in command_result.stdout"

    - name: this is a test
      shell: echo "haha"

[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml
Using /etc/ansible/ansible.cfg as config file

PLAY [kevin_server] ******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.242]
ok: [172.16.60.241]

TASK [this command prints FAILED when it fails] **************************************************************************************************
changed: [172.16.60.241] => {"changed": true, "cmd": ["echo", "FAILED"], "delta": "0:00:00.003989", "end": "2019-10-11 19:19:06.741840", "rc": 0, "start": "2019-10-11 19:19:06.737851", "stderr": "", "stderr_lines": [], "stdout": "FAILED", "stdout_lines": ["FAILED"]}
changed: [172.16.60.242] => {"changed": true, "cmd": ["echo", "FAILED"], "delta": "0:00:00.003135", "end": "2019-10-11 19:19:06.744136", "rc": 0, "start": "2019-10-11 19:19:06.741001", "stderr": "", "stderr_lines": [], "stdout": "FAILED", "stdout_lines": ["FAILED"]}

TASK [fail the play if the previous command did not succeed] *************************************************************************************
fatal: [172.16.60.241]: FAILED! => {"changed": false, "msg": "the command failed"}
fatal: [172.16.60.242]: FAILED! => {"changed": false, "msg": "the command failed"}

PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
172.16.60.242              : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0 

这里就能够看出"failed_when"的做用,它的做用就是当failed_when关键字对应的条件成立时,failed_when会将对应的任务的执行状态设置为失败,以中止playbook的运行!
可是须要注意的时:failed_when虽然会将任务的执行状态设置为失败,可是它并不表明任务真的失败了!就以上面例子来讲,上面的command模块的确时彻底正常的执行了,
只不过在执行以后,failed_when对应的条件成立了,failed_when将command模块的执行状态设置为失败而已!因此,failed_when并不会影响command模块的执行过程,
只会在条件成立时影响command模块最终的执行状态,以便于中止playbook的运行。

所以须要注意:
failed_when:关键字的做用是在条件成立时,将对应任务的执行状态设置为失败!
changed_when:关键字的做用是在条件成立时,将对应任务的执行状态设置为changed!

7、性能优化  [ 提高ansible执行效率 ]
最初,ansible的执行效率和saltstack(基于zeromq消息队列的方式)相比要慢的多的多,特别是被控节点量很大的时候。可是ansible发展到如今,它的效率获得了极大的改善。在被控节点不太多的时候,默认的设置已经够快。即便被控节点数量巨大的时候,也能够经过一些优化去极大的提升ansible的执行效率。因此在使用 Ansible 的过程当中,当管理的服务器数量增长时,不得不面对一个没法避免的问题执行效率慢,这里列出一些解决办法。

优化一: 关闭gathering facts功能

若是观察过ansible-playbook的执行过程,就会发现ansible-playbook的第1个步骤老是执行gather facts,不论你有没有在playbook设定这个tasks。
若是你不须要获取被控机器的fact数据的话,就能够关闭获取fact数据功能。关闭以后,能够加快ansible-playbook的执行效率,尤为是你管理很大量的机器时,这很是明显。
关闭获取facts很简单,只须要在playbook文件中加上"gather_facts: False" 或者 "gather_facts: No"便可(False和No都为小写也能够)。
     
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root
     
  tasks:
    - name: this is a test
      shell: echo "haha"
     
执行这个paly,会发现第一个执行的是gather facts,由于默认是打开gather facts功能的!!!!
[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml
Using /etc/ansible/ansible.cfg as config file
     
PLAY [kevin_server] ******************************************************************************************************************************
     
TASK [Gathering Facts] ***************************************************************************************************************************
ok: [172.16.60.242]
ok: [172.16.60.241]
     
TASK [this is a test] ****************************************************************************************************************************
changed: [172.16.60.241] => {"changed": true, "cmd": "echo \"haha\"", "delta": "0:00:00.002949", "end": "2019-10-11 19:33:54.883702", "rc": 0, "start": "2019-10-11 19:33:54.880753", "stderr": "", "stderr_lines": [], "stdout": "haha", "stdout_lines": ["haha"]}
changed: [172.16.60.242] => {"changed": true, "cmd": "echo \"haha\"", "delta": "0:00:00.003409", "end": "2019-10-11 19:33:54.884398", "rc": 0, "start": "2019-10-11 19:33:54.880989", "stderr": "", "stderr_lines": [], "stdout": "haha", "stdout_lines": ["haha"]}
     
PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
172.16.60.242              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
     
如今关闭gathering facts功能
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root
  gather_facts: False
     
  tasks:
    - name: this is a test
      shell: echo "haha"
     
再执行这个play,就会发现没有了gathering facts执行过程,整个执行速度也快了!
[root@hostname ~]# ansible-playbook -v /etc/ansible/test.yml
Using /etc/ansible/ansible.cfg as config file
     
PLAY [kevin_server] ******************************************************************************************************************************
     
TASK [this is a test] ****************************************************************************************************************************
changed: [172.16.60.242] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "echo \"haha\"", "delta": "0:00:00.002571", "end": "2019-10-11 19:35:06.821842", "rc": 0, "start": "2019-10-11 19:35:06.819271", "stderr": "", "stderr_lines": [], "stdout": "haha", "stdout_lines": ["haha"]}
changed: [172.16.60.241] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "echo \"haha\"", "delta": "0:00:00.003121", "end": "2019-10-11 19:35:06.842207", "rc": 0, "start": "2019-10-11 19:35:06.839086", "stderr": "", "stderr_lines": [], "stdout": "haha", "stdout_lines": ["haha"]}
     
PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
172.16.60.242              : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

优化二: 开启 SSH pipelining

pipeline是openssh的一个特性,ssh pipelining 是一个加速Ansible执行速度的简单方法。
 
在ansible执行每一个任务的整个流程中,有一个过程是将临时任务文件put到远程的ansible客户机上,而后经过ssh链接过去远程执行这个任务。
若是开启了pipelining,一个任务的全部动做都在一个ssh会话中完成,也会省去sftp到远端的过程,它会直接将要执行的任务在ssh会话中进行。
 
ssh pipelining 默认是关闭!!!!之因此默认关闭是为了兼容不一样的sudo 配置,主要是 requiretty 选项。若是不使用sudo,建议开启!!!
打开此选项能够减小ansible执行没有传输时ssh在被控机器上执行任务的链接数。
不过,若是使用sudo,必须关闭requiretty选项。修改/etc/ansible/ansible.cfg 文件能够开启pipelining
    
[root@hostname ~]# vim /etc/ansible/ansible.cfg
........
pipelining = True
    
这样开启了pipelining以后, ansible执行的整个流程就少了一个PUT脚本去远程服务端的流程,而后就能够批量对机器执行命令试下,能够明显感觉到速度的提高。
 
——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
可是要注意的是:
若是在ansible中使用sudo命令的话(ssh user@host sudo cmd),须要在被控节点的/etc/sudoers中禁用"requiretty"!!!!
 
之因此要设置/etc/sudoers中的requiretty,是由于ssh远程执行命令时,它的环境是非登陆式非交互式shell,默认不会分配tty,没有tty,ssh的sudo就没法关闭密码回显(使用
"-tt"选项强制SSH分配tty)。因此出于安全考虑,/etc/sudoers中默认是开启requiretty的,它要求只有拥有tty的用户才能使用sudo,也就是说ssh链接过去不容许执行sudo。
能够经过visudo编辑配置文件,注释该选项来禁用它。
 
[root@webserver01 ~]# grep requiretty /etc/sudoers  
# Defaults    requiretty

优化三:开启SSH长链接 (ControlPersist特性)

ansible自然支持openssh,默认链接方式下,它对ssh的依赖性很是强。因此优化ssh链接,在必定程度上也在优化ansible。其中一点是开启ssh的长链接,即长时间保持链接状态。
  
Ansible模式是使用SSH和远程主机进行通讯, 因此Ansible对SSH的依赖性很是强, 在OpenSSH 5.6版本之后SSH就支持了Multiplexing(多路复用)。
因此若是Ansible中控机的SSH -V版本高于5.6时, 就可使用ControlPersist来提升ssh链接速度,从而提升ansible执行效率。
    
[root@hostname ansible]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
    
[root@hostname ansible]# ssh -V
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017
    
咱们能够直接在ansible.cfg文件中设置SSH长链接, 设置参数以下:
[root@hostname ansible]# vim /etc/ansible/ansible.cfg
..........
ssh_args = -C -o ControlMaster=auto -o ControlPersist=5d
    
注意:
ConrolPersist=5d, 这个参数是设置整个长链接保持时间为5天。
  
开启此参数的ssh长链接功能后,在会话过时前会一直创建链接,在netstat的结果中会看到ssh链接是一直established状态,且经过SSH链接过的设备都会在当前用户家目录的
".ansible/cp"目录下生成一个socket文件,每一个会话对应生成一个socket文件。也能够经过netstat命令查看, 会发现有一个ESTABLISHED状态的链接一直与远程设备进行着TCP链接。
    
[root@hostname ansible]# ps -ef|grep ssh|grep ansible
root      5614     1  0 23:09 ?        00:00:00 ssh: /root/.ansible/cp/7e37065045 [mux]
root      5617     1  0 23:09 ?        00:00:00 ssh: /root/.ansible/cp/e2056334cd [mux]
    
[root@hostname ansible]# netstat -anptu|grep ESTABLISHED|grep ssh|grep /root
tcp        0      0 172.16.60.246:44430     172.16.60.242:22        ESTABLISHED 5617/ssh: /root/.an
tcp        0      0 172.16.60.246:43498     172.16.60.241:22        ESTABLISHED 5614/ssh: /root/.an
    
[root@hostname ansible]# ls /root/.ansible/cp/
7e37065045  e2056334cd
    
须要注意:
ControlPersist 特性须要高版本的SSH才支持,CentOS 6默认是不支持的,若是须要使用,须要自行升级openssh(确保SSH -V版本高于5.6)。
ControlPersist即持久化socket,一次验证,屡次通讯。而且只须要修改 ssh 客户端就行,也就是 Ansible 机器便可。

优化四: 开启accelerate模式  [ 注意:这个只针对centos6系统 ] 

Ansible还有一个accelerate模式, 这和前面的Multiplexing有点相似, 由于都依赖Ansible中控机跟远程机器有一个长链接。
可是accelerate是使用python程序在远程机器上运行一个守护进程, 而后Ansible会经过这个守护进程监听的端口进行通讯。
开启accelerate模式很简单, 只要在playbook中配置accelerate: true便可.
    
可是须要注意的是:
若是开启accelerate模式, 则须要在Ansible中控机与远程机器都安装python-keyczar软件包。
下面是在ansible.cfg文件中定义一些accelerate参数, 固然也能够在写playbook的时候再定义
    
第一步:ansible服务端和客户端都要安装python-keyczar
[root@hostname ~]# yum install -y python-keyczar
    
第二步:修改ansible服务端的ansible.cfg文件
[root@hostname ~]# vim /etc/ansible/ansible.cfg
..........
[accelerate]
accelerate_port = 5099
accelerate_timeout = 30
accelerate_connect_timeout = 5.0
    
第三步:修改ansible服务端的ansible-playbook的剧本文件,加入 accelerate: true
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root
  gather_facts: False
  accelerate: true
     
  tasks:
    - name: this is a test
      shell: echo "haha"
    
须要注意:
这种优化方式只针对centos6系统来提升链接速度。在centos7下不可用,不然会报错:"ERROR! 'accelerate' is not a valid attribute for a Play"
若是ansible没有性能瓶颈的状况下,不推荐使用这种优化措施!

优化五: 设置facts缓存

若是细心的话, 就会发现执行playbook的时候, 默认第一个task都是GATHERING FACTS, 这个过程就是Ansible在收集每台主机的facts信息。
方便咱们在playbook中直接饮用facts里的信息,固然若是你的playbook中不须要facts信息, 能够在playbook中设置"gather_facts: False"来提升playbook效率.
   
可是若是咱们既想在每次执行playbook的时候都能收集facts, 又想加速这个收集过程, 那么就须要配置facts缓存了。
目前Ansible支持使用json文件存储facts信息。
   
第一种缓存方式:使用json文件缓存
[root@hostname ~]# vim /etc/ansible/ansible.cfg
.........
gathering = smart
fact_caching_timeout = 86400
fact_caching = jsonfile
fact_caching_connection = /dev/shm/ansible_fact_cache
   
正常配置palybook,不须要关闭gathering facts功能
[root@hostname ~]# cat /etc/ansible/test.yml
- hosts: kevin_server
  remote_user: root
    
  tasks:
    - name: this is a test
      shell: echo "haha"
   
查看这个playbook过程,用时1.102s(第一次可能稍微慢点,缓存以后,后面执行就很快了)
[root@hostname ~]# time ansible-playbook /etc/ansible/test.yml
   
PLAY [kevin_server] ******************************************************************************************************************************
   
TASK [this is a test] ****************************************************************************************************************************
changed: [172.16.60.241]
changed: [172.16.60.242]
   
PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
172.16.60.242              : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
   
   
real    0m1.102s
user    0m0.879s
sys     0m0.179s
   
若是去掉上面的facts缓存的四行配置,再次执行上面的playbok,发现用时10s左右!!!
   
查看缓存文件:
[root@hostname ~]# ls /dev/shm/ansible_fact_cache/
172.16.60.241  172.16.60.242
   
第二种缓存方式:使用redis存储facts文件需安装redis,还须要安装python库
[root@hostname ~]# yum install redis
   
[root@hostname ~]# yum -y install epel-release
[root@hostname ~]# yum install python-pip
[root@hostname ~]# pip install redis
   
[root@hostname ~]# vim /etc/ansible/ansible.cfg
........
gathering = smart
facts_caching_timeout = 86400      #设置缓存过时时间86400秒
facts_caching = redis              # 使用redis或者 (或者使用memcached,即"facts_caching = memcached")
fact_caching_connection = 127.0.0.1:6379
#若redis设置了密码,好比密码为"admin",则配置修改以下:
# fact_caching_connection = localhost:6379:0:admin
   
启动redis
[root@hostname ~]# systemctl start redis
[root@hostname ~]# lsof -i:6379      
COMMAND     PID  USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
redis-ser 29218 redis    4u  IPv4 291786209      0t0  TCP localhost:6379 (LISTEN)
   
执行上面的palybook
[root@hostname ~]# time ansible-playbook /etc/ansible/test.yml
   
PLAY [kevin_server] ******************************************************************************************************************************
   
TASK [this is a test] ****************************************************************************************************************************
changed: [172.16.60.241]
changed: [172.16.60.242]
   
PLAY RECAP ***************************************************************************************************************************************
172.16.60.241              : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
172.16.60.242              : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
   
   
real    0m1.132s
user    0m0.909s
sys     0m0.178s
   
须要注意:
在使用redis缓存后,若是出现异常(若未出现,请忽略):TypeError: the JSON object must be str, not 'bytes'。
解决办法:
[root@hostname ~]# find / -name ansible
[root@hostname ~]# vim /usr/lib/python2.7/site-packages/ansible/plugins/cache/redis.py
..........
self._cache[key] = json.loads(value.decode('utf-8'))       #修改成这个
   
查看redis存储状况
[root@hostname ~]# redis-cli
127.0.0.1:6379> keys *
1) "ansible_facts172.16.60.242"
2) "ansible_facts172.16.60.241"
3) "ansible_cache_keys"
   
总之:不一样网络环境下的耗时确定是不一样的,可是设置缓存是确定能够加快 Ansible 运行速度的,特别是 playbook 的运行。

优化六: Ansible取消交互

[root@hostname ~]# vim /etc/ansible/ansible.cfg
........
host_key_checking = False          # 打开注释便可
   
取消ssh的yes和no的交互:
[root@hostname ~]# vim /root/.ssh/config
UserKnownHostsFile /dev/null
ConnectTimeout 15
StrictHostKeyChecking no
   
或者直接ssh时增长一个参数
[root@hostname ~]# ssh -o StrictHostKeyChecking=no -p22 root@172.16.60.247

优化七:ansible的-t选项,提升ansible执行效率

ansible的"-t"或"--tree"选项是将ansible的执行结果按主机名保存在指定目录下的文件中。
  
有些时候,ansible执行起来的速度会很是慢,这种慢体如今即便执行的是一个当即返回的简单命令(如ping模块),也会耗时好久,且不是由于ssh链接慢致使的。
若是使用-t选项,将第一次执行获得的结果按inventory中定义的主机名保存在文件中,下次执行到同一台主机时速度将会变快不少,即便以后再也不加上-t选项,
也能够在必定时间内保持迅速执行。即便执行速度正常(如执行一个Ping命令0.7秒左右),使用-t选项也能够在此基础上变得更快。
  
除了使用-t选项,使用重定向将结果重定向到某个文件中也是同样的效果。
这也算是一种ansible提速方式,但在centos6上使用低版本ansible时,有时会出现执行很慢的现象,但不是每次都这样,且centos7执行速度正常
因此这也是一种"bug"式问题,故这种方式没有通用性。
  
[root@hostname ~]# time ansible kevin_server -m command -a "hostname"
[root@hostname ~]# time ansible kevin_server -m command -a "hostname" -t /tmp/test
  
[root@hostname ~]# ll /tmp/a
total 8
-rw-r--r-- 1 root root 2780 Oct 12 02:03 172.16.60.241
-rw-r--r-- 1 root root 2776 Oct 12 02:03 172.16.60.242
  
上面作了对比,发现使用-t或重定向方式,将ansible的执行结果按主机名保存在指定目录下的文件中,ansible执行效率会有所提高。
相关文章
相关标签/搜索