文章目錄
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']}}"