Ansible 故障排查与调试技巧

Ansible 故障排查与调试技巧

故障排查基础

理解 Ansible 执行流程

# Ansible 执行流程
1. 加载配置(ansible.cfg)
2. 加载 Inventory
3. 加载 Playbook
4. 收集 Facts(如果启用)
5. 应用变量和模板
6. 执行任务
7. 调用 Handlers(如果需要)
8. 输出结果

调试模式

增加详细输出

# -v: 基本详细信息
ansible-playbook site.yml -v

# -vv: 更详细信息
ansible-playbook site.yml -vv

# -vvv: 包含 SSH 连接详细信息
ansible-playbook site.yml -vvv

# -vvvv: 最详细信息,包含模块参数和返回值
ansible-playbook site.yml -vvvv

使用 check mode(干运行)

# 检查模式:只显示会发生的变更,不实际执行
ansible-playbook site.yml --check

# 配合 diff 使用:显示变更的详细差异
ansible-playbook site.yml --check --diff

# 示例输出:
# --- before: /etc/nginx/nginx.conf (content)
# +++ after: /etc/nginx/nginx.conf (content)
# @@ -1,5 +1,5 @@
#  worker_processes 4;
# -worker_connections 1024;
# +worker_connections 2048;

限制执行范围

# 只在特定主机上执行
ansible-playbook site.yml --limit web1.example.com

# 使用模式匹配
ansible-playbook site.yml --limit "web*.example.com"

# 使用组
ansible-playbook site.yml --limit webservers

# 多个目标
ansible-playbook site.yml --limit "web1,db1"

debug 模块

输出变量

---
- name: 调试变量
  hosts: all
  tasks:
    - name: 显示主机名
      debug:
        msg: "主机名: {{ inventory_hostname }}"

    - name: 显示所有 facts
      debug:
        var: ansible_facts

    - name: 显示特定 fact
      debug:
        var: ansible_default_ipv4

    - name: 显示注册的变量
      shell: uname -a
      register: kernel_info

    - name: 显示命令输出
      debug:
        msg: "{{ kernel_info.stdout }}"
        verbosity: 1

条件调试

- name: 条件调试
  debug:
    msg: "这是生产环境,请谨慎操作"
  when: deployment_env == "production"

- name: 根据详细级别输出
  debug:
    msg: "详细的调试信息"
  when: ansible_verbosity > 0

常见问题诊断

连接问题

# 问题:SSH 连接失败
# 解决方案1:检查 SSH 连接
ansible all -m ping -vvv

# 解决方案2:使用密码认证
ansible-playbook site.yml -k

# 解决方案3:指定 SSH 密钥
ansible-playbook site.yml --private-key ~/.ssh/mykey

# 解决方案4:跳过主机密钥检查
ansible-playbook site.yml -e "ansible_ssh_common_args='-o StrictHostKeyChecking=no'"

# 解决方案5:检查防火墙
ansible all -m raw -a "ufw status" -u root

权限问题

# 问题:权限不足
# 解决方案1:使用 become
- name: 需要 root 权限的任务
  become: yes
  apt:
    name: nginx
    state: present

# 解决方案2:指定 become 用户
- name: 以特定用户身份执行
  become: yes
  become_user: postgres
  postgresql_user:
    name: myapp

# 解决方案3:提供 sudo 密码
ansible-playbook site.yml -K

# 解决方案4:检查 sudo 配置
ansible all -m command -a "sudo -l" --become

Python 环境问题

# 问题:Python 版本不匹配或未安装
# 解决方案1:指定 Python 解释器
ansible-playbook site.yml -e "ansible_python_interpreter=/usr/bin/python3"

# 解决方案2:在 Inventory 中指定
[webservers]
web1.example.com ansible_python_interpreter=/usr/bin/python3

# 解决方案3:使用 ansible_python_interpreter 变量
[all:vars]
ansible_python_interpreter=auto_legacy_silent

# 解决方案4:安装 Python
ansible all -m raw -a "apt-get install -y python3" --become

Facts 收集问题

# 问题:Facts 收集失败或耗时太长
# 解决方案1:禁用 Facts 收集
- name: 不需要 Facts 的 Play
  gather_facts: no
  tasks:
    - name: 简单任务
      debug:
        msg: "不需要 Facts"

