Ansible劇本的6個排查技巧
譯文【51CTO.com快譯】Ansible是一種功能很強大的工具,可以跨服務器、云、網(wǎng)絡(luò)和容器等更多系統(tǒng)自動化管理各種平臺。
您常常只需重用現(xiàn)有的角色和集合,就可以自動執(zhí)行想要完成的操作。還有許多模塊可供選擇,可以在劇本中使用它們。
但是當您開始開發(fā)和測試更復雜的劇本時,最終需要一些故障排查方法。比如檢查Ansible任務流、確認變量的數(shù)據(jù)類型,甚至在某個特定點暫停以檢查它們的值。
本文討論的一些技巧僅適用于通過命令行執(zhí)行Ansible。其他技巧從Ansible Tower運行時也適用。
1. 你的Ansible環(huán)境和參數(shù)
如果您需要分析為什么某內(nèi)容在劇本中沒有按預期運行,最好先檢查您的Ansible環(huán)境。
您在運行Ansible二進制文件和Python的哪些版本和路徑?
如果您添加了劇本所需的操作系統(tǒng)包或Python模塊,Ansible解釋器會“看到”它們嗎?
可以通過多種不同的方式獲取這些基本信息。從命令行運行ansible --version命令。
- > ansible --version
- ansible 2.9.10
- config file = /etc/ansible/ansible.cfg
- configured module search path = ['/home/admin/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
- ansible python module location = /usr/lib/python3.6/site-packages/ansible
- executable location = /usr/bin/ansible
- python version = 3.6.8 (default, Mar 18 2021, 08:58:41) [GCC 8.4.1 20200928 (Red Hat 8.4.1-1)]
可以通過運行其他Ansible命令來獲取同樣的信息,比如使用--version選項的ansible-playbook或ansible-config。
在Ansible Tower中,如果使用VERBOSITY 2(更詳細)或更高版本執(zhí)行作業(yè)模板,可顯示該信息。
除了Ansible和Python二進制文件的版本和位置之外,仔細檢查用于模塊的路徑總是好事,包括執(zhí)行是否使用了不是默認的ansible.cfg文件(即不是/etc/ansible /ansible.cfg)。
要調(diào)查來自自定義ansible.cfg文件的選項,可以從命令行執(zhí)行以下操作:
- > ansible-config dump --only-changed
- DEFAULT_BECOME(/home/admin/ansible/ansible.cfg) = True
- DEFAULT_BECOME_USER(/home/admin/ansible/ansible.cfg) = root
- DEFAULT_FORKS(/home/admin/ansible/ansible.cfg) = 10
- DEFAULT_HOST_LIST(/home/admin/ansible/ansible.cfg) = ['/home/admin/ansible/inventory']
- DEFAULT_ROLES_PATH(/home/admin/ansible/ansible.cfg) = ['/home/admin/ansible/roles']
- HOST_KEY_CHECKING(/home/admin/ansible/ansible.cfg) = False
顧名思義,這將列出與默認參數(shù)不同的參數(shù)。
2. 以詳細模式運行
下一步是在調(diào)試模式下運行劇本,以獲取有關(guān)任務和變量中所發(fā)生情況的更多詳細信息。
您可以從命令行添加-v(或-vv、-vvv、-vvvv、-vvvvv)。最高的詳細級別有時可能含有太多信息,因此最好在多次執(zhí)行中逐漸增加詳細級別,直至獲得所需信息。
排查連接問題時級別4有所幫助,級別5適用于排查WinRM問題。
在Tower中,您可以從作業(yè)模板定義中選擇VERBOSITY級別。
注意:記得在解決問題后禁用調(diào)試模式,因為詳細信息僅對排查故障有用。
此外,在調(diào)試模式下,除非您在任務中使用no_log選項,否則會顯示某些變量(比如密碼)的值,因此完成后刪除輸出。
3.使用調(diào)試來顯示變量
如果您很清楚問題可能出在哪里,可以使用更精準的方法:僅顯示您需要查看的變量。
- (...)
- - name: Display the value of the counter
- debug:
- msg: "Counter={{ counter }} / Data type={{ counter | type_debug }}"
- (...)
- TASK [Display the value of the counter]
- ***************************************************************************
- ok: [node1] => {
- "msg": "Counter=42 / Data type=AnsibleUnicode"
- }
這就是為什么我無法加大計數(shù)。過濾器type_debug顯示,數(shù)據(jù)類型是text,而不是我以為的int。
4. 確保變量有合適的內(nèi)容和數(shù)據(jù)類型
可以使用assert模塊來確認:變量有預期的內(nèi)容/類型,如果出了岔子,就促使任務失敗。
下列劇本表明了這點:
- ---
- - name: Assert examples
- hosts: node1
- gather_facts: no
- vars:
- var01: 13
- tasks:
- - debug:
- msg: "Parameter 01 is {{ (param01 | type_debug) }}"
- - name: Make sure that we have the right type of content before proceeding
- assert:
- that:
- - "var01 is defined"
- - "(var01 | type_debug) == 'int' "
- - "param01 is defined "
- - "(param01 | type_debug) == 'int' "
如果我從命令行運行劇本,不提供額外變量:
- > ansible-playbook xassert.yml
- PLAY [Assert examples]
- ***************************************************************************
- TASK [debug]
- ***************************************************************************
- ok: [node1] => {
- "msg": "Parameter 01 is AnsibleUndefined"
- }
- TASK [Make sure that we have the right type of content before proceeding] ***************************************************************************
- fatal: [node1]: FAILED! => {
- "assertion": "param01 is defined ",
- "changed": false,
- "evaluated_to": false,
- "msg": "Assertion failed"
- }
- PLAY RECAP
- ***************************************************************************
- node1 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
如果我從命令行運行劇本,使用額外變量:
- > ansible-playbook xassert.yml -e "param01=99"
- PLAY [Assert examples]
- ***************************************************************************
- TASK [debug]
- ***************************************************************************
- ok: [node1] => {
- "msg": "Parameter 01 is str"
- }
- TASK [Make sure that we have the right type of content before proceeding] ***************************************************************************
- fatal: [node1]: FAILED! => {
- "assertion": "(param01 | type_debug) == 'int' ",
- "changed": false,
- "evaluated_to": false,
- "msg": "Assertion failed"
- }
- PLAY RECAP
- ***************************************************************************node1 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
看到數(shù)據(jù)類型顯示為str讓我很驚訝,但這里給出了解釋和解決方法(https://github.com/ansible/ansible/issues/14179)。
另外,如果您從Tower運行同一個劇本,將參數(shù)作為額外變量或調(diào)查字段來傳遞,參數(shù)的數(shù)據(jù)類型就會是int。
是的,這有點復雜,但如果您知道找什么,這些“功能”對您來說不成問題。
5. 列出事實和變量
無論您在庫存中定義了變量,還是在劇本執(zhí)行期間創(chuàng)建和賦予了額外變量,它在某個時候?qū)τ跈z查值很有用:
- ---
- - name: vars
- hosts: node1,node2
- tasks:
- - name: Dump vars
- copy:
- content: "{{ hostvars[inventory_hostname] | to_nice_yaml }}"
- dest: "/tmp/{{ inventory_hostname }}_vars.txt"
- delegate_to: control
- - name: Dump facts
- copy:
- content: "{{ ansible_facts | to_nice_yaml }}"
- dest: "/tmp/{{ inventory_hostname }}_facts.txt"
- delegate_to: control
這將把變量和事實 (如果您在搜集事實)保存在單獨的文件中,供您分析。
6. 使用任務調(diào)試器從命令行排查
您還可以使用Ansible調(diào)試器,在逐步模式下執(zhí)行劇本,并以交互方式檢查變量和參數(shù)的內(nèi)容。
此外,還可以實時改變變量的值,并繼續(xù)執(zhí)行。
可以在任務或劇本層面啟用調(diào)試器,比如在以下示例中:
- ---
- - name: Example using debugger
- hosts: localhost
- gather_facts: no
- vars:
- radius: "5.3"
- pi: "3.1415926535"
- debugger: on_failed
- tasks:
- - name: Calculate the area of a circle
- debug:
- msg:
- - "Radius.............: {{ radius }}"
- - "pi................: {{ pi }}"
- - "Area of the circle: {{ (pi * (radius * radius)) }}"
事先聲明一下:我將變量定義為字符串,我試圖執(zhí)行計算時這顯然會引發(fā)錯誤。
- > ansible-playbook xdebugger.yml
- PLAY [Example using debugger]
- ***************************************************************************
- TASK [Calculate the area of a circle]
- ***************************************************************************
- fatal: [localhost]: FAILED! => {"msg": "Unexpected templating type error occurred on (Area of the circle: {{ (pi * (radius * radius)) }}): can't multiply sequence by non-int of type 'AnsibleUnicode'"}
- [localhost] TASK: Calculate the area of a circle (debug)> p task_vars['pi']
- '3.1415926535'
- [localhost] TASK: Calculate the area of a circle (debug)> p task_vars['radius']
- '5.3'
- [localhost] TASK: Calculate the area of a circle (debug)> task_vars['pi']=3.1415926535
- [localhost] TASK: Calculate the area of a circle (debug)> task_vars['radius']=5.3
- [localhost] TASK: Calculate the area of a circle (debug)> p task_vars['radius']
- 5.3
- [localhost] TASK: Calculate the area of a circle (debug)> task_vars['pi']=3.1415926535
- [localhost] TASK: Calculate the area of a circle (debug)> redo
- ok: [localhost] => {
- "msg": [
- "Radius............: 5.3",
- "pi................: 3.1415926535",
- "Area of the circle: 88.247337636815"
- ]
- }
- PLAY RECAP
- ***************************************************************************
- localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
這里發(fā)生了什么:
1. 起初,任務失敗,抱怨是非int變量。
2. 調(diào)試器被調(diào)用。
3. 我使用了 print (p) 命令來顯示變量的值。
4. 在這種情況下,我知道問題出在數(shù)據(jù)類型上,但有人可能認為值是正確的(如果不注意值兩邊的引號)。
5. 后來,我更新了變量的內(nèi)容,為它們賦予了數(shù)字。
6. 然后,我使用redo命令用新值重新執(zhí)行任務,結(jié)果成功完成。
這是簡單的場景,因為我們知道沒人會真正使用Ansible來計算圓的面積。但在較復雜的情況下,在長時間的劇本執(zhí)行過程中找到變量的內(nèi)容可能很有用,無需從頭開始就能在此后繼續(xù)下去。
原文標題:6 troubleshooting skills for Ansible playbooks,作者:Roberto Nozaki
【51CTO譯稿,合作站點轉(zhuǎn)載請注明原文譯者和出處為51CTO.com】