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會當即返回(至少得鏈接上目標主機,任務發佈成功以後當即返回),並不會去檢查它的任務進度。mysql
Ansible的同步模式與異步模式
同步模式: 若是節點數太多,ansible沒法一次在全部遠程節點上執行任務,那麼將先在一部分節點上執行一個任務(每一批節點的數量取決於fork進程數量,默認爲5個,可設置),直到這一批全部節點上該任務徹底執行完畢纔會接入下一個批節點,直到全部節點將該任務都執行完畢,而後從新回到第一批節點開始執行第二個任務。依次類推,直到全部節點執行完全部任務,ansible端纔會釋放shell。這是默認同步模式,也就是說在未執行完畢時,ansible是佔用當前shell的,任務執行完後,釋放shell了才能夠輸入其餘命令作其餘動做。ios
異步模式:假如fork控制的併發進程數爲5,遠程控制節點爲24個,則ansible一開始會將5個節點的任務扔在後臺,並每隔一段時間去檢查這些節點的任務完成狀況,當某節點完成不會當即返回,而是繼續等待直到5個進程都空閒了,纔會將這5個節點的結果返回給ansible端,ansible會繼續將下一批5個節點的任務扔在後臺並每隔一段時間進行檢查,依次類推,直到完成全部任務。nginx
在異步模式下,若是設置的檢查時間間隔爲0,在將每一批節點的任務丟到後臺後都會當即返回ansible,並當即將下一批節點的任務丟到後臺,直到全部任務都丟到後臺完後,才返回ansible端,ansible纔會當即釋放佔用的shell。即此時ansible是不會管各個節點任務執行狀況的,無論執行成功或失敗。所以在輪訓檢查時間內,ansible仍然正在運行(儘管某批任務已經被放到後臺執行了),當前shell進程仍被佔用處於睡眠狀態,只有指定的檢查時間間隔爲0,纔會儘快將全部任務放到後臺並釋放shell。web
1、Ansible的異步和輪詢 [ async、poll ]
Ansible有時候要執行等待時間很長的操做,這個操做可能要持續很長時間,設置超過ssh的timeout。這種狀況下能夠選擇在step中指定async和poll來實現異步操做。其中:async:表示這個step的最長等待時長, 若是設置爲0, 表示一直等待下去直到動做完成;poll:表示檢查step操做結果的間隔時長。redis
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參數值:表明了任務異步執行時輪詢的時間間隔。sql
2、Ansible的併發限制 [ serial、max_fail_percentage ]
當ansible清單文件裏設置的組裏有不少機器,能夠限制一下ansible任務的併發。ansible的併發功能能夠在ansible.cfg裏修改配置,也能夠在playbook中限制服務端的併發數量,這是ansible常常用到的一個關鍵功能。ansible默認狀況下只會建立5個進程,因此一次任務只能同時控制5臺機器執行。若是有大量的機器須要控制,或者但願減小進程數,那就能夠採起異步執行(async),ansible的模塊能夠把task放進後臺,而後輪詢它(poll)。shell
使用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時並無實現併發)json
[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執行效率 ]
最初,ansible的執行效率和saltstack(基於zeromq消息隊列的方式)相比要慢的多的多,特別是被控節點量很大的時候。可是ansible發展到如今,它的效率獲得了極大的改善。在被控節點不太多的時候,默認的設置已經夠快。即便被控節點數量巨大的時候,也能夠經過一些優化去極大的提升ansible的執行效率。因此在使用 Ansible 的過程當中,當管理的服務器數量增長時,不得不面對一個沒法避免的問題執行效率慢,這裏列出一些解決辦法。
1. 關閉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
2. 開啓 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
3. 開啓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 機器便可。
4. 開啓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沒有性能瓶頸的狀況下,不推薦使用這種優化措施!
5. 設置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 的運行。
6. 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
7. 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執行效率會有所提高。
8、Ansible之變量設置
首先Ansible經過facts組件來收集被管理節點信息,facts收集的信息是json格式的,其中任一項均可以看成變量被直接引用,如在ansible-playbook、jinja2模板中引用。
1. Ansible facts
facts組件是用來收集被管理節點信息的,使用setup模塊能夠獲取這些信息。
[root@ss-server ~]# ansible-doc -s setup - name: Gathers facts about remote hosts setup: ..................
以下示例,是setup收集客戶機172.16.60.21的信息示例,因爲收集的信息項很是多,這裏只是截取了部份內容項。
[root@ss-server ~]# ansible 172.16.60.21 -m setup 172.16.60.21 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "172.16.60.21" ], "ansible_all_ipv6_addresses": [ "fe80::20c:29ff:fe03:a452" ], "ansible_apparmor": { "status": "disabled" }, "ansible_architecture": "x86_64", "ansible_bios_date": "07/02/2019", "ansible_bios_version": "6.00", "ansible_cmdline": { "BOOT_IMAGE": "/vmkivin-3.10.0-327.el7.x86_64", "LANG": "en_US.UTF-8", "biosdevname": "0", "crashkernel": "auto", "net.ifnames": "0", "quiet": true, "ro": true, "root": "UUID=b2a70faf-aea4-4d8e-8be8-c7109ac9c8b8" }, ........................................ "ansible_default_ipv6": {}, "ansible_devices": { "sda": { "holders": [], "host": "SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)", "model": "VMware Virtual S", "partitions": { "sda1": { "holders": [], "sectors": "512000", "sectorsize": 512, "size": "250.00 MB", "start": "2048", "uuid": "367d6a77-033b-4037-bbcb-416705ead095" }, "sda2": { "holders": [], "sectors": "37332992", "sectorsize": 512, "size": "17.80 GB", "start": "514048", "uuid": "b2a70faf-aea4-4d8e-8be8-c7109ac9c8b8" }, ................................ "ansible_user_dir": "/root", "ansible_user_gecos": "root", "ansible_user_gid": 0, "ansible_user_id": "root", "ansible_user_shell": "/bin/bash", "ansible_user_uid": 0, "ansible_userspace_architecture": "x86_64", "ansible_userspace_bits": "64", "ansible_virtualization_role": "guest", "ansible_virtualization_type": "VMware", "module_setup": true }, "changed": false }
使用filter能夠篩選指定的facts信息,以下示例:
[root@ss-server ~]# ansible 172.16.60.21 -m setup -a "filter=changed" 172.16.60.21 | SUCCESS => { "ansible_facts": {}, "changed": false } [root@ss-server ~]# ansible localhost -m setup -a "filter=*ipv4" localhost | SUCCESS => { "ansible_facts": { "ansible_default_ipv4": { "address": "172.16.60.20", "alias": "eth0", "broadcast": "172.16.60.255", "gateway": "172.16.60.2", "interface": "eth0", "macaddress": "00:0c:29:d9:0b:71", "mtu": 1500, "netmask": "255.255.255.0", "network": "172.16.60.0", "type": "ether" } }, "changed": false }
總之,facts收集的信息是json格式的,其裏面的任一項均可以在playbook、jinja2模板中看成變量被直接引用。
2. 變量引用json數據的方式
在ansible中,任何一個模塊都會返回json格式的數據,即便是錯誤信息都是json格式的。在ansible中,json格式的數據,其中每一項均可以經過變量來引用它。固然,引用的前提是先將其註冊爲變量。例以下面的playbook是將shell模塊中echo命令的結果註冊爲變量,並使用debug模塊輸出。
--- - hosts: 172.16.60.21 tasks: - shell: echo good work register: kevin_bo - debug: var=kevin_bo
debug輸出的結果以下:
TASK [debug] ********************************************* ok: [172.16.60.21] => { "kevin_bo": { "changed": true, "cmd": "echo good work", "delta": "0:00:00.002086", "end": "2019-05-20 11:23:40.484507", "rc": 0, "start": "2019-05-20 11:23:40.482421", "stderr": "", "stderr_lines": [], "stdout": "good work", "stdout_lines": [ "good work" ] } }
能夠看出,結果是一段json格式的數據,最頂端的key爲kevin_bo,內部是一大段的字典(即便用大括號包圍的),其中的stdout_lines還包含了一個json數組,也就是所謂的yaml列表項(即便用中括號包圍的)。
2.1 引用json字典數據的方式
若是想要輸出json數據的某一字典項,則應該使用"key.dict"或"key['dict']"的方式引用。例如最多見的stdout項"good work"是想要輸出的項,如下兩種方式都能引用該字典變量。
--- - hosts: 172.16.60.21 tasks: - shell: echo good work register: kevin_bo - debug: var=kevin_bo.stdout - debug: var=kevin_bo['stdout']
ansible-playbook的部分輸出結果以下:
TASK [debug] ************************************************ ok: [172.16.60.21] => { "kevin_bo.stdout": "good work" } TASK [debug] ************************************************ ok: [172.16.60.21] => { "kevin_bo['stdout']": "good work" }
"key.dict"或"key['dict']"的方式都能引用,但在dict字符串自己就包含"."的時候,應該使用中括號的方式引用。例如:
anykey['172.16.60.21']
2.2 引用json數組數據的方式
若是想要輸出json數據中的某一數組項(列表項),則應該使用"key[N]"的方式引用數組中的第N項,其中N是數組的index,從0開始計算。若是不使用index,則輸出的是整個數組列表。例如想要輸出上面的stdout_lines中的"good work",它是數組stdout_lines中第一項因此使用stdout_lines[0]來引用,再加上stdout_lines上面的kevin_bo,因而引用方式以下:
--- - hosts: 172.16.60.21 tasks: - shell: echo good work register: kevin_bo - debug: var=kevin_bo.stdout_lines[0]
因爲stdout_lines中僅有一項,因此即便不使用index的方式即kevin_bo.stdout_lines也能獲得指望的結果。輸出結果以下:
TASK [debug] ************************************************* ok: [172.16.60.21] => { "kevin_bo.stdout_lines[0]": "good work" }
接着看下面一段json數據:
"ipv6": [ { "address": "fe80::20c:29ff:fe26:1498", "prefix": "64", "scope": "link" } ]
其中key=ipv6,其內部有且僅有是一個列表項,但該列表內包含了數個字典項。要引用列表內的字典,例如上面的address項。應該以下引用:
ipv6[0].address
2.3 引用facts數據
如上介紹已經能夠了解json數據中的字典和列表項的引用方式,顯然facts中的一大堆數據就能被引用並派上用場了。例如如下是一段facts數據。
[root@ss-server ~]# ansible localhost -m setup -a "filter=*eth*" localhost | SUCCESS => { "ansible_facts": { "ansible_eth0": { "active": true, "device": "eth0", "features": { "busy_poll": "off [fixed]", "fcoe_mtu": "off [fixed]", "generic_receive_offload": "on", ......................... }, "ipv4": { "address": "172.16.60.18", "broadcast": "172.16.60.255", "netmask": "255.255.255.0", "network": "172.16.60.0" }, "macaddress": "00:0c:29:d9:0b:71", "module": "e1000", ............................ } }, "changed": false }
顯然,facts數據的頂級key爲ansible_facts,在引用時應該將其包含在變量表達式中。但自動收集的facts比較特殊,它以ansible_facts做爲key,ansible每次收集後會自動將其註冊爲變量,因此facts中的數據均可以直接經過變量引用,甚至連頂級key即ansible_facts都要省略。
例如引用上面的ipv4的地址address項,須要寫成:
ansible_eth0.ipv4.address
而不能寫成:
ansible_facts.ansible_eth0.ipv4.address
但其餘任意時候,都應該帶上全部的key!
3. 設置本地facts
在ansible收集facts時,還會自動收集/etc/ansible/facts.d/*.fact文件內的數據到facts中,且以ansible_local作爲key。目前fact支持兩種類型的文件:ini和json。固然,若是fact文件的json或ini格式寫錯了致使沒法解析,那麼確定也沒法收集。例如,在/etc/ansible/facts.d目錄下存在一個kevin.fact的文件,其內數據以下:
[root@ss-server ~]# cat /etc/ansible/facts.d/kevin.fact { "member": { "wangbo": { "address": "anhui", "age": "31" }, "liuxiaoru": { "address": "hebei", "age": "28" } } }
ansible收集facts後的本地facts數據以下:
[root@ss-server ~]# ansible localhost -m setup -a "filter=ansible_local" localhost | SUCCESS => { "ansible_facts": { "ansible_local": { "kevin": { "member": { "wangbo": { "age": "31", "address": "anhui" }, "liuxiaoru": { "age": "28", "address": "hebei" } } } } }, "changed": false }
可見,若是想要引用本地文件中的某個key,除了帶上ansible_local外,還必須得帶上fact文件的文件名。例如,引用father的name。
ansible_local.kevin.member.wangbo.address
4. 輸出和引用變量
上面已經展現了一種變量的引用方式:使用debug的var參數。debug的另外一個參數msg也能輸出變量,且msg能夠輸出自定義信息,而var參數只能輸出變量。另外,msg和var引用參數的方式有所不一樣。例如:
--- - hosts: 172.16.60.21 tasks: - debug: 'msg="ipv4 address: {{ansible_eth0.ipv4.address}}"' - debug: var=ansible_eth0.ipv4.address
msg引用變量須要加上雙大括號包圍,既然加了大括號,爲了防止被解析爲內聯字典,還得加引號包圍。這裏使用了兩段引號,由於其內部還包括了一個": ",加引號能夠防止它被解析爲"key: "的格式。而var參數引用變量則直接指定變量名。這就像bash中引用變量的方式是同樣的,有些時候須要加上$,有些時候不能加$。即當引用的是變量的值的時候,msg就須要加雙大括號,就像加$同樣,而當引用的是變量自己的時候,則msg不能加雙大括號。其實雙大括號是jinja2中的分隔符。執行的部分結果以下:
TASK [debug] ***************************************************** ok: [172.16.60.21] => { "msg": "ipv4 address: 172.16.60.21" } TASK [debug] ***************************************************** ok: [172.16.60.21] => { "ansible_eth0.ipv4.address": "172.16.60.21" }
在Ansible使用中,幾乎全部地方均可以引用變量,例如循環、when語句、信息輸出語句、template文件等等。只不過有些地方不能使用雙大括號,有些地方須要使用。
5. 註冊和定義變量的各類方式
Ansible中定義變量的方式有不少種,大體有下面七種:1) 將模塊的執行結果註冊爲變量;2) 直接定義字典類型的變量;3) role中文件內定義變量;4) 命令行傳遞變量;5) 藉助with_items迭代將多個task的結果賦值給一個變量;6) inventory中的主機或主機組變量;7) 內置變量。
5.1 register註冊變量
使用register選項,能夠將當前task的輸出結果賦值給一個變量。例如,下面的示例中將echo的結果"good work"賦值給kevin_bo變量。注意,模塊的輸出結果是json格式的,因此,引用變量時要指定引用的對象。
--- - hosts: localhost tasks: - shell: echo good work register: kevin_bo - debug: var=kevin_bo.stdout
5.2 set_fact定義變量
set_fact和register的功能很類似,也是將值賦值給變量。它更像shell中變量的賦值方式,能夠將某個變量的值賦值給另外一個變量,也能夠將字符串賦值給變量。例如:
--- - hosts: 172.16.60.21 tasks: - shell: echo good work register: kevin_bo - set_fact: var1="{{kevin_bo.stdout}}" - set_fact: var2="your name is" - debug: msg="{{var2}} {{var1}}"
5.3 vars定義變量
能夠在play或task層次使用vars定義字典型變量。若是同名,則task層次的變量覆蓋play層次的變量。例如:
--- - hosts: localhost vars: var1: anhui var2: beijing tasks: - debug: msg="{{var1}} {{var2}}" vars: var2: shanghai
輸出結果爲:
TASK [debug] ******************************************** ok: [localhost] => { "msg": "anhui shanghai" }
5.4 vars_files定義變量
和vars同樣,只不過它是將變量以字典格式定義在獨立的文件中,且vars_files不能定義在task層次,只能定義在play層次。
--- - hosts: localhost vars_files: - /tmp/var_file1.yml - var_file2.yml tasks: - debug: msg="{{var1}} {{var2}}"
上面var_file2.yml使用的是相對路徑,基於playbook所在的路徑。例如該playbook爲/tmp/x.yml,則var_file2.yml也應該在/tmp下。固然,徹底可使用絕對路徑。
5.5 roles中的變量
因爲role是整合playbook的,它有默認的文件組織結構。其中有一個目錄vars,其內部的main.yml用於定義變量。還有defaults目錄內的main.yml則是定義role默認變量的,默認變量的優先級最低。
[root@ss-server ~]# tree /yaml /yaml ├── roles │ └── nginx │ ├── defaults │ └── main.yml │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars │ └── main.yml └── site.yml
main.yml中變量定義方式也是字典格式,例如:
--- mysql_port: 3306
5.6 命令行傳遞變量
ansible和ansible-playbook命令的"-e"選項均可以傳遞變量,傳遞的方式有兩種:-e key=value 和 -e @var_file。注意:當key=value方式傳遞變量時,若是變量中包含特殊字符,必須防止其被shell解析。例如:
ansible localhost -m shell -a "echo {{kevin_bo}}" -e 'kevin_bo="good work"' ansible localhost -m shell -a "echo {{kevin_bo}}" -e @/tmp/var_file1.yml
其中/tmp/var_file1.yml中的內容以下:
--- kevin_bo: good work
5.7 藉助with_items疊加變量
ansible中能夠藉助with_items實現列表迭代的功能,做用於變量註冊的行爲上,就能夠實現將多個結果賦值給同一個變量。例以下面的playbook中,給出了3個item列表,並在shell模塊中經過固定變量"{{item}}"分別迭代,第一次迭代的是anhui,第二次迭代的是beijing,第三次迭代的是shanghai,也就實現了3次循環。最後,將結果註冊爲變量kevin_bo。
--- - hosts: localhost remote_user: root tasks: - name: test shell: echo "{{item}}" with_items: - anhui - beijing - shanghai register: kevin_bo - debug: var=kevin_bo.results[0].stdout - debug: var=kevin_bo.results[1].stdout - debug: var=kevin_bo.results[2].stdout
每次迭代的過程當中,調用item的模塊都會將結果保存在一個key爲results的數組中。所以,引用迭代後註冊的變量時,須要在變量名中加上results,並指定數組名。例如上面的kevin_bo.results[N].stdout。此外,還可使用for循環遍歷列表。例如:
- debug: msg="{% for i in kevin_bo.results %} {{i.stdout}} {% endfor %}"
其實,看一下kevin_bo的輸出就很容易理解了。如下是kevin_bo的第一個列表的輸出。
"kevin_bo": { "changed": true, "msg": "All items completed", "results": [ { "_ansible_item_result": true, "_ansible_no_log": false, "_ansible_parsed": true, "changed": true, "cmd": "echo \"anhui\"", "delta": "0:00:00.001942", "end": "2019-05-12 04:45:57.032946", "invocation": { "module_args": { "_raw_params": "echo \"anhui\"", "_uses_shell": true, "chdir": null, "creates": null, "executable": null, "removes": null, "warn": true } }, "item": "anhui", "rc": 0, "start": "2019-05-12 04:45:57.031004", "stderr": "", "stderr_lines": [], "stdout": "anhui", "stdout_lines": [ "anhui" ] }
5.8 inventory中主機變量和主機組變量
在inventory文件中能夠爲主機和主機組定義變量,不只包括內置變量賦值,還包括自定義變量賦值。例如如下inventory文件(/root/ansible/test.yml)。
172.16.60.20 ansible_ssh_port=22 var1=1 #變量優先級別排第二 [webserver] #變量優先級別排第一 172.16.60.18 172.16.60.19 172.16.60.20 var1=2 [webserver:vars] #變量優先級別排第三 var1=2.2 var2=3 [all:vars] #變量優先級別排第四 var2=4
其中ansible_ssh_port是主機內置變量,爲其賦值22,這類變量是內置類變量。此外還在多處爲主機172.16.60.20進行了賦值。其中[webserver:vars]和[all:vars]表示爲主機組賦值,前者是爲webserver這個組賦值,後者是爲全部組賦值。如下是執行語句:
[root@ss-server ~]# ansible 172.16.60.20 -i /root/ansible/test.yml -m shell -a 'echo "{{var1}} {{var2}} {{ansible_ssh_port}}"' 172.16.60.20 | SUCCESS | rc=0 >> 2 3 22
從結果可知:主機組裏面的主機變量優先級高於無主機組的主機變量,設定的主機組變量優先級高於all特殊組。除了在inventory文件中定義主機、主機組變量,還能夠將其定義在host_vars和group_vars目錄下的獨立的文件中,但要求這些host_vars或group_vars這兩個目錄和inventory文件或playbook文件在同一個目錄下,且變量的文件以對應的主機名或主機組名命名。例如,inventory文件路徑爲/etc/ansible/hosts,playbook文件路徑爲/root/ansible/play.yml,則主機172.16.60.20和主機組webserver的變量文件路徑能夠爲如下幾種:
/etc/ansible/host_vars/172.16.60.20 /etc/ansible/group_vars/webserver /root/ansible/host_vars/172.16.60.20 /root/ansible/group_vars/webserver
以下是一個host_vars目錄下的文件內容:
[root@ss-server ~]# cat /root/ansible/host_vars/172.16.60.20 var1: 1.1 var2: 2.2 var3: 3.3 var4: 4.4
如下爲/root/ansible/play.yml的內容
--- - hosts: 172.16.60.20 tasks: - debug: msg='{{var1}} {{var2}} {{var3}} {{var4}}'
執行結果以下:
TASK [debug] ********************************************** ok: [172.16.60.20] => { "msg": "1.1 2.2 3.3 4.4" }
5.9 內置變量
ansible除了inventory中內置的一堆不可被引用的設置類變量,還有幾個全局均可以引用的內置變量,主要有如下幾個:inventory_hostname、inventory_hostname_short、groups、group_names、hostvars、play_hosts、inventory_dir和ansible_version。
1)inventory_hostname 和 inventory_hostname_short
分別表明的是inventory中被控節點的主機名和主機名的第一部分,若是定義的是主機別名,則變量的值也是別名。例如inventory中webserver主機組定義爲以下:
[webserver] 172.16.60.20 web01 ansible_ssh_host=172.16.60.21 www.kevin.com ansible_ssh_host=172.16.60.22
分別輸出它們的inventory_hostname和inventory_hostname_short。
[root@ss-server ~]# ansible webserver -m debug -a 'msg="{{inventory_hostname}} & {{inventory_hostname_short}}"' 172.16.60.20 | SUCCESS => { "msg": "172.16.60.20 & 172" } web01 | SUCCESS => { "msg": "web01 & web01" } www.kevin.com | SUCCESS => { "msg": "www.kevin.com & www" }
2)groups 和 group_names
group_names返回的是主機所屬主機組,若是該主機在多個組中,則返回多個組,若是它不在組中,則返回ungrouped這個特殊組。例如,某個inventory文件以下:
172.16.60.20 172.16.60.21 172.16.60.22 172.16.60.23 [webserver] 172.16.60.20 [dbserver] 172.16.60.21 db01 ansible_ssh_host=172.16.60.22 www.kevin.com ansible_ssh_host=172.16.60.23 [server:children] webserver dbserver
其中172.16.60.20定義在webserver和server中,因此返回這兩個組。同理172.16.60.21返回dbserver和server。172.16.60.22和172.16.60.23則返回ungrouped,雖然它們在dbserver中都定義了別名,但至少將172.16.60.22和172.16.60.23做爲主機名時,它們是不在任何主機中的。另外一方面,db01和www.kevin.com這兩個別名主機都返回dbserver和server兩個組。groups變量則是返回其所在inventory文件中全部組和其內主機名。注意:該變量對每一個控制節點都返回一次,因此返回的內容可能很是多。例如,上面的inventory中,若是指定被控節點爲dbserver,則會重複返回3次(由於有3臺被控主機)該inventory文件。其中的第三臺主機www.kevin.com的返回結果爲:
www.kevin.com | SUCCESS => { "msg": { "all": [ "172.16.60.20", "172.16.60.21", "172.16.60.22", "172.16.60.23", "db01", "www.kevin.com" ], "server": [ "172.16.60.20", "172.16.60.21", "db01", "www.kevin.com" ], "webserver": [ "172.16.60.20" ], "dbserver": [ "172.16.60.21", "db01", "www.kevin.com" ], "ungrouped": [ "172.16.60.20", "172.16.60.21", "172.16.60.22", "172.16.60.23" ] } }
3)hostvars
該變量用於引用其餘主機上收集的facts中的數據,或者引用其餘主機的主機變量、主機組變量。其key爲主機名或主機組名。舉個例子,假如使用ansible部署一臺nginx服務器web01,且配置文件內須要指向另外一臺數據庫服務器web02的ip地址ip2,能夠直接在配置文件中指定ip2,但也能夠在模板配置文件中直接引用host2收集的facts數據中的ansible_eth0.ipv4.address變量。例如,dbserver主機組中包含了172.16.60.[21:23]共3臺主機。playbook內容以下:
--- - hosts: dbserver tasks: - debug: msg="{{hostvars['172.16.60.21'].ansible_eth0.ipv4.address}}"
執行結果以下:
TASK [debug] ********************************************************* ok: [172.16.60.21] => { "msg": "172.16.60.21" } ok: [172.16.60.22] => { "msg": "172.16.60.21" } ok: [172.16.60.23] => { "msg": "172.16.60.21" }
可是請注意:在引用其餘主機facts中數據時,要求被引用主機進行了facts收集動做,或者有facts緩存。不然都沒收集,固然沒法引用其facts數據。也就是說,當被引用主機沒有facts緩存時,ansible的控制節點中必須同時包含引用主機和被引用主機。除了引用其餘主機的facts數據,還能夠引用其餘主機的主機變量和主機組變量,且不要求被引用主機有facts數據,由於主機變量和主機組變量是在ansible執行任務前加載的。例如,inventory中格式以下:
172.16.60.18 [dbserver] 172.16.60.21 var1=1.1 172.16.60.22 172.16.60.23 [dbserver:vars] var2=2.2
playbook內容以下:
--- - hosts: 172.16.60.18 tasks: - debug: msg="{{hostvars['172.16.60.21'].var1}} & {{hostvars['172.16.60.23'].var2}}"
執行結果以下:
TASK [debug] *************************************** ok: [172.16.60.18] => { "msg": "1.1 & 2.2" }
4)play_hosts 和 inventory_dir
play_hosts表明的是當前play所涉及inventory內的全部主機名列表。inventory_dir是所使用inventory所在的目錄。例如,inventory內容爲:
172.16.60.18 [webserver] 172.16.60.21 172.16.60.22 [dbserver] 172.16.60.24 172.16.60.25
那麼,該inventory內的任意一或多臺主機做爲ansible或ansible-playbook的被控節點時,都會返回整個inventory內的全部主機名稱。
5)ansible_version
表明的是ansible軟件的版本號。變量返回的內容以下:
{ "full": "2.3.1.0", "major": 2, "minor": 3, "revision": 1, "string": "2.3.1.0" }
##### ansibl變量的優先級 #####
1. extra vars變量(在命令行中使用 -e);優先級最高
2. 在inventory中定義的鏈接變量(好比ansible_ssh_user);優先級第二
3. 大多數的其餘變量(命令行轉換,play中的變量,include的變量,role的變量等);優先級第三
4. 在inventory定義的其餘變量;優先級第四
5. 有系統發現的facts;優先級第五
6. "role默認變量",這個是最默認的值,很容易喪失優先權。優先級最小。
##### inventory清單列表裏定義變量:單個主機定義的變量優先級高於主機組定義的變量 #####
通過實驗,ansible使用inventory定義變量的優先級順序從高到低爲:
1. host_vars下定義變量
2. inventory中單個主機定義變量
3. group_vars下定義變量
4. inventory中組定義變量
總之,ansible提供的變量定義方式真的是太豐富了,這些變量可讓ansible使用起來十分靈活,功能強大!