Ansible 最佳实践 #

项目结构 #

推荐的目录结构 #

text
project/
├── ansible.cfg              # Ansible 配置文件
├── README.md                # 项目文档
├── requirements.yml         # 角色依赖
├── inventory/               # 清单目录
│   ├── production/
│   │   ├── hosts
│   │   ├── group_vars/
│   │   │   ├── all.yml
│   │   │   ├── webservers.yml
│   │   │   └── vault/
│   │   │       └── all.yml
│   │   └── host_vars/
│   │       └── web1.yml
│   └── staging/
│       ├── hosts
│       └── group_vars/
│           └── all.yml
├── playbooks/               # Playbook 目录
│   ├── site.yml             # 主 Playbook
│   ├── webservers.yml
│   ├── databases.yml
│   └── maintenance/
│       ├── backup.yml
│       └── update.yml
├── roles/                   # 角色目录
│   ├── common/
│   ├── nginx/
│   ├── mysql/
│   └── app/
├── files/                   # 静态文件
├── templates/               # 模板文件
├── vars/                    # 全局变量
│   └── main.yml
├── library/                 # 自定义模块
├── module_utils/            # 模块工具
├── filter_plugins/          # 过滤器插件
└── tests/                   # 测试文件
    ├── inventory
    └── test.yml

清晰的职责划分 #

text
┌─────────────────────────────────────────────────────────────┐
│                      目录职责划分                            │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  inventory/          存放主机清单和环境变量                   │
│  playbooks/          存放 Playbook 文件                      │
│  roles/              存放可复用的角色                        │
│  files/              存放静态文件                            │
│  templates/          存放 Jinja2 模板                        │
│  vars/               存放全局变量                            │
│  library/            存放自定义模块                          │
│  tests/              存放测试文件                            │
│                                                              │
└─────────────────────────────────────────────────────────────┘

命名规范 #

文件命名 #

text
# Playbook 文件
site.yml                 # 主 Playbook
webservers.yml           # 功能 Playbook
deploy-app.yml           # 操作 Playbook

# 变量文件
all.yml                  # 全局变量
webservers.yml           # 组变量

# 模板文件
nginx.conf.j2            # 配置模板
app.env.j2               # 环境变量模板

# 角色目录
roles/nginx/             # 小写,连字符分隔
roles/mysql-server/

变量命名 #

yaml
# 好的命名
app_name: myapp
nginx_port: 80
db_host: localhost
max_connections: 100

# 不好的命名
n: myapp
p: 80
h: localhost
mc: 100

# 布尔变量使用问号结尾或 is_ 前缀
ssl_enabled: true
is_production: false

# 列表变量使用复数
nginx_vhosts: []
allowed_hosts: []

任务命名 #

yaml
# 好的命名 - 描述行为和结果
- name: Install Nginx web server
  apt:
    name: nginx
    state: present

- name: Ensure Nginx service is running
  service:
    name: nginx
    state: started

# 不好的命名 - 太模糊
- name: Install
  apt:
    name: nginx

- name: Service
  service:
    name: nginx

代码组织 #

使用 Roles 组织代码 #

yaml
# 不推荐 - 所有任务在一个文件
- hosts: all
  tasks:
    - name: Install Nginx
      ...
    - name: Configure Nginx
      ...
    - name: Install MySQL
      ...
    - name: Configure MySQL
      ...

# 推荐 - 使用 Roles
- hosts: webservers
  roles:
    - nginx

- hosts: databases
  roles:
    - mysql

模块化任务文件 #

yaml
# roles/nginx/tasks/main.yml
---
- name: Include install tasks
  include_tasks: install.yml
  tags: install

- name: Include configure tasks
  include_tasks: configure.yml
  tags: configure

- name: Include service tasks
  include_tasks: service.yml
  tags: service

使用标签 #

yaml
tasks:
  - name: Install Nginx
    apt:
      name: nginx
      state: present
    tags:
      - install
      - nginx

  - name: Configure Nginx
    template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    tags:
      - configure
      - nginx

