第十五章:故障排查

掌握 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 权威教程的所有章节!

👉 返回目录