ansible筆記(37):include(二)
- A+
所屬分類:ansible ofollow,noindex" target="_blank">運維技術
在本部落格中,ansible是一個系列文章,我們會盡量以通俗易懂的方式總結ansible的相關知識點。
ansible系列博文直達連結:ansible輕鬆入門系列
"ansible系列"中的每篇文章都建立在前文的基礎之上,所以, 請按照順序閱讀這些文章,否則有可能在閱讀中遇到障礙。
在上一篇文章中我們總結了"include"的使用方法,而且我們提到,"include"的某些原始用法在之後的版本中可能會被棄用,在之後的版本中,會使用一些新的關鍵字代替這些原始用法,那麼在這篇文章中,我們就來介紹一下這些與"include"有關的新的關鍵字。
include_tasks
先來介紹一下include_tasks模組,在理解了include以後,再來理解include_tasks,簡直不要太輕鬆,我們知道,include模組可以用來包含一個任務列表,include_tasks模組的作用也是用來包含一個任務列表,在之後的版本中,如果我們想要包含一個任務列表,那麼就可以使用"include_tasks"關鍵字代替"include"關鍵字,示例如下:
# cat intest.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: "test task1" - include_tasks: in.yml - debug: msg: "test task2" # cat in.yml - debug: msg: "task1 in in.yml" - debug: msg: "task2 in in.yml"
如上例所示,當我們需要包含一個任務列表時,"include_tasks"關鍵字的用法與"include"完全相同,那麼我們執行一下上例的playbook,看看執行效果如何
如上圖所示,當我們使用"include_tasks"時,"include_tasks"本身會被當做一個"task",這個"task"會把被include的檔案的路徑輸出在控制檯中,這就是"include_tasks模組"與"include模組"之間的區別,如果將上例中的"include_tasks"關鍵字替換成"include",控制檯中則不會顯示上圖中標註的任務資訊,由此可見"include"是透明的,"include_tasks"是可見的,"include_tasks"更像是一個任務,這個任務包含了其他的一些任務。
在ansible的2.7版本中,"include_tasks"模組中加入了新的引數,由於我當前使用的版本為2.6.2,所以此處先將ansible升級至2.7.0版本(當前使用yum源能夠更新的最新版本為2.7.0),以便了解"include_tasks"模組的新引數,在之後的文章中,如果沒有特殊說明,則使用的是2.7.0版本的ansible進行演示。
從2.7版本開始,"include_tasks"模組加入了file引數和apply引數,先來聊聊file引數,file引數可以用來指定要包含的任務列表檔案,語法如下:
- include_tasks: file: in.yml
上例表示包含in.yml檔案,in.yml檔案中的任務將被引用,你可能會有疑問,上例的寫法和如下寫法的效果相同嗎?
- include_tasks: in.yml
沒錯,這兩種寫法的最終效果是完全相同的,只不過一個使用了"file"引數的方式,另一個使用了"free_form"的方式(free_form的意思前文中已經總結過,此處不再贅述),雖然語法上不同,但是本質上沒有區別。
在前一篇文章中我們演示過,如果為include新增tags,那麼tags是對include檔案中的所有任務生效的,也就是說,如果呼叫include對應的tag,那麼include檔案中的所有任務都會執行,如果對"include_tasks"新增tags,tags是不是也會對include_tags中的所有任務生效呢?我們來試試,示例如下
# cat intest.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: "test task1" - include_tasks: file: in.yml tags: t1 - debug: msg: "test task2"
如上例所示,我們為"include_tasks"任務添加了標籤t1,那麼,我們在執行此playbook時,指定使用t1標籤,執行後輸出如下
# ansible-playbook intest.yml --tags t1 PLAY [test70] ******************************************* TASK [include_tasks] ************************************* included: /testdir/ansible/in.yml for test70 PLAY RECAP ******************************************** test70 : ok=1 changed=0 unreachable=0 failed=0
從執行後的輸出資訊可以看出,當我們指定t1標籤後,"include_tasks"這個任務本身被呼叫了,而"include_tasks"對應檔案中的任務卻沒有被呼叫,所以我們可以得出結論,在使用tags時,"include_tasks"與"include"並不相同,標籤只會對"include_tasks"任務本身生效,而不會對其中包含的任務生效。
如果我們想要tags對"include_tasks"中包含的所有任務都生效,該怎麼實現呢?這時,我們就需要使用到"include_tasks"模組的apply引數了,示例如下:
--- - hosts: test70 remote_user: root gather_facts: no tasks: - include_tasks: file: in.yml apply: tags: - t1
如上例所示,除了使用file引數指定包含的任務列表檔案以外,還使用了apply引數,apply引數呼叫了tags關鍵字,tags關鍵字用於指定一個標籤列表,列表中的標籤將會被應用到in.yml檔案中的所有任務上,換句話說就是,apply引數可以指定哪些標籤會被應用到被包含的任務上,聰明如你,一定看懂了,但是,按照上述語法,就能夠正常的將t1標籤應用到in.yml中的所有任務嗎?不如我們來實際測試一下,看看與我們預想的結果是否一致,上例playbook執行後結果如下
# ansible-playbook intest.yml --tags t1 PLAY [test70] ******************************************* PLAY RECAP ********************************************
如你所見,當我們執行上例playbook以後,並沒有如我們預想的一樣呼叫in.yml中的任務,就連"include_tasks"任務自身也沒有被呼叫,我們該怎麼辦呢?如果想讓上例playbook按照我們所想的那樣執行,則需要使用如下方法:
--- - hosts: test70 remote_user: root gather_facts: no tasks: - include_tasks: file: in.yml apply: tags: - t1 tags: always
如上例所示,在使用"include_tasks"時,不僅使用apply引數指定了tags,同時還使用tags關鍵字,對"include_tasks"本身添加了always標籤(前文中已經總結過always標籤的含義,如果你忘記了可以回顧前文),按照上例的語法,才能夠按照我們所想的那樣,將t1標籤應用到in.yml中的所有任務上,效果如下:
# ansible-playbook intest.yml --tags t1 PLAY [test70] ******************************************* TASK [include_tasks] ************************************* included: /testdir/ansible/in.yml for test70 TASK [debug] ******************************************* ok: [test70] => { "msg": "task1 in in.yml" } TASK [debug] ******************************************* ok: [test70] => { "msg": "task2 in in.yml" } PLAY RECAP ******************************************* test70 : ok=3 changed=0 unreachable=0 failed=0
從上述執行結果可以看出,當我們呼叫t1標籤時,in.yml中的所有任務都執行了,但是你可能會有疑問,如果對"include_tasks"自身添加了always標籤,那麼我們呼叫其他標籤時,in.yml檔案中的任務也會"always"執行嗎?我們來測試一下,playbook如下:
# cat intest.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: "test task" tags: t0 - include_tasks: file: in.yml apply: tags: - t1 tags: always
執行上例playbook時,我們只調用t0標籤,看看t1標籤對應的in.yml中的任務會不會"always"執行,測試結果如下:
# ansible-playbook intest.yml --tags t0 PLAY [test70] ******************************************* TASK [debug] ******************************************* ok: [test70] => { "msg": "test task" } TASK [include_tasks] ************************************* included: /testdir/ansible/in.yml for test70 PLAY RECAP ******************************************** test70 : ok=2 changed=0 unreachable=0 failed=0
從上述測試結果可以看出,in.yml中的任務並未"always"執行,而"include_tasks"任務本身卻"always"執行了,所以,我們可以得出結論,上例中的always標籤只是針對"include_tasks"任務自身而言的,之所以為"include_tasks"任務新增always標籤,就是為了讓apply引數中的t1標籤能夠針對包含的所有任務生效,always標籤並不會對被包含的所有任務生效,如果想要被包含的任務也都"always"執行,該怎麼辦呢?你一定已經想到了,沒錯,在apply引數的tags列表中新增always標籤即可,示例如下:
--- - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: "test task" tags: t0 - include_tasks: file: in.yml apply: tags: t1,always tags: always
綜上所述就是,apply引數中的tags用於給in.yml中的任務統一打標籤,"include_tasks"對應的tags用於給"include_tasks"任務自身打標籤,同時,如果想要apply引數中的tags能夠生效,"include_tasks"的標籤中必須包含always標籤,是不是覺得有點"繞",多動手試試就不繞了。
除了使用引數的方式能夠指定tags,使用"free_form"的方式也可以指定tags,示例如下:
--- - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: "test task" tags: t0 - include_tasks: in.yml args: apply: tags: t1 tags: always
我覺得我已經說清楚了,你肯定也已經明白了。
import_tasks
剛才已經瞭解了"include_tasks"的用法,現在聊聊另一個與它很像的模組:"import_tasks",如果想要包含引用一個任務列表,也可以使用"import_tasks"關鍵字,示例如下:
# cat intest1.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: "test task" - import_tasks: in.yml # cat in.yml - debug: msg: "task1 in in.yml" - debug: msg: "task2 in in.yml"
如上例所示,"import_tasks"模組使用了free-form的方式引用了in.yml檔案,到目前的版本為止,"import_tasks"模組只能使用free-form的方式引用任務列表檔案,沒有其他方式可以使用,那麼我們來執行一下上例playbook,執行後輸出如下:
# ansible-playbook intest1.yml PLAY [test70] ******************************************* TASK [debug] ******************************************* ok: [test70] => { "msg": "test task" } TASK [debug] ******************************************* ok: [test70] => { "msg": "task1 in in.yml" } TASK [debug] ******************************************* ok: [test70] => { "msg": "task2 in in.yml" } PLAY RECAP ******************************************** test70 : ok=3 changed=0 unreachable=0 failed=0
從輸出的結果可以看出,"import_tasks"模組並不會像"include_tasks"模組那樣,在控制檯中輸出相關的任務資訊,"import_tasks"是相對透明的。
除了上述不同,"import_tasks"和"include_tasks"到底有什麼不同之處呢?它們的不同之處在於,"import_tasks"是靜態的,"include_tasks"是動態的。
那麼動態和靜態又是什麼意思呢,它們有什麼不同呢?動態和靜態的主要區別在於被include的檔案的載入時機不同。
"靜態"的意思就是被include的檔案在playbook被載入時就展開了(是預處理的)。
"動態"的意思就是被include的檔案在playbook執行時才會被展開(是實時處理的)。
由於"include_tasks"是動態的,所以,被include的檔案的檔名可以使用任何變數替換。
由於"import_tasks"是靜態的,所以,被include的檔案的檔名不能使用動態的變數替換。
這樣說可能不容易理解,不如來看一個小示例,有時候,我們想要使用變數替換被包含檔案的檔名,示例如下:
# cat intest3.yml --- - hosts: test70 remote_user: root gather_facts: no vars: file_name: in.yml tasks: - import_tasks: "{{file_name}}" - include_tasks: "{{file_name}}"
如上例所示,被包含的檔名並沒有寫死,而是使用變數替換成了對應的檔名,執行上例playbook,是完全可以正常執行的。
但是,如果我們不使用vars關鍵字定義變數,而是使用set_fact的方式定義變數,能不能正常執行呢?我們來試試,示例如下:
# cat intest3.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - set_fact: file_name: in.yml - import_tasks: "{{file_name}}" - include_tasks: "{{file_name}}"
如上例所示,我們使用了set_fact的方式定義了file_name變數,嘗試執行上例playbook,會發現如下報錯:
# ansible-playbook intest3.yml ERROR! Error when evaluating variable in import path: {{file_name}}. When using static imports, ensure that any variables used in their names are defined in vars/vars_files or extra-vars passed in from the command line. Static imports cannot use variables from facts or inventory sources like group or host vars.
從報錯資訊可以看出,當使用靜態的import時,請確保檔名中使用到的變數被定義在vars中、vars_files中、或者extra-vars中,靜態的import不支援其他方式傳入的變數。
除了上述不同之處,在使用"迴圈操作"和"條件判斷"時,"include_tasks"和"import_tasks"也有很多不同點需要注意,注意點如下。
如果想要對包含的任務列表進行迴圈操作,則只能使用"include_tasks"關鍵字,不能使用"import_tasks"關鍵字,"import_tasks"並不支援迴圈操作,
也就是說,使用"loop"關鍵字或"with_items"關鍵字對include檔案進行迴圈操作時,只能配合"include_tasks"才能正常執行。
我們知道,當使用when關鍵字對include檔案添加了條件判斷時,只有條件滿足後,include檔案中的任務列表才會被執行,其實,when關鍵字對"include_tasks"和"import_tasks"的實際操作有著本質區別,區別如下:
當對"include_tasks"使用when進行條件判斷時,when對應的條件只會應用於"include_tasks"任務本身,當執行被包含的任務時,不會對這些被包含的任務重新進行條件判斷。
當對"import_tasks"使用when進行條件判斷時,when對應的條件會應用於被include的檔案中的每一個任務,當執行被包含的任務時,會對每一個被包含的任務進行同樣的條件判斷。
這樣說不容易理解,來看一個小示例,就容易理解多了,示例playbook如下:
# cat intest4.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - name: '----------set testvar to 0' set_fact: testnum: 0 - debug: msg: '-----include_tasks-----enter the in1.yml-----' - include_tasks: in1.yml when: testnum == 0 - name: '----------set testvar to 0' set_fact: testnum: 0 - debug: msg: '-----import_tasks-----enter the in1.yml-----' - import_tasks: in1.yml when: testnum == 0 # cat in1.yml - set_fact: testnum: 1 - debug: msg: "task1 in in1.yml"
如上例 所示,我們分別使用"include_tasks"和"import_tasks"兩個關鍵字包含了in1.yml檔案,並且,二者都有相同的條件判斷,即當testnum變數的值為0時,才會呼叫in1.yml中的任務,無論是在測試"include_tasks"還是測試"import_tasks"之前,都會使用set_fact將testnum的值設定為0,以便條件成立後執行in.yml中的任務,在in.yml中 ,又使用set_fact將testnum的值設定為了1,為什麼這樣做呢?咱們稍後解釋,同時,在in1.yml中輸出了一條測試資訊,那麼你預想一下,如果執行上例的intest4.yml劇本,"task1 in in1.yml"這條測試資訊會輸出幾次呢?請先在你的大腦裡預演一遍上例的playbook,然後再檢視實際的執行效果 ,實際的執行效果如下:
PLAY [test70] ********************************************* TASK [----------set testvar to 0] ****************************** ok: [test70] TASK [debug] ********************************************* ok: [test70] => { "msg": "-----include_tasks-----enter the in1.yml-----" } TASK [include_tasks] *************************************** included: /testdir/ansible/in1.yml for test70 TASK [set_fact] ******************************************** ok: [test70] TASK [debug] ********************************************** ok: [test70] => { "msg": "task1 in in1.yml" } TASK [----------set testvar to 0] ******************************* ok: [test70] TASK [debug] ********************************************** ok: [test70] => { "msg": "-----import_tasks-----enter the in1.yml-----" } TASK [set_fact] ********************************************* ok: [test70] TASK [debug] ********************************************** skipping: [test70] PLAY RECAP *********************************************** test70 : ok=8 changed=0 unreachable=0 failed=0
從上述實際執行結果中可以看出,"task1 in in1.yml"這條測試資訊只輸出了一遍,這可能與我們預想的效果不太一樣,那麼上例具體是怎樣執行的呢?我們來捋一遍,之前粗略的解釋過,當對"include_tasks"使用when進行條件判斷時,when對應的條件只會應用於"include_tasks"任務本身,所以,當testnum的值為0時,條件成立,in1.yml中的任務被執行,於是,debug模組輸出了 "task1 in in1.yml",如我們所想,一切正常,然後繼續執行之後的任務,我們重新將testnum的值設定為了0,所以"import_tasks"的條件" testnum == 0"也同樣成立了 ,於是,開始執行in1.yml檔案中的任務,之前粗略的介紹過,when對應的條件會應用於被include的檔案中的每一個任務,當執行被包含的任務時,會對每一個被包含的任務進行同樣的條件判斷,所以,當執行in1.yml檔案中的第一個任務(set_fact任務)時,此任務會再次對" testnum == 0"條件進行判斷,如果"testnum == 0",則執行set_fact任務,此時,條件完全成立,於是,執行set_fact任務,將testnum的值設定為1,當執行到debug任務時,同樣會對debug任務進行" testnum == 0"條件判斷,如果條件成立,則執行debug任務,但是,由於上一個任務已經把testnum的值設定為1,所以此時條件不成立,於是,debug任務並沒有被執行,這就是上例playbook執行的整個過程,你肯定已經明白了,由此可見,"when"對於"include_tasks"和"import_tasks"的操作在本質上存在差別。
說完了上述注意點,我們再來聊聊"tags"與"handlers"。
與"include_tasks"不同,當為"import_tasks"新增標籤時,tags是針對被包含檔案中的所有任務生效的,與"include"關鍵字的效果相同。
"include_tasks"與"import_tasks"都可以在handlers中使用,並沒有什麼不同,不過在當前2.7.0版本中,如果在handlers中使用"import_tasks"引用任務列表,會出現bug,bug的具體表現可以參考如下連結:
https://github.com/ansible/ansible/issues/47392
import_playbook
在上篇文章中我們還提到,使用"include"關鍵字除了能夠引用任務列表,還能夠引用整個playbook,在之後的版本中,如果想要引入整個playbook,則需要使用"import_playbook"模組代替"include"模組,因為在2.8版本以後,使用"include"關鍵字引用整個playbook的特性將會被棄用。
"import_playbook"的示例如下:
# cat intest6.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: "test task in intest6.yml" - import_playbook: intest7.yml # cat intest7.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - debug: msg: "test task in intest7.yml"
如上例所示, "import_playbook"的用法與"include"的用法並沒有什麼區別,此處不再贅述。
這篇文章中我們總結了一些新的模組,通過這些模組可以替代"include"模組的一些功能特性,文章就先總結到這裡吧,希望能夠對你有所幫助~