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+*

posted @ 2026-06-29 09:57  ggsa202008  阅读(9)  评论(0)    收藏  举报