• 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']}}"
  • 相关阅读:
    eshint的配置
    jsp 或 php 等view之中使用javascript简单处理的使用技巧
    响应式图片,在不同尺寸下切换不同张数
    swiper.js + jquery.magnific-popup.js 实现奇葩的轮播需要
    Websocket 协议的基本使用与示例
    vue手记
    docker 架构
    webpack基本使用
    vue组件、路由、事件
    vue基本使用
  • 原文地址:https://www.cnblogs.com/cherylgi/p/14421687.html
Copyright © 2020-2023  润新知