Ansible 使用手册
# Ansible 使用手册
> Ansible 是一款开源的自动化运维工具,基于 Python 开发,能够实现配置管理、应用部署、任务编排等功能。
---
## 目录
1. [Ansible 简介](#1-ansible-简介)
2. [安装与配置](#2-安装与配置)
3. [核心概念](#3-核心概念)
4. [Ad-Hoc 命令](#4-ad-hoc-命令)
5. [Playbook 详解](#5-playbook-详解)
6. [常用模块](#6-常用模块)
7. [变量与 Facts](#7-变量与-facts)
8. [条件与循环](#8-条件与循环)
9. [Roles 角色](#9-roles-角色)
10. [Inventory 主机清单](#10-inventory-主机清单)
11. [Vault 加密](#11-vault-加密)
12. [最佳实践](#12-最佳实践)
13. [常用场景示例](#13-常用场景示例)
---
## 1. Ansible 简介
### 1.1 核心特性
| 特性 | 说明 |
|------|------|
| 无代理 | 通过 SSH 管理目标主机,无需安装 Agent |
| 幂等性 | 多次执行同一操作,结果一致 |
| 简单易用 | YAML 语法,易于阅读和编写 |
| 扩展性强 | 丰富的模块生态,支持自定义模块 |
### 1.2 工作原理
```
控制节点 (Control Node)
|
| SSH (Python)
v
被管节点 (Managed Node)
```
- 控制节点:运行 Ansible 的机器
- 被管节点:被 Ansible 管理的主机
- 通过 SSH 连接,在远程执行 Python 脚本
---
## 2. 安装与配置
### 2.1 安装 Ansible
```bash
# CentOS / RHEL
yum install -y epel-release
yum install -y ansible
# Ubuntu / Debian
apt update
apt install -y ansible
# Python pip 方式(推荐,版本最新)
pip install --upgrade pip
pip install ansible
# 验证安装
ansible --version
```
### 2.2 配置文件
配置文件读取顺序(优先级从低到高):
```
1. ANSIBLE_CONFIG 环境变量
2. ./ansible.cfg # 当前目录
3. ~/.ansible.cfg # 用户目录
4. /etc/ansible/ansible.cfg # 全局配置
```
**常用配置项 `ansible.cfg`:**
```ini
[defaults]
# 主机清单文件位置
inventory = ./inventory/hosts
# 远程执行用户
remote_user = root
# 是否收集 facts
gathering = smart
# fact 缓存时间(秒)
fact_caching_timeout = 86400
# 输出格式
stdout_callback = yaml
# 颜色输出
force_color = True
[ssh_connection]
# SSH 管道加速
pipelining = True
# 控制并发数
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
```
---
## 3. 核心概念
### 3.1 术语表
| 术语 | 说明 |
|------|------|
| **Control Node** | 安装 Ansible 的控制机 |
| **Managed Node** | 被管理的目标主机 |
| **Inventory** | 主机清单,定义管理哪些主机 |
| **Module** | 模块,执行具体操作的工具 |
| **Task** | 任务,调用一个模块完成一个操作 |
| **Play** | 一组任务的集合,针对一组主机 |
| **Playbook** | YAML 文件,包含一个或多个 Play |
| **Role** | 角色,组织 Playbook 的标准方式 |
| **Fact** | 自动收集的远程主机信息 |
### 3.2 执行模型
```
Playbook → Play → Task → Module → 远程执行 → 返回结果
```
---
## 4. Ad-Hoc 命令
Ad-Hoc 命令用于快速执行一次性任务,无需编写 Playbook。
### 4.1 基本语法
```bash
ansible <host-pattern> -m <module> -a "<module-args>"
```
### 4.2 常用 Ad-Hoc 示例
```bash
# 测试主机连通性
ansible all -m ping
# 执行远程命令
ansible webservers -m shell -a "uptime"
ansible webservers -m command -a "df -h"
# 复制文件
ansible webservers -m copy -a "src=/local/file dest=/remote/file"
# 安装软件包
ansible webservers -m yum -a "name=nginx state=present"
ansible webservers -m apt -a "name=nginx state=present"
# 启动服务
ansible webservers -m service -a "name=nginx state=started enabled=yes"
# 添加用户
ansible all -m user -a "name=deploy password={{ 'password' | password_hash('sha512') }}"
# 使用 sudo 执行
ansible webservers -m shell -a "systemctl status nginx" --become
```
### 4.3 输出详解
```bash
# 详细输出
ansible webservers -m ping -v
ansible webservers -m ping -vvv # 更详细
# 指定并行数
ansible webservers -m ping -f 10 # 10 个并行进程
```
---
## 5. Playbook 详解
### 5.1 Playbook 结构
```yaml
---
# 一个 Playbook 包含一个或多个 Play
- name: Play 的名称(描述性)
hosts: webservers # 目标主机
become: yes # 是否提权
vars: # 变量
http_port: 80
tasks: # 任务列表
- name: 任务描述
module_name:
option1: value1
option2: value2
handlers: # 处理器(由任务通知触发)
- name: restart nginx
service:
name: nginx
state: restarted
```
### 5.2 第一个 Playbook
`site.yml`:
```yaml
---
- name: 配置 Web 服务器
hosts: webservers
become: yes
tasks:
- name: 安装 Nginx
apt:
name: nginx
state: present
- name: 启动 Nginx 服务
service:
name: nginx
state: started
enabled: yes
```
**执行 Playbook:**
```bash
# 检查语法
ansible-playbook site.yml --syntax-check
# 干跑(不实际执行)
ansible-playbook site.yml --check
# 执行
ansible-playbook site.yml
# 指定 tags 执行
ansible-playbook site.yml --tags "install,config"
# 从指定 task 开始执行
ansible-playbook site.yml --start-at-task "启动 Nginx 服务"
# 指定额外变量
ansible-playbook site.yml -e "env=prod version=1.2.0"
```
### 5.3 Handlers(处理器)
处理器在任务中通过 `notify` 触发,仅在任务状态为 `changed` 时执行。
```yaml
tasks:
- name: 复制 Nginx 配置文件
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify:
- restart nginx
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
```
---
## 6. 常用模块
### 6.1 系统管理模块
| 模块 | 说明 | 示例 |
|------|------|------|
| `ping` | 测试连通性 | `ansible all -m ping` |
| `command` | 执行命令(无 shell) | `command: uptime` |
| `shell` | 执行 shell 命令 | `shell: echo $HOME` |
| `script` | 执行本地脚本 | `script: /path/to/script.sh` |
| `service` | 管理服务 | `service: name=nginx state=started` |
| `systemd` | 管理 systemd 服务 | `systemd: name=nginx state=started` |
| `user` | 管理用户 | `user: name=deploy state=present` |
| `group` | 管理用户组 | `group: name=dev state=present` |
| `cron` | 管理定时任务 | `cron: name="backup" minute="0" job="/script"` |
| `reboot` | 重启主机 | `reboot: reboot_timeout=300` |
### 6.2 文件管理模块
| 模块 | 说明 | 示例 |
|------|------|------|
| `copy` | 复制文件 | `copy: src=file dest=/path/` |
| `fetch` | 从远程拉取文件 | `fetch: src=/remote/file dest=/local/` |
| `file` | 管理文件属性 | `file: path=/tmp state=touch mode=0644` |
| `lineinfile` | 行替换 | `lineinfile: path=/etc/hosts line="127.0.0.1 localhost"` |
| `blockinfile` | 块替换 | `blockinfile: path=/etc/hosts block="..."` |
| `template` | 模板渲染 | `template: src=nginx.j2 dest=/etc/nginx.conf` |
| `unarchive` | 解压文件 | `unarchive: src=tar.gz dest=/opt/` |
| `find` | 查找文件 | `find: paths=/var/log patterns="*.log"` |
### 6.3 软件包管理模块
| 模块 | 说明 |
|------|------|
| `apt` | Debian/Ubuntu |
| `yum` | RHEL/CentOS 7 |
| `dnf` | RHEL/CentOS 8+ |
| `pip` | Python 包 |
| `npm` | Node.js 包 |
```yaml
# apt 示例
- name: 安装软件包
apt:
name:
- nginx
- mysql-server
state: present
update_cache: yes
# yum 示例
- name: 安装软件包
yum:
name: nginx
state: latest
```
### 6.4 网络与下载模块
| 模块 | 说明 |
|------|------|
| `get_url` | 下载文件 | `get_url: url=http://... dest=/tmp/` |
| `uri` | 调用 HTTP API | `uri: url=http://api method=GET` |
| `slurp` | 读取远程文件(base64) | `slurp: src=/etc/hostname` |
### 6.5 数据库模块
| 模块 | 说明 |
|------|------|
| `mysql_db` | 管理 MySQL 数据库 |
| `mysql_user` | 管理 MySQL 用户 |
| `postgresql_db` | 管理 PostgreSQL 数据库 |
| `redis` | 管理 Redis |
### 6.6 模块参数说明
```yaml
# state 参数的常用值
state: present # 确保存在
state: absent # 确保不存在
state: latest # 最新版本
state: started # 服务启动
state: stopped # 服务停止
state: restarted # 服务重启
state: reloaded # 服务重载
```
---
## 7. 变量与 Facts
### 7.1 变量定义方式
**方式一:在 Playbook 中定义**
```yaml
- hosts: webservers
vars:
http_port: 80
app_dir: /opt/myapp
vars_files:
- vars/secrets.yml
tasks:
- name: 使用变量
debug:
msg: "端口是 {{ http_port }}"
```
**方式二:在 Inventory 中定义**
```ini
# inventory/hosts
[webservers]
web1 ansible_host=192.168.1.10 http_port=8080
web2 ansible_host=192.168.1.11
[webservers:vars]
http_port=80
env=prod
```
**方式三:在 Group/Host Vars 文件中定义**
```
inventory/
├── hosts # 主机清单
├── group_vars/
│ ├── all.yml # 所有主机
│ └── webservers.yml # webservers 组
└── host_vars/
├── web1.yml # web1 主机
└── web2.yml # web2 主机
```
`group_vars/webservers.yml` 示例:
```yaml
---
http_port: 80
app_env: production
db_host: 192.168.1.100
```
### 7.2 变量优先级(由低到高)
```
1. command line -e 参数
2. vars_files
3. vars in play
4. host_vars/
5. group_vars/
6. inventory 中的变量
7. facts(通过 setup 模块收集)
8. registered variables(任务注册变量)
```
### 7.3 Facts(事实变量)
```bash
# 收集所有 facts
ansible web1 -m setup
# 收集指定 facts
ansible web1 -m setup -a "filter=ansible_os_family"
ansible web1 -m setup -a "filter=ansible_*_mb"
```
**常用 Facts 变量:**
```yaml
# 操作系统信息
ansible_os_family # RedHat / Debian
ansible_distribution # CentOS / Ubuntu
ansible_distribution_major_version
# 网络信息
ansible_default_ipv4.address
ansible_hostname
ansible_fqdn
# 硬件信息
ansible_memtotal_mb
ansible_processor_cores
ansible_devices
```
### 7.4 注册变量(Register)
```yaml
- name: 检查 Nginx 配置
shell: nginx -t
register: nginx_test
changed_when: false # 始终不标记为 changed
- name: 输出检查结果
debug:
var: nginx_test.stderr_lines
```
---
## 8. 条件与循环
### 8.1 条件判断(when)
```yaml
- name: 仅对 CentOS 执行
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"
- name: 仅对 Ubuntu 执行
apt:
name: nginx
state: present
when: ansible_distribution == "Ubuntu"
- name: 条件组合
service:
name: nginx
state: restarted
when:
- ansible_os_family == "Debian"
- nginx_installed.changed
# 运算符
when: ansible_memtotal_mb > 2048
when: package_version is version('1.2.0', '>=')
when: result.rc == 0
when: result.rc != 0
```
### 8.2 循环(Loops)
**标准循环(loop):**
```yaml
# 安装多个包
- name: 安装软件包
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- mysql-server
- redis-server
# 带字典的循环
- name: 创建用户
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop:
- { name: 'alice', groups: 'sudo' }
- { name: 'bob', groups: 'dev' }
```
**旧式循环(with_items,兼容):**
```yaml
- name: 复制配置文件
copy:
src: "{{ item }}"
dest: /etc/nginx/conf.d/
with_items:
- site1.conf
- site2.conf
```
**循环控制:**
```yaml
- name: 循环直到成功
shell: systemctl is-active nginx
register: result
until: result.stdout == "active"
retries: 5
delay: 3 # 每次重试间隔 3 秒
```
---
## 9. Roles 角色
### 9.1 角色目录结构
```
roles/
└── nginx/
├── tasks/
│ └── main.yml # 主任务文件
├── handlers/
│ └── main.yml # 处理器
├── templates/
│ └── nginx.conf.j2 # 模板文件
├── files/
│ └── logo.png # 静态文件
├── vars/
│ └── main.yml # 角色变量
├── defaults/
│ └── main.yml # 默认变量(优先级最低)
├── meta/
│ └── main.yml # 角色元数据
└── README.md
```
### 9.2 创建角色
```bash
# 使用 ansible-galaxy 创建角色骨架
ansible-galaxy init roles/nginx
# 从 Galaxy 安装角色
ansible-galaxy install nginxinc.nginx
# 列出已安装角色
ansible-galaxy list
```
### 9.3 角色示例
`roles/nginx/tasks/main.yml`:
```yaml
---
- name: 安装 Nginx
apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
- name: 复制配置文件
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: 启动 Nginx
service:
name: nginx
state: started
enabled: yes
```
`roles/nginx/templates/nginx.conf.j2`(Jinja2 模板):
```nginx
user www-data;
worker_processes {{ ansible_processor_cores }};
pid /run/nginx.pid;
events {
worker_connections 768;
}
http {
server {
listen {{ http_port | default(80) }};
server_name {{ server_name | default("_") }};
root {{ app_root | default("/var/www/html") }};
}
}
```
### 9.4 使用角色
```yaml
---
- name: 部署 Web 服务器
hosts: webservers
become: yes
roles:
- nginx
- role: mysql
mysql_port: 3307
vars:
http_port: 8080
server_name: example.com
```
---
## 10. Inventory 主机清单
### 10.1 INI 格式
```ini
# inventory/hosts
# 定义组
[webservers]
web1 ansible_host=192.168.1.10 ansible_user=root
web2 ansible_host=192.168.1.11
web[3:5] ansible_host=192.168.1.[12:14] # 范围写法
[dbservers]
db1 ansible_host=192.168.1.20
db2 ansible_host=192.168.1.21
# 组嵌套
[production:children]
webservers
dbservers
# 通用变量
[all:vars]
ansible_port=22
ansible_user=root
```
### 10.2 YAML 格式(推荐)
```yaml
# inventory/hosts.yml
all:
vars:
ansible_user: root
ansible_port: 22
children:
webservers:
hosts:
web1:
ansible_host: 192.168.1.10
web2:
ansible_host: 192.168.1.11
dbservers:
hosts:
db1:
ansible_host: 192.168.1.20
production:
children:
webservers:
dbservers:
```
### 10.3 动态 Inventory
```bash
# AWS EC2 动态清单
pip install boto3
export AWS_ACCESS_KEY_ID=xxx
export AWS_SECRET_ACCESS_KEY=xxx
ansible-inventory -i aws_ec2.yml --list
# 使用动态清单执行
ansible-playbook -i aws_ec2.yml site.yml
```
---
## 11. Vault 加密
用于加密敏感数据(密码、密钥等)。
```bash
# 创建加密文件
ansible-vault create secrets.yml
# 编辑加密文件
ansible-vault edit secrets.yml
# 加密已有文件
ansible-vault encrypt secrets.yml
# 解密文件
ansible-vault decrypt secrets.yml
# 查看加密文件
ansible-vault view secrets.yml
# 修改密码
ansible-vault rekey secrets.yml
# 执行时提供密码
ansible-playbook site.yml --ask-vault-pass
ansible-playbook site.yml --vault-password-file ~/.vault_pass.txt
```
---
## 12. 最佳实践
### 12.1 目录结构建议
```
ansible-project/
├── ansible.cfg # 配置文件
├── inventory/
│ ├── hosts.yml # 主机清单
│ ├── group_vars/
│ │ ├── all.yml
│ │ └── production.yml
│ └── host_vars/
├── roles/ # 角色目录
│ ├── common/
│ ├── nginx/
│ └── mysql/
├── playbooks/ # Playbook 目录
│ ├── site.yml # 主入口
│ ├── web.yml
│ └── db.yml
├── files/ # 静态文件
├── templates/ # 模板文件
└── .gitignore
```
### 12.2 编码规范
- 使用 YAML 格式,缩进用 2 个空格
- 每个任务都写 `name`,便于调试
- 使用 `become` 而非 `sudo`
- 敏感数据使用 Vault 加密
- 使用 Roles 组织代码,避免巨型 Playbook
- 使用 `--check` 模式先验证再执行
### 12.3 性能优化
```ini
# ansible.cfg
[defaults]
# 开启 SSH 管道
pipelining = True
# 开启 Fact 缓存
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
[ssh_connection]
# 开启 SSH 多路复用
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o PreferredAuthentications=publickey
# 加速文件传输
control_path_dir = /tmp/.ansible/cp
```
---
## 13. 常用场景示例
### 13.1 部署 LNMP 环境
```yaml
# playbooks/lnmp.yml
---
- name: 部署 LNMP 环境
hosts: webservers
become: yes
roles:
- role: common
- role: nginx
- role: mysql
- role: php
```
### 13.2 批量修改 SSH 端口
```yaml
# playbooks/change_ssh_port.yml
---
- name: 修改 SSH 端口
hosts: all
become: yes
vars:
new_ssh_port: 2222
tasks:
- name: 修改 SSH 配置
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?Port'
line: "Port {{ new_ssh_port }}"
notify: restart sshd
- name: 开放新端口防火墙
firewalld:
port: "{{ new_ssh_port }}/tcp"
permanent: yes
state: enabled
notify: reload firewalld
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
- name: reload firewalld
service:
name: firewalld
state: reloaded
```
### 13.3 滚动更新(Rolling Update)
```yaml
- name: 滚动更新应用
hosts: webservers
become: yes
serial: 1 # 每次只更新一台
max_fail_percentage: 0 # 不允许失败
tasks:
- name: 摘除流量(从负载均衡)
uri:
url: "http://lb/api/remove/{{ ansible_hostname }}"
delegate_to: localhost
- name: 部署新版本
copy:
src: app-v2.jar
dest: /opt/app/app.jar
- name: 重启应用
systemd:
name: myapp
state: restarted
- name: 健康检查
uri:
url: "http://{{ ansible_host }}:8080/health"
return_content: yes
register: health
until: health.status == 200
retries: 10
delay: 3
- name: 恢复流量
uri:
url: "http://lb/api/add/{{ ansible_hostname }}"
delegate_to: localhost
```
### 13.4 常用 Ad-Hoc 速查
```bash
# 检查磁盘空间
ansible all -m shell -a "df -h"
# 检查内存使用
ansible all -m shell -a "free -h"
# 批量修改主机名
ansible all -m hostname -a "name={{ inventory_hostname }}"
# 同步时间
ansible all -m shell -a "ntpdate ntp.aliyun.com"
# 清理 YUM 缓存
ansible all -m shell -a "yum clean all"
# 查看系统信息
ansible all -m setup -a "filter=ansible_*_mb"
```
---
## 附录:常用命令速查
```bash
# 语法检查
ansible-playbook site.yml --syntax-check
# 干跑(不实际执行)
ansible-playbook site.yml --check
# 详细输出
ansible-playbook site.yml -vvv
# 指定 inventory
ansible-playbook -i inventory/hosts.yml site.yml
# 指定用户
ansible-playbook site.yml -u root --ask-pass
# 指定 sudo 密码
ansible-playbook site.yml --ask-become-pass
# 列出所有主机
ansible all --list-hosts
# 列出所有任务
ansible-playbook site.yml --list-tasks
# 列出所有标签
ansible-playbook site.yml --list-tags
# 只执行指定标签的任务
ansible-playbook site.yml --tags "install,config"
# 跳过指定标签的任务
ansible-playbook site.yml --skip-tags "test"
```
---
*文档版本:v1.0 | 适用于 Ansible 2.9+*
浙公网安备 33010602011771号