zoukankan      html  css  js  c++  java
  • Ansible常用功能说明 [异步、并发、委托等]

    Ansible的同步模式与异步模式

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

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

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

    Ansible的异步和轮询 [async、poll]

    Ansible有时候要执行等待时间很长的操作,这个操作可能要持续很长时间,设置超过ssh的timeout。这种情况下可以选择在step中指定async和poll来实现异步操作。其中:async:表示这个step的最长等待时长, 如果设置为0, 表示一直等待下去直到动作完成;poll:表示检查step操作结果的间隔时长。

    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参数值:代表了任务异步执行时轮询的时间间隔。

    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]
    

    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']}}"
  • 相关阅读:
    HDU 6182 A Math Problem 水题
    HDU 6186 CS Course 位运算 思维
    HDU 6188 Duizi and Shunzi 贪心 思维
    HDU 2824 The Euler function 欧拉函数
    HDU 3037 Saving Beans 多重集合的结合 lucas定理
    HDU 3923 Invoker Polya定理
    FZU 2282 Wand 组合数学 错排公式
    HDU 1452 Happy 2004 数论
    HDU 5778 abs 数论
    欧拉回路【判断连通+度数为偶】
  • 原文地址:https://www.cnblogs.com/cherylgi/p/14421687.html
Copyright © 2011-2022 走看看