# 执行特定标签
# ansible-playbook site.yml --tags "install"
# ansible-playbook site.yml --tags "configure"

变量管理 #

分离敏感数据 #

yaml
# inventory/group_vars/all.yml (非敏感)
---
app_name: myapp
app_port: 8080
db_host: localhost
db_port: 3306

# inventory/group_vars/vault/all.yml (加密)
---
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"

使用默认值 #

yaml
# roles/nginx/defaults/main.yml
---
nginx_port: 80
nginx_user: www-data
nginx_worker_processes: auto

# 使用默认值
- name: Configure Nginx
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  vars:
    nginx_port: "{{ nginx_port | default(80) }}"

变量优先级 #

text
优先级从低到高:

1. role defaults          (roles/x/defaults/main.yml)
2. inventory group_vars   (inventory/group_vars/*.yml)
3. inventory host_vars    (inventory/host_vars/*.yml)
4. play vars              (vars:)
5. play vars_files        (vars_files:)
6. role vars              (roles/x/vars/main.yml)
7. extra vars             (-e)

幂等性 #

确保幂等性 #

yaml
# 不幂等 - 每次都会创建
- name: Add line to file
  shell: echo "new line" >> /etc/hosts

# 幂等 - 使用 lineinfile
- name: Add line to file
  lineinfile:
    path: /etc/hosts
    line: "192.168.1.100 webserver"
    state: present

# 不幂等 - 每次都重启
- name: Restart service
  service:
    name: nginx
    state: restarted

# 幂等 - 只在配置变更时重启
- name: Configure Nginx
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: Reload Nginx

使用 changed_when #

yaml
- name: Run migration
  command: /app/migrate.sh
  register: migration_result
  changed_when: "'Migration completed' in migration_result.stdout"

- name: Check if restart needed
  command: /app/check_restart.sh
  register: check_result
  changed_when: false
  failed_when: false

错误处理 #

合理的错误处理 #

yaml
tasks:
  - name: Stop application
    service:
      name: app
      state: stopped
    ignore_errors: yes
  
  - name: Update application
    block:
      - name: Pull latest code
        git:
          repo: "{{ app_repo }}"
          dest: "{{ app_dir }}"
      
      - name: Run migrations
        command: "{{ app_dir }}/migrate.sh"
      
      - name: Start application
        service:
          name: app
          state: started
    
    rescue:
      - name: Rollback
        command: "{{ app_dir }}/rollback.sh"
      
      - name: Start application
        service:
          name: app
          state: started
      
      - name: Notify team
        slack:
          token: "{{ slack_token }}"
          msg: "Deployment failed on {{ inventory_hostname }}"
    
    always:
      - name: Clean up
        file:
          path: /tmp/deploy_temp
          state: absent

失败条件 #

yaml
- name: Run health check
  command: /app/health_check.sh
  register: health_result
  failed_when: "'ERROR' in health_result.stderr or health_result.rc != 0"
  changed_when: false

性能优化 #

禁用 Facts 收集 #

yaml
- hosts: all
  gather_facts: no
  tasks:
    - name: Simple task
      ...

使用 Pipelining #

ini
# ansible.cfg
[ssh_connection]
pipelining = True

控制并发 #

yaml
- hosts: all
  serial: 5    # 每次处理 5 台主机
  tasks:
    ...

使用策略 #

yaml
- hosts: all
  strategy: free    # 自由策略,主机独立执行
  tasks:
    ...

异步任务 #

yaml
- name: Long running task
  command: /usr/bin/long_task.sh
  async: 3600
  poll: 0
  register: long_task

- name: Check task status later
  async_status:
    jid: "{{ long_task.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 60
  delay: 60

安全实践 #

不在代码中硬编码密码 #

yaml
# 不安全
- name: Create user
  user:
    name: deploy
    password: "plain_password"

# 安全 - 使用 Vault
- name: Create user
  user:
    name: deploy
    password: "{{ vault_deploy_password }}"

使用最小权限 #

yaml
- name: Start service
  service:
    name: nginx
    state: started
  become: yes
  become_user: root

验证配置 #

yaml
- name: Deploy Nginx config
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    validate: nginx -t -c %s
  notify: Reload Nginx

限制敏感信息输出 #

yaml
- name: Run command with password
  command: mysql -u root -p{{ mysql_root_password }} -e "SHOW DATABASES"
  no_log: true

文档化 #

README 文件 #

markdown
# Project Name

## Description
Brief description of what this project does.

## Requirements
- Ansible 2.9+
- Python 3.6+

## Installation
```bash
ansible-galaxy install -r requirements.yml

Usage #

bash
ansible-playbook -i inventory/production playbooks/site.yml

Variables #

Variable Default Description
app_name myapp Application name
app_port 8080 Application port

Author #

DevOps Team

text

### 角色文档

```yaml
# roles/nginx/README.md
# Ansible Role: Nginx

## Description
Installs and configures Nginx web server.

## Requirements
- Ubuntu 20.04/22.04
- Ansible 2.9+

## Role Variables
Available variables are listed below:

| Variable | Default | Description |
|----------|---------|-------------|
| nginx_port | 80 | Listen port |
| nginx_user | www-data | Nginx user |

## Dependencies
None

## Example Playbook
```yaml
- hosts: webservers
  roles:
    - role: nginx
      nginx_port: 8080

License #

MIT

text

### 内联注释

```yaml
tasks:
  - name: Configure Nginx worker processes
    lineinfile:
      path: /etc/nginx/nginx.conf
      regexp: '^worker_processes'
      line: "worker_processes {{ ansible_facts.processor_vcpus }};"
    # Auto-configure based on CPU cores for optimal performance
    # Reference: https://nginx.org/en/docs/ngx_core_module.html#worker_processes

测试 #

语法检查 #

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

# 检查 YAML 语法
yamllint site.yml

检查模式 #

bash
# 模拟执行
ansible-playbook --check site.yml

# 显示差异
ansible-playbook --check --diff site.yml

使用 Molecule 测试角色 #

bash
# 安装 Molecule
pip install molecule

# 初始化测试
molecule init scenario

# 运行测试
molecule test

CI/CD 集成 #

GitLab CI #

yaml
# .gitlab-ci.yml
stages:
  - lint
  - test
  - deploy

lint:
  stage: lint
  script:
    - ansible-lint playbooks/
    - yamllint playbooks/ roles/

test:
  stage: test
  script:
    - ansible-playbook --syntax-check playbooks/site.yml
    - ansible-playbook --check playbooks/site.yml -i inventory/staging

deploy_staging:
  stage: deploy
  script:
    - ansible-playbook playbooks/site.yml -i inventory/staging
  environment: staging
  only:
    - develop

deploy_production:
  stage: deploy
  script:
    - ansible-playbook playbooks/site.yml -i inventory/production
  environment: production
  only:
    - main
  when: manual

GitHub Actions #

yaml
# .github/workflows/ansible.yml
name: Ansible CI/CD

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run ansible-lint
        uses: ansible/ansible-lint-action@main
  
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Syntax check
        run: ansible-playbook --syntax-check playbooks/site.yml
  
  deploy:
    runs-on: ubuntu-latest
    needs: [lint, test]
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v3
      - name: Deploy to production
        run: ansible-playbook playbooks/site.yml -i inventory/production
        env:
          VAULT_PASSWORD: ${{ secrets.VAULT_PASSWORD }}

总结 #

遵循这些最佳实践可以帮助你:

  1. 编写可维护、可读性强的代码
  2. 提高代码复用性
  3. 确保代码安全
  4. 提升执行效率
  5. 便于团队协作

下一步 #

现在你已经掌握了最佳实践,接下来学习 高级特性 了解 Ansible 的更多高级功能!

最后更新:2026-03-29