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']}}"
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章