# 解决方案2:只收集特定 Facts
- name: 收集特定 Facts
  gather_facts: yes
  gather_subset:
    - network
    - virtual
    - hardware

# 解决方案3:使用 Facts 缓存
# ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400

模块相关故障

模块未找到

# 问题:模块未找到
# 解决方案1:检查模块路径
ansible-doc -l | grep module_name

# 解决方案2:使用完整模块路径
- name: 使用完整路径
      mynamespace.mycollection.my_module:

# 解决方案3:安装 collections
ansible-galaxy collection install community.general

模块参数错误

# 问题:模块参数错误
# 解决方案1:查看模块文档
ansible-doc module_name

# 解决方案2:使用 validate 参数
- name: 配置前验证
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    validate: 'nginx -t -c %s'

# 解决方案3:使用 check mode 测试
ansible-playbook site.yml --check

变量问题

变量未定义

# 问题:变量未定义导致错误
# 解决方案1:使用 default 过滤器
- name: 使用默认值
  debug:
    msg: "值: {{ my_var | default('default_value') }}"

# 解决方案2:使用 Jinja2 的 default
{{ my_var is defined | ternary(my_var, 'default') }}

# 解决方案3:检查变量是否定义
- name: 检查变量
  debug:
    msg: "变量未定义"
  when: my_var is undefined

# 解决方案4:设置必需变量
- name: 确保必需变量存在
  assert:
    that:
      - my_var is defined
      - my_var | length > 0
    fail_msg: "必需变量 my_var 未定义"
    success_msg: "变量 my_var 已定义"

变量优先级问题

# 问题:变量优先级导致意外值
# 解决方案1:显示变量来源
ansible-playbook site.yml -vvv

# 解决方案2:使用 debug 显示所有同名变量
- name: 显示所有可能的变量
  debug:
    msg:
      - "Play vars: {{ play_vars }}"
      - "Host vars: {{ hostvars[inventory_hostname] }}"

# 解决方案3:使用 {{ hostvars }} 查看所有变量
- name: 显示主机所有变量
  debug:
    var: hostvars[inventory_hostname]

性能问题

执行缓慢

# 问题:Playbook 执行缓慢
# 解决方案1:增加并发数
# ansible.cfg
[defaults]
forks = 50

# 解决方案2:禁用 Facts 收集
- name: 快速执行
  gather_facts: no

# 解决方案3:启用 pipelining
# ansible.cfg
[ssh_connection]
pipelining = True

# 解决方案4:使用 SSH 管道复用
# ansible.cfg
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

# 解决方案5:使用 async 异步执行
- name: 异步执行长任务
  command: /usr/local/bin/long_task.sh
  async: 3600
  poll: 0

日志分析

启用日志记录

# ansible.cfg
[defaults]
log_path = /var/log/ansible.log

# 查看日志
tail -f /var/log/ansible.log
grep "ERROR" /var/log/ansible.log
grep "FAILED" /var/log/ansible.log

分析输出

# 使用 jq 分析 JSON 输出
ansible all -m setup -a "filter=ansible_*" --json | jq '.ansible_facts'

# 过滤失败任务
ansible-playbook site.yml 2>&1 | grep -A 5 "FAILED"

# 统计变更
ansible-playbook site.yml 2>&1 | grep "changed" | wc -l

使用 ansible-playbook

step 模式

# 逐步执行,每个任务前确认
ansible-playbook site.yml --step

从特定任务开始

# 从特定任务开始
ansible-playbook site.yml --start-at-task "安装 Nginx"

只执行特定 tags

# 只执行特定标签的任务
ansible-playbook site.yml --tags "nginx"

# 跳过特定标签
ansible-playbook site.yml --skip-tags "database"

常用调试工具

ansible-vault

# 查看 Vault 文件
ansible-vault view secrets.yml

# 编辑 Vault 文件
ansible-vault edit secrets.yml

# 重新加密
ansible-vault rekey secrets.yml

ansible-console

# 进入交互式控制台
ansible-console

