第十五章:故障排查
掌握 Ansible 常见问题的诊断和解决方法。
最后更新: 2024-01-29
页面目录
Ansible 故障排查
本章节介绍 Ansible 常见问题的诊断方法和解决方案。
常用诊断工具
语法检查
# 检查 Playbook 语法
ansible-playbook site.yml --syntax-check
# 检查 YAML 语法
yamllint playbook.yml
# Python 语法检查
python3 -m py_compile my_module.py
列出主机和任务
# 列出所有主机
ansible-playbook site.yml --list-hosts
# 列出所有任务
ansible-playbook site.yml --list-tasks
# 列出所有标签
ansible-playbook site.yml --list-tags
检查模式
# 模拟执行,不实际修改
ansible-playbook site.yml --check
# 检查并显示差异
ansible-playbook site.yml --check --diff
逐步执行
# 逐步确认每个任务
ansible-playbook site.yml --step
连接问题
SSH 连接失败
症状
UNREACHABLE! Failed to connect to the host via ssh:
ssh: connect to host 192.168.1.10 port 22: Connection refused
排查步骤
# 1. 确认主机可达
ping 192.168.1.10
# 2. 检查 SSH 端口
nc -zv 192.168.1.10 22
telnet 192.168.1.10 22
# 3. 测试 SSH 连接
ssh -v user@192.168.1.10
# 4. 检查 SSH 密钥权限
ls -la ~/.ssh/
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 700 ~/.ssh
解决方案
# ansible.cfg
[ssh_connection]
ssh_args = -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
pipelining = True
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
SSH 认证失败
症状
FAILED! => {"msg": "FAILED: Authentication failure."}
排查步骤
# 1. 检查 SSH 密钥
ls -la ~/.ssh/
# 2. 复制公钥
ssh-copy-id user@192.168.1.10
# 3. 手动复制公钥
cat ~/.ssh/id_rsa.pub | ssh user@192.168.1.10 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# 4. 测试 SSH 连接
ssh -i ~/.ssh/id_rsa user@192.168.1.10
Inventory 配置
# 使用密钥
[webservers]
web1 ansible_host=192.168.1.10 ansible_user=root ansible_ssh_private_key_file=/root/.ssh/id_rsa
# 使用密码
web2 ansible_host=192.168.1.11 ansible_user=root ansible_ssh_pass=password
# 或使用 ask-pass
ansible-playbook site.yml --ask-pass
权限问题
症状
msg": "This command has to be run under the root or superduer user."
"msg": "Missing sudo password"
排查步骤
# 检查 sudo 配置
sudo -l
# 测试 sudo
ssh user@host "sudo whoami"
解决方案
# Playbook 配置
---
- name: Execute with privilege
hosts: all
become: yes
become_method: sudo
become_user: root
become_flags: '-i' # 使用 login shell
# 或运行时指定
ansible-playbook site.yml --become --ask-become-pass
模块执行问题
模块未找到
症状
ERROR! couldn't resolve module/action 'some_module'.
排查步骤
# 检查 ansible-doc
ansible-doc -l | grep module_name
# 检查模块路径
ansible-config dump | grep DEFAULT_MODULE_PATH
解决方案
# ansible.cfg
[defaults]
library = ./library:/usr/share/ansible/library
模块执行失败
症状
failed: [host] (item={'name': 'app'}) => {"msg": "MODULE FAILURE"}
排查步骤
# 使用 verbose 模式
ansible-playbook site.yml -vvv
# 单独测试模块
ansible localhost -m module_name -a "arg1=value1"
# 查看模块文档
ansible-doc module_name
常见模块问题
# apt 模块
# 问题:apt cache 需要更新
tasks:
- name: Install package
apt:
name: nginx
update_cache: yes
# copy vs template
# copy:复制静态文件
# template:使用变量生成文件
# command vs shell
# command:简单命令
# shell:需要管道等 shell 特性
Playbook 问题
变量未定义
症状
fatal: [host]: FAILED! => {"msg": "The task includes an option with an undefined variable"}
排查步骤
# 检查变量定义
- name: Debug variable
debug:
var: my_variable
# 检查 Facts
- name: Show facts
debug:
var: ansible_facts
解决方案
# 使用默认值
app_port: "{{ my_variable | default(8080) }}"
# 条件检查
when: my_variable is defined
# 检查 registered 变量
- name: Check command
command: /opt/app/status.sh
register: result
- name: Use result
debug:
msg: "{{ result.stdout }}"
条件判断不执行
症状
TASK skipped
排查步骤
# 检查条件变量
ansible all -m setup -a "filter=ansible_distribution"
正确写法
# ✅ 正确
when: ansible_os_family == "RedHat"
# ❌ 错误
when: ansible_os_family = "RedHat"
# ✅ 字符串比较
when: ansible_facts['distribution'] == "Ubuntu"
# ✅ 检查列表
when: inventory_hostname in groups['webservers']
# ✅ 检查组
when: "'webservers' in group_names"
循环不工作
症状
循环任务只执行一次
排查步骤
# 检查循环语法
tasks:
- name: Install packages
apt:
name: "{{ item }}"
loop:
- nginx
- vim
常见错误
# ❌ 错误:引号问题
loop: "{{ ['nginx', 'vim'] }}"
# 应该是:
loop:
- nginx
- vim
# ✅ 正确
loop: "{{ packages_list }}"
YAML 语法问题
常见 YAML 错误
# ❌ Tab vs 空格
---
- name: Example
tasks: # Tab 错误
- name: Task
# ✅ 正确
---
- name: Example
tasks:
- name: Task
# ❌ 缩进不一致
tasks:
- name: Task1
- name: Task2
# ❌ 列表格式错误
items:
- item1
- item2
验证 YAML
# Python 验证
python3 -c "import yaml; yaml.safe_load(open('playbook.yml'))"
#yamllint
pip install yamllint
yamllint playbook.yml
Facts 问题
Facts 未收集
# 检查 Facts
- name: Show facts
debug:
var: ansible_facts
# 禁用 Facts 收集
- name: Fast playbook
hosts: all
gather_facts: no
# 选择性收集
- name: Selective facts
hosts: all
gather_facts:
- ansible_distribution
- ansible_memory_mb
Facts 变量名变化
# Ansible 2.x
{{ ansible_distribution }}
# Ansible 2.9+ 兼容
{{ ansible_facts['distribution'] }}
Handler 问题
Handler 未执行
# 检查点1:状态是否真正改变
tasks:
- name: Update config
copy:
src: app.conf
dest: /etc/myapp/app.conf
# 需要 changed 才会通知
notify: Restart app
# 检查点2:notify 名称匹配
handlers:
- name: Restart app # 必须匹配
service:
name: myapp
state: restarted
# 检查点3:强制执行 Handler
- name: Ensure handlers run
hosts: all
force_handlers: yes
Handler 执行顺序
Handlers 按定义顺序执行,不是按 notify 顺序。
Vault 问题
忘记 Vault 密码
无法恢复,需要使用备份密码。
Vault 解密失败
# 检查密码文件
cat ~/.vault_pass
# 检查权限
chmod 600 ~/.vault_pass
# 使用正确密码
ansible-playbook site.yml --ask-vault-pass
多个 Vault
# 指定 Vault ID
ansible-playbook site.yml --vault-id dev@prompt --vault-id prod@prompt
性能问题
执行慢
# 1. 减少并发
ansible-playbook site.yml -f 5
# 2. 禁用 Facts 收集
ansible-playbook site.yml --tags never --tags facts
# 3. 缓存 Facts
- name: Cache facts
hosts: all
gather_facts: yes
cacheable: yes
# 4. 使用 Pipelining
# ansible.cfg
[connection]
pipelining = True
批量主机超时
# 增加超时时间
ansible all -m ping --timeout=60
# ansible.cfg
[defaults]
timeout = 60
Debug 技巧
使用 debug 模块
tasks:
- name: Show variable
debug:
var: my_variable
verbosity: 2
- name: Show message
debug:
msg: "Debug at this point: {{ my_variable }}"
使用 assert
tasks:
- name: Verify configuration
assert:
that:
- app_port | int >= 1
- app_port | int <= 65535
- db_host is defined
fail_msg: "Invalid configuration"
success_msg: "Configuration valid"
注册变量检查
tasks:
- name: Run command
command: /opt/app/status.sh
register: result
- name: Show result
debug:
msg: "{{ result }}"
- name: Show stdout
debug:
msg: "{{ result.stdout }}"
- name: Show rc
debug:
msg: "Exit code: {{ result.rc }}"
常见错误码
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| 0 | 成功 | - |
| 1 | 失败 | 检查任务输出 |
| 2 | 连接失败 | 检查 SSH 配置 |
| 4 | 语法错误 | 检查 YAML 语法 |
| 5 | 选项错误 | 检查模块参数 |
日志分析
启用详细日志
# ansible.cfg
[defaults]
log_path = /var/log/ansible.log
查看系统日志
# Linux
journalctl -u ansible
# 查看 SSH 日志
cat /var/log/auth.log | grep sshd
获取帮助
官方文档
- Ansible 官方文档:https://docs.ansible.com/
- Ansible Galaxy:https://galaxy.ansible.com/
社区支持
- GitHub Issues:https://github.com/ansible/ansible/issues
- Ansible Forum:https://forum.ansible.com/
- IRC: #ansible on Libera.Chat
调试命令
# 查看配置
ansible-config dump
# 查看版本
ansible --version
# 查看模块帮助
ansible-doc -l | head -20
ansible-doc <module_name>
# 查看所有变量
ansible-inventory -i inventory --list --yaml
下一步
恭喜你完成了 Ansible 权威教程的所有章节!
👉 返回目录