【51CTO.com快译】Ansible是一种功能很强大的工具,可以跨服务器、云、网络和容器等更多系统自动化管理各种平台。
您常常只需重用现有的角色和集合,就可以自动执行想要完成的操作。还有许多模块可供选择,可以在剧本中使用它们。
但是当您开始开发和测试更复杂的剧本时,最终需要一些故障排查方法。比如检查Ansible任务流、确认变量的数据类型,甚至在某个特定点暂停以检查它们的值。
本文讨论的一些技巧仅适用于通过命令行执行Ansible。其他技巧从Ansible Tower运行时也适用。
1. 你的Ansible环境和参数
如果您需要分析为什么某内容在剧本中没有按预期运行,最好先检查您的Ansible环境。
您在运行Ansible二进制文件和Python的哪些版本和路径?
如果您添加了剧本所需的操作系统包或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(更详细)或更高版本执行作业模板,可显示该信息。
除了Ansible和Python二进制文件的版本和位置之外,仔细检查用于模块的路径总是好事,包括执行是否使用了不是默认的ansible.cfg文件(即不是/etc/ansible /ansible.cfg)。
要调查来自自定义ansible.cfg文件的选项,可以从命令行执行以下操作:
- > 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
顾名思义,这将列出与默认参数不同的参数。
2. 以详细模式运行
下一步是在调试模式下运行剧本,以获取有关任务和变量中所发生情况的更多详细信息。
您可以从命令行添加-v(或-vv、-vvv、-vvvv、-vvvvv)。最高的详细级别有时可能含有太多信息,因此最好在多次执行中逐渐增加详细级别,直至获得所需信息。
排查连接问题时级别4有所帮助,级别5适用于排查WinRM问题。
在Tower中,您可以从作业模板定义中选择VERBOSITY级别。
注意:记得在解决问题后禁用调试模式,因为详细信息仅对排查故障有用。
此外,在调试模式下,除非您在任务中使用no_log选项,否则会显示某些变量(比如密码)的值,因此完成后删除输出。
3.使用调试来显示变量
如果您很清楚问题可能出在哪里,可以使用更精准的方法:仅显示您需要查看的变量。
- (...)
- - 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"
- }
这就是为什么我无法加大计数。过滤器type_debug显示,数据类型是text,而不是我以为的int。
4. 确保变量有合适的内容和数据类型
可以使用assert模块来确认:变量有预期的内容/类型,如果出了岔子,就促使任务失败。
下列剧本表明了这点:
- ---
- - 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
看到数据类型显示为str让我很惊讶,但这里给出了解释和解决方法(https://github.com/ansible/ansible/issues/14179)。
另外,如果您从Tower运行同一个剧本,将参数作为额外变量或调查字段来传递,参数的数据类型就会是int。
是的,这有点复杂,但如果您知道找什么,这些“功能”对您来说不成问题。
5. 列出事实和变量
无论您在库存中定义了变量,还是在剧本执行期间创建和赋予了额外变量,它在某个时候对于检查值很有用:
- ---
- - 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. 使用任务调试器从命令行排查
您还可以使用Ansible调试器,在逐步模式下执行剧本,并以交互方式检查变量和参数的内容。
此外,还可以实时改变变量的值,并继续执行。
可以在任务或剧本层面启用调试器,比如在以下示例中:
- ---
- - 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)) }}"
事先声明一下:我将变量定义为字符串,我试图执行计算时这显然会引发错误。
- > 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
这里发生了什么:
1. 起初,任务失败,抱怨是非int变量。
2. 调试器被调用。
3. 我使用了 print (p) 命令来显示变量的值。
4. 在这种情况下,我知道问题出在数据类型上,但有人可能认为值是正确的(如果不注意值两边的引号)。
5. 后来,我更新了变量的内容,为它们赋予了数字。
6. 然后,我使用redo命令用新值重新执行任务,结果成功完成。
这是简单的场景,因为我们知道没人会真正使用Ansible来计算圆的面积。但在较复杂的情况下,在长时间的剧本执行过程中找到变量的内容可能很有用,无需从头开始就能在此后继续下去。
原文标题:6 troubleshooting skills for Ansible playbooks,作者:Roberto Nozaki
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】