Ansible 故障排除与测试策略
Ansible 代码和其他代码一样,可能会存在问题和漏洞。尽管 Ansible 在任务执行前会检查任务语法,以确保尽可能安全,但这种检查只能避免少数类型的错误,如任务参数不正确,而无法防范其他类型的错误。由于 Ansible 代码描述的是期望状态,而非获取该状态的具体步骤,所以系统不太容易出现逻辑错误。然而,Playbook 中的一个小错误可能会导致所有机器出现潜在的配置错误,尤其是在更改系统的关键部分(如 SSH 守护进程或 sudo 配置)时,甚至可能会导致无法访问系统。因此,我们需要采取一系列措施来预防或减轻 Ansible playbook 中的错误。
1. 进一步阅读资源
在处理容器和云管理时,有许多不同云平台的 Ansible 模块可供使用,以下是一些常见云平台模块的详细链接:
| 云平台 | 模块链接 |
| ---- | ---- |
| AWS | https://docs.ansible.com/ansible/latest/modules/list_of_cloud_modules.html#amazon |
| Azure | https://docs.ansible.com/ansible/latest/modules/list_of_cloud_modules.html#azure |
| Docker | https://docs.ansible.com/ansible/latest/modules/list_of_cloud_modules.html#docker |
| GCP | https://docs.ansible.com/ansible/latest/modules/list_of_cloud_modules.html#google |
| OpenStack | https://docs.ansible.com/ansible/latest/modules/list_of_cloud_modules.html#openstack |
| Rackspace | https://docs.ansible.com/ansible/latest/modules/list_of_cloud_modules.html#rackspace |
2. 技术要求
在进行后续的故障排除和测试之前,需要确保控制主机已安装 Ansible,并且使用的是最新版本。本文中的示例是在 Ansible 2.9 版本上进行测试的。虽然文中会给出具体的主机名示例,但你可以根据自己的需求替换为主机名或 IP 地址。示例代码可以在 GitHub 仓库 中找到。
3. 深入探究 Playbook 执行问题
有时候,Ansible 执行会中断,这可能由多种原因导致。其中,网络问题是执行 Ansible playbook 时最常见的问题之一。由于发出命令的机器和执行命令的机器通常通过网络连接,网络问题会直接导致 Ansible 执行出现问题。
另外,对于某些模块(如 shell 或 command),即使执行成功,返回码也可能非零。在这种情况下,可以在模块中使用 ignore_errors: yes 来忽略错误。例如,运行 /bin/false 命令时,它始终返回 1。为了避免在 playbook 中阻塞执行,可以这样编写:
- name: Run a command that will return 1
command: /bin/false
ignore_errors: yes
不过,这只是特殊情况,通常最好的做法是修复应用程序,使其遵循 UNIX 标准,在应用程序正常运行时返回 0,而不是在 Playbook 中采用变通方法。
4. 使用主机事实诊断故障
有些执行失败是由目标机器的状态引起的,最常见的问题是 Ansible 期望某个文件或变量存在,但实际上却不存在。在这种情况下,打印机器的事实信息可能有助于找到问题所在。
我们可以创建一个名为 print_facts.yaml 的简单 playbook,内容如下:
---
- hosts: target_host
tasks:
- name: Display all variables/facts known for a host
debug:
var: hostvars[inventory_hostname]
通过运行这个 playbook,可以获取目标机器在 Ansible 执行期间的大量状态信息。
5. 使用 Playbook 进行测试
在 IT 领域,调试软件和系统往往比创建它们更具挑战性,Ansible 也不例外。无论你创建 Ansible playbook 的能力有多强,迟早都会遇到需要调试的 playbook。
执行基本测试的最简单方法是在执行期间打印变量的值。以下是使用 Ansible 实现这一目的的步骤:
1. 创建一个名为 debug.yaml 的 playbook,内容如下:
---
- hosts: localhost
tasks:
- shell: /usr/bin/uptime
register: result
- debug:
var: result
- 使用以下命令运行 playbook:
$ ansible-playbook debug.yaml
运行后,会得到类似以下的输出:
PLAY [localhost] ***********************************************************************
***********
TASK [Gathering Facts] ***********************************************************************
*****
ok: [localhost]
TASK [shell] ***********************************************************************
***************
changed: [localhost]
TASK [debug] ***********************************************************************
***************
ok: [localhost] => {
"result": {
"changed": true,
"cmd": "/usr/bin/uptime",
"delta": "0:00:00.003461",
"end": "2019-06-16 11:30:51.087322",
"failed": false,
"rc": 0,
"start": "2019-06-16 11:30:51.083861",
"stderr": "",
"stderr_lines": [],
"stdout": " 11:30:51 up 40 min, 1 user, load average: 1.11, 0.73, 0.53",
"stdout_lines": [
" 11:30:51 up 40 min, 1 user, load average: 1.11, 0.73, 0.53"
]
}
}
PLAY RECAP ***********************************************************************
*****************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0
ignored=0
在这个示例中,第一个任务使用 shell 模块执行 uptime 命令,并将输出保存到 result 变量中。第二个任务使用 debug 模块打印 result 变量的内容。
debug 模块还提供了 verbosity 选项。例如,将 playbook 修改如下:
---
- hosts: localhost
tasks:
- shell: /usr/bin/uptime
register: result
- debug:
var: result
verbosity: 2
默认情况下,Ansible 的详细级别为 0,因此直接运行这个 playbook 时, debug 步骤将被跳过。要查看 debug 模块的输出,需要在命令行中指定详细级别:
$ ansible-playbook debug2.yaml -vv
通过在命令行中添加两个 -v 选项,我们将以详细级别 2 运行 Ansible,这将影响所有设置了不同调试级别的模块。
6. 使用检查模式
即使对自己编写的代码有信心,在生产环境中实际运行之前进行测试仍然是明智的选择。检查模式就是为此而设计的,它可以在不实际更改机器状态的情况下运行代码,只突出显示当前状态与 playbook 中声明状态之间的差异。
以下是使用检查模式的步骤:
1. 创建一个名为 check-mode.yaml 的 playbook,内容如下:
---
- hosts: localhost
tasks:
- name: Touch a file
file:
path: /tmp/myfile
state: touch
- 使用
--check选项运行 playbook:
$ ansible-playbook check-mode.yaml --check
运行后,输出看起来就像实际执行了操作一样,但实际上 /tmp 目录下并不会创建 myfile 文件。
并非所有模块都支持检查模式,但大多数主要模块都支持,并且随着版本的更新,支持的模块越来越多。需要注意的是, command 和 shell 模块不支持检查模式,因为这些模块无法确定命令是否会导致更改,所以在非检查模式下运行时,它们总是会返回 changed 。
与检查模式类似的是 --diff 标志,它可以跟踪 Ansible 执行期间的具体更改。例如,运行以下命令:
$ ansible-playbook check-mode.yaml --diff
输出将显示类似以下的差异信息:
PLAY [localhost] ***********************************************************************
*******
TASK [Gathering Facts] ***********************************************************************
*
ok: [localhost]
TASK [Touch a file] ***********************************************************************
****
--- before
+++ after
@@ -1,6 +1,6 @@
{
- "atime": 1560693571.3594637,
- "mtime": 1560693571.3594637,
+ "atime": 1560693571.3620908,
+ "mtime": 1560693571.3620908,
"path": "/tmp/myfile",
- "state": "absent"
+ "state": "touch"
}
changed: [localhost]
PLAY RECAP ***********************************************************************
*************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0
ignored=0
从输出中可以看出,文件状态从 absent 变为 touch ,表示文件被创建,同时 mtime 和 atime 也发生了变化。
7. 解决主机连接问题
Ansible 常用于管理远程主机或系统,因此需要能够连接到远程主机才能执行命令。有时,会遇到 Ansible 无法连接到远程主机的问题,例如尝试管理尚未启动的机器。快速识别并解决这些问题可以节省大量时间。
以下是解决主机连接问题的步骤:
1. 创建一个名为 remote.yaml 的 playbook,内容如下:
---
- hosts: all
tasks:
- name: Touch a file
file:
path: /tmp/myfile
state: touch
- 尝试针对一个不存在的 FQDN 运行
remote.yamlplaybook:
$ ansible-playbook -i host.example.com, remote.yaml
如果遇到无法连接的问题,输出会明确提示 SSH 服务未及时响应,例如:
PLAY [all] ***********************************************************************
*************
TASK [Gathering Facts] ***********************************************************************
*
fatal: [host.example.com]: UNREACHABLE! => {"changed": false, "msg":
"Failed to connect to the host via ssh: ssh: Could not resolve hostname
host.example.com: Name or service not known", "unreachable": true}
PLAY RECAP ***********************************************************************
*************
host.example.com : ok=0 changed=0 unreachable=1 failed=0 skipped=0
rescued=0 ignored=0
也可能会遇到另一种错误,例如:
PLAY [all] ***********************************************************************
*************
TASK [Gathering Facts] ***********************************************************************
*
fatal: [host.example.com]: UNREACHABLE! => {"changed": false, "msg":
"Failed to connect to the host via ssh: fale@host.example.com: Permission
denied (publickey,gssapi-keyex,gssapi-with-mic).", "unreachable": true}
PLAY RECAP ***********************************************************************
*************
host.example.com : ok=0 changed=0 unreachable=1 failed=0 skipped=0
rescued=0 ignored=0
这种情况下,主机有响应,但没有足够的权限进行 SSH 连接。
SSH 连接失败通常有两个原因:
- SSH 客户端无法与 SSH 服务器建立连接。
- SSH 服务器拒绝 SSH 客户端提供的凭据。
由于 OpenSSH 的高稳定性和向后兼容性,当出现第一个问题时,很可能是 IP 地址或端口错误,导致 TCP 连接不可行。通常,仔细检查 IP 和主机名(如果使用 DNS,确保其解析到正确的 IP)可以解决问题。为了进一步调查,可以尝试从同一台机器进行 SSH 连接,例如:
$ ssh host.example.com -vvv
当遇到第二个问题时,调试可能会更复杂,因为它可能由多种原因引起,例如连接到错误的主机或用户名错误。可以使用错误信息中显示的 user@host 地址进行 SSH 连接,例如:
$ ssh fale@host.example.com -vvv
这样可以获取更详细的错误信息,帮助诊断问题。
8. 通过 CLI 传递工作变量
在调试过程中,通过命令行向 playbook 传递变量不仅有助于调试,还能提高代码的可重用性。每次应用程序(包括 Ansible playbook)从第三方(如用户)接收输入时,都应该确保输入值合理,例如检查变量是否已设置且不为空字符串。
以下是通过 CLI 传递工作变量的步骤:
1. 创建一个名为 printvar.yaml 的 playbook,用于打印变量的内容:
---
- hosts: localhost
tasks:
- debug:
var: variable
- 在执行命令中声明变量并运行 playbook:
$ ansible-playbook printvar.yaml --extra-vars='{"variable": "Hello, World!"}'
运行后,会得到类似以下的输出:
PLAY [localhost] ***********************************************************************
*******
TASK [Gathering Facts] ***********************************************************************
*
ok: [localhost]
TASK [debug] ***********************************************************************
***********
ok: [localhost] => {
"variable": "Hello, World!"
}
PLAY RECAP ***********************************************************************
*************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0
ignored=0
Ansible 允许以多种模式和不同优先级设置变量,具体如下:
| 优先级 | 设置方式 |
| ---- | ---- |
| 最低 | 命令行值 |
| | 角色默认值 |
| | 清单文件或脚本组变量 |
| | 清单 group_vars/all |
| | Playbook group_vars/all |
| | 清单 group_vars/ |
| | Playbook group_vars/ |
| | 清单文件或脚本主机变量 |
| | 清单 host_vars/ |
| | Playbook host_vars/ |
| | 主机事实/缓存的 set_facts |
| | Play 变量 |
| | Play 变量提示 |
| | Play 变量文件 |
| | 角色变量(在 role/vars/main.yml 中定义) |
| | 块变量(仅适用于块中的任务) |
| | 任务变量(仅适用于任务) |
| | include_vars |
| | set_facts/注册变量 |
| | 角色(和 include_role)参数 |
| | include 参数 |
| 最高 | 额外变量(–extra-vars) |
9. 限制主机执行
在测试 playbook 时,可能只需要在有限数量的机器上进行测试,例如只在一台机器上测试。
以下是限制主机执行的步骤:
1. 创建一个名为 helloworld.yaml 的 playbook,内容如下:
---
- hosts: all
tasks:
- debug:
msg: "Hello, World!"
- 创建一个包含至少两个主机的清单文件
inventory,例如:
[hosts]
host1.example.com
host2.example.com
host3.example.com
- 使用以下命令以常规方式运行 playbook:
$ ansible-playbook -i inventory helloworld.yaml
运行后,playbook 将在清单中的所有机器上执行。如果只想在 host3.example.com 上运行,可以在命令行中指定:
$ ansible-playbook -i inventory helloworld.yaml --limit=host3.example.com
运行后,输出将只显示在 host3.example.com 上执行的结果:
PLAY [all] ***********************************************************************
*************
TASK [Gathering Facts] ***********************************************************************
*
ok: [host3.example.com]
TASK [debug] ***********************************************************************
***********
ok: [host3.example.com] => {
"msg": "Hello, World!"
}
PLAY RECAP ***********************************************************************
*************
host3.example.com : ok=2 changed=0 unreachable=0 failed=0 skipped=0
rescued=0 ignored=0
通过以上这些方法和技巧,我们可以更有效地进行 Ansible 代码的故障排除和测试,确保系统的稳定性和可靠性。
Ansible 故障排除与测试策略
10. 清除代码缓存
在某些情况下,Ansible 可能会因为缓存的代码而导致问题。例如,当你更新了某个模块或者 playbook 中的代码,但 Ansible 仍然使用旧的缓存版本,这可能会引发一些意外的错误。清除代码缓存可以确保 Ansible 使用最新的代码进行执行。
Ansible 的代码缓存通常存储在本地的某个目录中,不同的操作系统和配置可能会有所不同。一般来说,可以通过删除缓存目录中的文件来清除缓存。以下是一个通用的步骤示例:
- 确定缓存目录的位置。在大多数情况下,Ansible 的缓存目录位于
/root/.ansible/cp或者~/.ansible/cp(对于普通用户)。你可以通过查看 Ansible 的配置文件或者使用以下命令来确认:
ansible --version | grep "config file"
打开配置文件,查找 fact_caching_connection 或者 local_tmp 等相关配置项,以确定缓存目录的具体位置。
- 删除缓存目录中的文件。使用以下命令删除缓存目录中的所有文件:
rm -rf /root/.ansible/cp/* # 如果你是 root 用户
# 或者
rm -rf ~/.ansible/cp/* # 如果你是普通用户
删除缓存后,再次运行 Ansible playbook 时,它将重新加载最新的代码,避免因为缓存问题导致的错误。
11. 检查语法错误
在编写 Ansible playbook 时,语法错误是常见的问题之一。一个小的语法错误可能会导致整个 playbook 无法正常执行。因此,在运行 playbook 之前,检查语法错误是非常重要的。
Ansible 提供了 ansible-playbook 命令的 --syntax-check 选项来检查 playbook 的语法。以下是使用该选项的步骤:
- 编写一个 playbook,例如
test.yaml:
---
- hosts: all
tasks:
- name: Example task
debug:
msg: "This is an example task."
- 使用
--syntax-check选项检查语法:
ansible-playbook test.yaml --syntax-check
如果 playbook 的语法正确,命令将输出类似以下的信息:
playbook: test.yaml
如果存在语法错误,命令将输出详细的错误信息,帮助你定位和修复问题。例如,如果在 debug 模块中拼写错误,可能会得到如下错误信息:
ERROR! Syntax Error while loading YAML.
did not find expected key
The error appears to be in '/path/to/test.yaml': line 5, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Example task
debu:
^ here
根据错误信息,我们可以很容易地发现 debu 应该是 debug ,从而进行修正。
12. 总结与最佳实践
在处理 Ansible 代码的故障排除和测试时,我们可以总结出以下一些最佳实践:
| 方面 | 最佳实践 |
| ---- | ---- |
| Playbook 执行 | 遇到问题时,首先检查网络连接,确保发出命令的机器和执行命令的机器之间网络正常。对于返回码非零但执行成功的情况,尽量修复应用程序而不是在 Playbook 中使用 ignore_errors 。 |
| 故障诊断 | 利用主机事实诊断故障,通过打印机器的事实信息来查找问题。使用 debug 模块在执行期间打印变量的值,帮助调试。 |
| 测试 | 在生产环境运行之前,使用检查模式和 --diff 标志进行测试,确保代码不会对系统造成意外影响。 |
| 连接问题 | 当遇到主机连接问题时,根据错误信息判断是网络连接问题还是凭据问题,分别进行排查和解决。 |
| 变量管理 | 通过 CLI 传递工作变量,提高代码的可重用性和灵活性。同时,注意变量的优先级设置,确保变量值的准确性。 |
| 执行范围 | 在测试阶段,使用 --limit 选项限制主机的执行范围,避免对所有机器造成影响。 |
| 代码维护 | 定期清除代码缓存,确保使用最新的代码。在编写 playbook 时,使用 --syntax-check 选项检查语法错误,提高代码的质量。 |
13. 故障排除流程图
graph TD;
A[Ansible 执行中断] --> B{是否是网络问题?};
B -- 是 --> C[检查网络连接, 确认 IP 和主机名];
C --> D[尝试 SSH 连接 -vvv 查看详细信息];
B -- 否 --> E{是否是返回码非零问题?};
E -- 是 --> F[使用 ignore_errors 或修复应用程序];
E -- 否 --> G{是否是主机状态问题?};
G -- 是 --> H[使用 print_facts.yaml 打印主机事实];
G -- 否 --> I{是否是语法错误?};
I -- 是 --> J[使用 --syntax-check 检查语法];
I -- 否 --> K{是否是连接问题?};
K -- 是 --> L[根据错误信息排查网络或凭据问题];
K -- 否 --> M{是否需要调试变量?};
M -- 是 --> N[使用 debug.yaml 打印变量值];
M -- 否 --> O{是否需要测试代码?};
O -- 是 --> P[使用检查模式和 --diff 标志];
O -- 否 --> Q{是否需要限制执行范围?};
Q -- 是 --> R[使用 --limit 选项];
Q -- 否 --> S{是否需要清除代码缓存?};
S -- 是 --> T[删除缓存目录文件];
S -- 否 --> U[其他问题, 进一步分析];
通过遵循这些策略和最佳实践,结合流程图进行故障排除和测试,我们可以更高效地处理 Ansible 代码中的各种问题,提高系统的稳定性和可靠性,确保 Ansible 在自动化管理中发挥最大的作用。同时,不断积累经验,根据实际情况灵活运用这些方法,能够更好地应对复杂多变的 IT 环境。
超级会员免费看
131

被折叠的 条评论
为什么被折叠?



