Ansible 最佳实践:构建可靠的自动化工作流
Ansible 最佳实践:构建可靠的自动化工作流
项目组织结构
推荐的项目结构
# 推荐的 Ansible 项目结构
project_root/
├── ansible.cfg # Ansible 配置文件
├── inventory/ # Inventory 目录
│ ├── production/ # 生产环境
│ │ ├── hosts.ini # 主机清单
│ │ └── group_vars/ # 组变量
│ ├── staging/ # 预发布环境
│ │ ├── hosts.ini
│ │ └── group_vars/
│ └── development/ # 开发环境
│ ├── hosts.ini
│ └── group_vars/
├── playbooks/ # Playbook 目录
│ ├── site.yml # 主 Playbook
│ ├── webservers.yml # Web 服务器 Playbook
│ └── databases.yml # 数据库 Playbook
├── roles/ # 角色目录
│ ├── common/ # 通用角色
│ ├── nginx/ # Nginx 角色
│ ├── mysql/ # MySQL 角色
│ └── php/ # PHP 角色
├── library/ # 自定义模块
├── filter_plugins/ # 自定义过滤器
├── host_vars/ # 主机变量(可选,推荐用 inventory/host_vars)
├── group_vars/ # 组变量(可选,推荐用 inventory/group_vars)
├── scripts/ # 脚本文件
├── templates/ # 模板文件(如果不在角色中)
├── files/ # 静态文件(如果不在角色中)
├── requirements.yml # 角色依赖
├── README.md # 项目文档
└── .gitignore # Git 忽略文件
环境分离
# 使用不同的 Inventory 文件管理不同环境
ansible-playbook -i inventory/production/hosts.ini site.yml
ansible-playbook -i inventory/staging/hosts.ini site.yml
ansible-playbook -i inventory/development/hosts.ini site.yml
Inventory 最佳实践
使用目录组织 Inventory
inventory/
├── production/
│ ├── hosts.ini # 主机清单
│ ├── group_vars/ # 组变量
│ │ ├── all.yml
│ │ ├── webservers.yml
│ │ ├── databases.yml
│ │ └── caches.yml
│ └── host_vars/ # 主机变量
│ ├── web1.yml
│ ├── web2.yml
│ └── db1.yml
└── staging/
└── ...
主机分组策略
# 按 function(功能)分组
[webservers]
web1.example.com
web2.example.com
[databases]
db1.example.com
db2.example.com
[caches]
cache1.example.com
cache2.example.com
# 按 environment(环境)分组
[production]
web1.example.com
db1.example.com
[staging]
web2.example.com
db2.example.com
# 按 location(位置)分组
[beijing]
web1.example.com
db1.example.com
[shanghai]
web2.example.com
db2.example.com
使用 YAML 格式
# YAML 格式更易读
all:
children:
webservers:
hosts:
web1.example.com:
ansible_user: deploy
web2.example.com:
ansible_user: deploy
databases:
hosts:
db1.example.com:
ansible_user: dbadmin
db2.example.com:
ansible_user: dbadmin
vars:
ansible_ssh_private_key_file: ~/.ssh/id_rsa
Playbook 最佳实践
使用 Roles
# 好的做法:使用 Roles
- name: 部署应用
hosts: app_servers
become: yes
roles:
- common
- nginx
- php
- mysql
# 不好的做法:在 Playbook 中写大量任务
- name: 部署应用
hosts: app_servers
become: yes
tasks:
- name: 更新缓存
apt:
update_cache: yes
# ... 几百行任务
命名规范
# Playbook 文件命名:使用下划线,描述性名称
site.yml # 主 Playbook
webservers.yml # Web 服务器 Playbook
databases.yml # 数据库 Playbook
deploy_application.yml # 部署应用 Playbook
# 任务命名:动词 + 对象
- name: 安装 Nginx # 好的做法
package:
name: nginx
state: present
- name: 配置 Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
# 变量命名:描述性,使用下划线
nginx_port: 80
nginx_worker_processes: auto
server_name: example.com
幂等性设计
# 好的做法:使用幂等模块
- name: 确保 Nginx 服务运行中
service:
name: nginx
state: started
enabled: yes
# 不好的做法:使用 shell 命令
- name: 启动 Nginx
shell: systemctl start nginx
# 使用 changed_when 定义幂等性
- name: 检查服务状态
command: systemctl is-active nginx
register: nginx_status
changed_when: false
- name: 启动服务(如果未运行)
service:
name: nginx
state: started
when: nginx_status.stdout != "active"
错误处理
# 使用 block-rescue-always
- name: 安全部署
block:
- name: 备份当前版本
shell: cp -r {{ app_dir }} {{ backup_dir }}
- name: 部署新版本
git:
repo: "{{ app_repo }}"
dest: "{{ app_dir }}"
rescue:
- name: 部署失败,回滚
shell: cp -r {{ backup_dir }} {{ app_dir }}
ignore_errors: yes
- name: 发送告警
debug:
msg: "部署失败,已回滚"
always:
- name: 清理临时文件
file:
path: /tmp/deploy_temp
state: absent
使用 Tags
# 为任务和角色添加标签
- name: 部署 Web 应用
hosts: webservers
become: yes
roles:
- role: nginx
tags:
- web
- nginx
- role: php-fpm
tags:
- web
- php
- role: mysql
tags:
- database
- mysql
# 执行特定标签的任务
ansible-playbook site.yml --tags "nginx"
ansible-playbook site.yml --tags "web"
ansible-playbook site.yml --skip-tags "database"
变量管理
变量优先级策略
# 1. defaults/ - 角色默认变量(优先级最低)
# roles/nginx/defaults/main.yml
nginx_port: 80
# 2. group_vars/all - 全局组变量
# group_vars/all.yml
nginx_port: 8080
# 3. group_vars/webservers - 特定组变量
# group_vars/webservers.yml
nginx_port: 8000
# 4. host_vars/web1 - 主机特定变量
# host_vars/web1.yml
nginx_port: 9000
# 5. play vars - Playbook 变量
# 在 Playbook 中定义
- name: 部署 Nginx
hosts: webservers
vars:
nginx_port: 7000
# 6. extra-vars - 命令行变量(优先级最高)
ansible-playbook site.yml -e "nginx_port=6000"
敏感信息管理
# 使用 Ansible Vault 加密敏感信息
# 加密文件
ansible-vault encrypt secrets.yml
# 创建加密文件
ansible-vault create secrets.yml
# secrets.yml
---
db_password: "secure_password_123"
api_key: "api_key_xyz"
ssh_key: |
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
# 在 Playbook 中使用
- name: 配置数据库
template:
src: database.conf.j2
dest: /etc/myapp/database.conf
vars_files:
- secrets.yml
# 运行时提供密码
ansible-playbook site.yml --ask-vault-pass
# 使用密码文件
echo "vault_password" > .vault_pass
ansible-playbook site.yml --vault-password-file .vault_pass
安全性
SSH 密钥管理
# 使用 SSH 密钥而非密码
# inventory/group_vars/all.yml
ansible_ssh_private_key_file: ~/.ssh/id_rsa_ansible
# 为不同环境使用不同密钥
# inventory/production/group_vars/all.yml
ansible_ssh_private_key_file: ~/.ssh/id_rsa_production
# inventory/staging/group_vars/all.yml
ansible_ssh_private_key_file: ~/.ssh/id_rsa_staging
限制 become 权限
# 只在需要时使用 become
- name: 配置应用(不需要 root 权限)
copy:
src: app.conf
dest: /opt/myapp/app.conf
# 没有 become
- name: 安装软件包(需要 root 权限)
become: yes
package:
name: nginx
state: present
# 使用特定的 become 用户
- name: 配置 PostgreSQL
become: yes
become_user: postgres
postgresql_user:
name: myapp
state: present
主机密钥检查
# ansible.cfg
[defaults]
# 生产环境开启检查
host_key_checking = True
# 测试环境可以关闭
# host_key_checking = False
# 或者在命令中覆盖
ansible-playbook site.yml -e "ansible_ssh_common_args='-o StrictHostKeyChecking=no'"
模块选择
优先使用 Ansible 模块
# 好的做法:使用 Ansible 模块
- name: 安装 Nginx
package:
name: nginx
state: present
- name: 创建用户
user:
name: deploy
shell: /bin/bash
- name: 配置文件
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
# 不好的做法:使用 shell 命令
- name: 安装 Nginx
shell: apt-get install -y nginx
- name: 创建用户
shell: useradd -m deploy -s /bin/bash
- name: 配置文件
shell: echo "server ..." > /etc/nginx/nginx.conf
何时使用 shell/command
# 当没有对应模块时使用 shell
- name: 自定义编译安装
shell: |
./configure
make
make install
args:
chdir: /tmp/source
# 使用 register 获取输出
- name: 获取系统信息
command: uname -r
register: kernel_version
changed_when: false
# 使用 failed_when 自定义失败条件
- name: 检查磁盘空间
shell: df -h / | tail -1 | awk '{print $5}' | cut -d'%' -f1
register: disk_usage
failed_when: disk_usage.stdout | int > 90
性能优化
禁用 Facts 收集
# 不需要 facts 时禁用
- name: 简单任务
hosts: all
gather_facts: no
tasks:
- name: ping
ping:
# 只收集特定 facts
- name: 收集特定 facts
hosts: all
gather_facts: yes
gather_subset:
- network
- virtual
SSH 管道复用
# ansible.cfg
[ssh_connection]
# 启用 SSH 管道复用
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
pipelining = True
# 提高并发数
[defaults]
forks = 20
使用 async
# 异步执行长时间任务
- name: 下载大文件
get_url:
url: http://example.com/large_file.iso
dest: /tmp/large_file.iso
async: 3600 # 最长等待 1 小时
poll: 30 # 每 30 秒检查一次
- name: 完全异步(不等待)
command: /usr/bin/long_running_task.sh
async: 3600
poll: 0 # 不等待,继续执行
调试和故障排查
使用 debug 模块
# 输出变量值
- name: 显示配置
debug:
msg: "配置: {{ app_config }}"
# 输出详细变量
- name: 显示所有 facts
debug:
var: ansible_facts
# 使用 verbosity 控制输出
- name: 调试信息
debug:
msg: "详细调试信息"
when: ansible_verbosity > 0
使用 check mode
# 只检查不执行(dry run)
ansible-playbook site.yml --check
# 配合 diff 使用
ansible-playbook site.yml --check --diff
启用详细输出
# -v: 详细输出
ansible-playbook site.yml -v
# -vv: 更详细
ansible-playbook site.yml -vv
# -vvv: 最详细(包括 SSH 连接)
ansible-playbook site.yml -vvv
版本控制
Git 配置
# .gitignore
.vault_pass
secrets.yml
*.retry
inventory/**/host_vars/*
inventory/**/group_vars/*_vault.yml
roles/**/tests/*
*.pyc
__pycache__/
# 加密敏感文件后再提交
ansible-vault encrypt group_vars/all/vault.yml
git add group_vars/all/vault.yml
提交规范
# 清晰的提交信息
git commit -m "feat: 添加 MySQL 角色"
git commit -m "fix: 修复 Nginx 配置模板错误"
git commit -m "docs: 更新部署文档"
git commit -m "refactor: 重构变量组织结构"
git commit -m "chore: 升级 Ansible 到 2.15"
文档
README.md
# 项目 README
## 项目描述
这是一个使用 Ansible 自动化部署 Web 应用的项目。
## 环境要求
- Ansible 2.15+
- Python 3.8+
- 目标主机:Ubuntu 20.04+
## 安装
```bash
pip install ansible-core
ansible-galaxy install -r requirements.yml
```
## 使用
```bash
# 更新 Inventory
vim inventory/production/hosts.ini
# 加密密码文件
ansible-vault create secrets.yml
# 执行 Playbook
ansible-playbook -i inventory/production/hosts.ini site.yml --ask-vault-pass
```
## 项目结构
说明各目录和文件的用途。
Role 文档
# roles/nginx/README.md
# Nginx Role
## 描述
安装和配置 Nginx Web 服务器。
## 变量
| 变量 | 默认值 | 描述 |
|------|--------|------|
| nginx_port | 80 | 监听端口 |
| nginx_worker_processes | auto | 工作进程数 |
| server_name | localhost | 服务器名称 |
## 依赖
- common
## 示例
```yaml
- name: 使用 Nginx 角色
hosts: webservers
roles:
- role: nginx
vars:
nginx_port: 8080
```
最佳实践检查清单
- 项目结构: 使用推荐的目录结构
- 环境分离: 使用不同的 Inventory 文件管理环境
- 使用 Roles: 将代码组织为可复用的角色
- 幂等性: 确保所有任务都是幂等的
- 命名规范: 使用一致的命名规范
- 变量管理: 合理管理变量优先级
- 敏感信息: 使用 Ansible Vault 加密
- 错误处理: 添加适当的错误处理
- 文档完善: 编写清晰的文档
- 版本控制: 使用 Git 管理代码
- 测试验证: 充分测试自动化流程
- 性能优化: 优化 Ansible 执行性能
总结
通过本教程,你已经了解了:
- 推荐的项目组织结构
- Inventory 和 Playbook 最佳实践
- 变量管理和安全性
- 性能优化技巧
- 调试和故障排查方法
- 版本控制和文档规范
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。