# 常用命令
> webservers            # 选择主机组
> yum name=nginx state=present  # 执行任务
> cd web1.example.com          # 切换主机
> gather_facts                 # 收集 Facts
> setup                          # 显示 Facts
> shell uname -r               # 执行命令

ansible-inventory

# 显示 Inventory
ansible-inventory -i inventory.ini --list

# 以 YAML 格式显示
ansible-inventory -i inventory.ini --list --yaml

# 显示主机变量
ansible-inventory -i inventory.ini --host web1.example.com

# 验证 Inventory
ansible-inventory -i inventory.ini --graph

网络问题排查

测试连接

# 测试基本连接
ansible all -m ping

# 测试 SSH 连接
ansible all -m command -a "echo 'SSH connection OK'"

# 测试 Python 环境
ansible all -m command -a "python3 --version"

# 测试提权
ansible all -m command -a "whoami" --become

Playbook 语法检查

语法验证

# 检查 Playbook 语法
ansible-playbook site.yml --syntax-check

# 检查角色
ansible-playbook site.yml --syntax-check --list-tasks

# 显示 Playbook 信息
ansible-playbook site.yml --list-tasks
ansible-playbook site.yml --list-hosts
ansible-playbook site.yml --list-tags

真实故障排查案例

案例1:部署失败

# 现象:部署 Playbook 执行失败,输出不明确
# 诊断步骤:

# 1. 使用详细输出
ansible-playbook deploy.yml -vvv

# 2. 使用 check mode
ansible-playbook deploy.yml --check --diff

# 3. 单独测试失败的任务
ansible all -m template -a "src=templates/config.j2 dest=/etc/config"

# 4. 验证模板语法
- name: 验证 Jinja2 语法
  template:
    src: config.j2
    dest: /tmp/test_config
  check_mode: yes

# 5. 检查变量
- name: 显示变量
  debug:
    var: hostvars[inventory_hostname]

# 常见原因:
# - 模板语法错误
# - 变量未定义
# - 文件权限问题
# - 磁盘空间不足

案例2:循环错误

# 现象:循环任务执行失败
# 诊断步骤:

# 1. 显示循环变量
- name: 显示循环项
  debug:
    msg: "{{ item }}"
  loop: "{{ my_list }}"

# 2. 检查列表内容
- name: 检查列表
  set_fact:
    list_content: "{{ my_list }}"
  when: my_list is defined

# 3. 添加错误处理
- name: 循环任务
  command: /usr/local/bin/process.sh {{ item }}
  loop: "{{ my_list }}"
  ignore_errors: yes
  register: loop_results

- name: 显示失败的任务
  debug:
    msg: "失败: {{ item }}"
  loop: "{{ loop_results.results }}"
  when: item.failed

# 常见原因:
# - 列表为空
# - 列表包含无效项
# - 某个项导致命令失败

案例3:条件判断错误

# 现象:条件判断不按预期工作
# 诊断步骤:

# 1. 显示条件值
- name: 显示条件变量
  debug:
    msg: "{{ condition_var }}"

# 2. 测试条件
- name: 测试条件
  debug:
    msg: "条件为真"
  when: condition_var

# 3. 使用 assert
- name: 验证条件
  assert:
    that:
      - condition_var is defined
      - condition_var | bool
    fail_msg: "条件不满足"
    success_msg: "条件满足"

# 常见原因:
# - 变量类型错误(字符串 vs 布尔)
# - 变量未定义
# - 条件逻辑错误

最佳实践

  1. 逐步验证: 使用 --check 和 --diff 预览变更
  2. 详细日志: 使用 -vvv 获取详细信息
  3. 小步前进: 分步验证,逐步构建 Playbook
  4. 错误处理: 添加适当的错误处理和回滚机制
  5. 文档记录: 记录常见问题和解决方案
  6. 版本控制: 使用 Git 管理变更
  7. 测试环境: 先在测试环境验证
  8. 日志分析: 定期分析日志找出潜在问题

总结

通过本教程,你已经了解了 Ansible 故障排查的方法:

  • 调试模式和详细输出
  • 常见连接、权限、Python 环境问题
  • 模块和变量问题诊断
  • 性能优化技巧
  • 日志分析和使用工具
  • 真实故障排查案例
  • 故障排查最佳实践

发表回复

后才能评论