Ansible Playbook编写教程
1. Ansible简介与核心概念
Ansible是一个开源的IT自动化工具,用于配置管理、应用部署、任务执行和基础设施编排。Playbook是Ansible的核心组件,使用YAML语言编写,定义了一系列要在目标主机上执行的任务。
1.1. 核心概念解析
Inventory(清单):定义要管理的主机列表和主机组
Modules(模块):执行具体任务的独立工具
Tasks(任务):调用模块完成的具体操作
Playbooks(剧本):多个任务的有序集合
Handlers(处理器):在特定变化时触发的特殊任务
Roles(角色):结构化组织Playbook的方法
Variables(变量):存储可配置值的数据结构
2. Playbook基础结构
一个基本的Playbook结构包含:
文档开始标记 ---
一个或多个play(以 - name: 开头)
每个play包含:
目标主机(hosts)
任务列表(tasks)
可选的变量、处理器等
2.1. 示例:最简单的Playbook
---
- name: 基础系统更新
hosts: webservers
become: yes
tasks:
- name: 更新所有软件包
apt:
update_cache: yes
upgrade: dist
解释:
---:YAML文档开始标记
- name::定义一个play,名称为"基础系统更新"
hosts: webservers:指定该play在名为webservers的主机组上执行
become: yes:使用sudo权限执行任务
tasks::任务列表的开始
- name::定义一个任务,名称为"更新所有软件包"
apt::使用apt模块(适用于Debian/Ubuntu系统)
update_cache: yes:更新软件包缓存
upgrade: dist:进行发行版升级
3. 详细Playbook编写指南
3.1. Inventory文件管理
Inventory定义了要管理的主机。可以创建为静态文件或动态生成。
#### 静态Inventory示例 (inventory.ini)
[webservers]
192.168.1.10
192.168.1.11
[databases]
192.168.1.20
[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa
解释:
[webservers]:定义主机组webservers,包含两台主机
[databases]:定义主机组databases,包含一台主机
[all:vars]:为所有主机定义变量
ansible_user:默认SSH用户
ansible_ssh_private_key_file:SSH私钥路径
#### 动态Inventory
可以通过脚本从云服务获取主机列表,例如AWS动态inventory脚本。
3.2. 常用模块详解
#### a. 包管理模块
apt模块(Debian/Ubuntu):
- name: 安装Nginx
apt:
name: nginx
state: present
update_cache: yes
yum模块(RHEL/CentOS):
- name: 安装httpd
yum:
name: httpd
state: present
参数说明:
name:软件包名称
state:状态(present/absent/latest)
update_cache:是否更新包缓存
#### b. 服务管理模块
- name: 确保Nginx服务运行
service:
name: nginx
state: started
enabled: yes
参数说明:
name:服务名称
state:状态(started/stopped/restarted/reloaded)
enabled:是否开机自启
#### c. 文件管理模块
- name: 创建目录
file:
path: /opt/myapp
state: directory
mode: '0755'
owner: www-data
group: www-data
参数说明:
path:文件/目录路径
state:状态(file/directory/link/absent)
mode:权限模式(八进制格式)
owner:所有者
group:所属组
#### d. 复制文件模块
- name: 复制配置文件
copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
backup: yes
validate: 'nginx -t %s'
参数说明:
src:源文件路径(相对于Playbook目录)
dest:目标路径
backup:是否备份原文件
validate:验证命令(%s会被临时文件名替换)
#### e. 模板模块
- name: 生成配置文件
template:
src: templates/app.conf.j2
dest: /etc/app.conf
mode: '0644'
模板文件示例(templates/app.conf.j2):
# {{ ansible_managed }}
ServerName {{ server_name }}
DocumentRoot {{ doc_root }}
LogLevel {{ log_level }}
参数说明:
src:模板文件路径(以.j2为后缀)
dest:目标文件路径
模板中使用Jinja2语法:{{ variable }}
#### f. 命令执行模块
- name: 检查磁盘空间
command: df -h
register: disk_info
- name: 显示磁盘信息
debug:
msg: "{{ disk_info.stdout_lines }}"
参数说明:
command:执行简单命令
register:将命令输出保存到变量
shell:执行复杂命令(支持管道、重定向等)
debug:调试模块,用于输出变量值
3.3. 变量系统
#### 定义变量的方式
在Playbook中直接定义:
- name: 使用变量示例
hosts: all
vars:
package_name: nginx
service_name: nginx
doc_root: /var/www/html
tasks:
- name: 安装{{ package_name }}
apt:
name: "{{ package_name }}"
state: present
在变量文件中定义:
创建vars/main.yml:
---
package_name: nginx
service_name: nginx
doc_root: /var/www/html
在Playbook中引用:
- name: 使用变量文件
hosts: all
vars_files:
- vars/main.yml
从命令行传递变量:
ansible-playbook playbook.yml --extra-vars "package_name=apache2"
#### 变量优先级
命令行值 (-e 或 --extra-vars)
Inventory中的变量
Play中的vars
角色中的变量
系统事实 (ansible_facts)
#### 使用Facts(系统事实)
- name: 显示系统信息
hosts: all
tasks:
- name: 显示操作系统信息
debug:
msg: "系统是 {{ ansible_facts['distribution'] }} {{ ansible_facts['distribution_version'] }}"
常用Facts:
ansible_facts['os_family']:操作系统家族(如Debian、RedHat)
ansible_facts['architecture']:系统架构(如x86_64)
ansible_facts['memtotal_mb']:总内存(MB)
ansible_facts['processor_vcpus']:CPU核心数
3.4. 任务控制结构
#### a. 条件判断(when)
- name: 仅在Debian系统上安装
apt:
name: apache2
state: present
when: ansible_facts['os_family'] == "Debian"
复杂条件示例:
- name: 满足多个条件时执行
apt:
name: nginx
state: present
when:
- ansible_facts['os_family'] == "Debian"
- ansible_facts['architecture'] == "x86_64"
- nginx_install is defined
#### b. 循环(with_items/loop)
简单列表循环:
- name: 安装多个软件包
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- mysql-server
- php-fpm
字典列表循环:
- name: 创建多个用户
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
groups: "{{ item.groups }}"
loop:
- { name: 'user1', uid: 1001, groups: 'admin' }
- { name: 'user2', uid: 1002, groups: 'developer' }
#### c. 错误处理
忽略错误:
- name: 可能失败的任务
command: /some/risky/command
ignore_errors: yes
自定义失败条件:
- name: 检查磁盘空间
command: df -h /
register: disk_usage
failed_when: "'90%' in disk_usage.stdout"
任务重试机制:
- name: 重试直到服务启动
uri:
url: http://localhost
register: result
until: result.status == 200
retries: 5
delay: 10
3.5. 处理器(Handlers)
处理器是特殊的任务,只有在被通知时才会执行,通常用于服务重启等操作。
示例:
- name: 配置Nginx
hosts: webservers
tasks:
- name: 更新Nginx配置
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: 重启Nginx
handlers:
- name: 重启Nginx
service:
name: nginx
state: restarted
解释:
当模板文件发生变化时,会触发"重启Nginx"处理器
处理器定义在handlers:部分
一个处理器可以被多个任务通知
3.6. 角色(Roles)
角色是一种结构化组织Playbook的方法,将变量、任务、文件等分离到独立目录。
#### 角色目录结构
nginx/
├── defaults/ # 默认变量(低优先级)
│ └── main.yml
├── files/ # 静态文件
├── handlers/ # 处理器
│ └── main.yml
├── meta/ # 角色依赖
│ └── main.yml
├── tasks/ # 任务
│ └── main.yml
├── templates/ # 模板文件
└── vars/ # 变量(高优先级)
└── main.yml
#### 创建Nginx角色
tasks/main.yml:
---
- name: 安装Nginx
apt:
name: nginx
state: present
- name: 配置Nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: 重启Nginx
- name: 启动Nginx服务
service:
name: nginx
state: started
enabled: yes
handlers/main.yml:
---
- name: 重启Nginx
service:
name: nginx
state: restarted
vars/main.yml:
---
worker_processes: "{{ ansible_processor_vcpus }}"
listen_port: 80
templates/nginx.conf.j2:
user www-data;
worker_processes {{ worker_processes }};
pid /run/nginx.pid;
events {
worker_connections 768;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server {
listen {{ listen_port }};
location / {
root /var/www/html;
index index.html;
}
}
}
#### 使用角色
site.yml:
---
- name: 配置Web服务器
hosts: webservers
become: yes
roles:
- nginx
- { role: php-fpm, when: install_php is defined }
传递变量给角色:
---
- name: 自定义角色变量
hosts: webservers
roles:
- role: nginx
vars:
worker_processes: 4
listen_port: 8080
3.7. 高级技巧
#### a. 包含和导入文件
include_tasks(动态包含):
- name: 包含任务
include_tasks: setup_{{ ansible_os_family }}.yml
import_tasks(静态导入):
- name: 导入任务
import_tasks: common_tasks.yml
include_role(动态包含角色):
- name: 包含角色
include_role:
name: nginx
#### b. 标签(Tags)
- name: 带标签的Playbook
hosts: all
tasks:
- name: 安装软件
apt:
name: nginx
tags: ['install', 'web']
- name: 配置防火墙
ufw:
rule: allow
name: 'Nginx Full'
tags: ['firewall']
执行特定标签的任务:
ansible-playbook playbook.yml --tags "install"
ansible-playbook playbook.yml --skip-tags "firewall"
#### c. 异步任务
- name: 长时间运行的任务
command: /usr/bin/long_running_script
async: 300 # 超时时间(秒)
poll: 30 # 轮询间隔(秒)
#### d. 委托任务(Delegate)
- name: 在负载均衡器上执行
command: /usr/bin/restart_haproxy
delegate_to: lb01
#### e. 本地执行任务
- name: 在控制节点上执行
command: echo "This runs on the control machine"
connection: local
3.8. 完整示例:部署WordPress应用
#### 目录结构
wordpress_deploy/
├── group_vars/
│ └── webservers.yml
├── roles/
│ ├── mysql/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ └── handlers/
│ │ └── main.yml
│ ├── nginx/
│ │ ├── tasks/
│ │ │ └── main.yml
│ │ ├── handlers/
│ │ │ └── main.yml
│ │ └── templates/
│ │ └── nginx.conf.j2
│ └── php/
│ ├── tasks/
│ │ └── main.yml
│ └── handlers/
│ └── main.yml
├── site.yml
└── inventory.ini
#### inventory.ini
[webservers]
192.168.1.10
192.168.1.11
[databases]
192.168.1.20
#### group_vars/webservers.yml
---
mysql_root_password: "SecurePassword123!"
wp_db_name: "wordpress"
wp_db_user: "wp_user"
wp_db_password: "WpUserPassword!"
wp_url: "example.com"
wp_title: "My WordPress Site"
wp_admin_user: "admin"
wp_admin_password: "AdminPassword123!"
wp_admin_email: "admin@example.com"
#### site.yml
---
- name: 配置数据库服务器
hosts: databases
become: yes
roles:
- mysql
- name: 配置Web服务器
hosts: webservers
become: yes
roles:
- nginx
- php
#### roles/mysql/tasks/main.yml
---
- name: 安装MySQL
apt:
name: "{{ item }}"
state: present
loop:
- mysql-server
- python3-pymysql
- name: 启动MySQL服务
service:
name: mysql
state: started
enabled: yes
- name: 创建WordPress数据库
mysql_db:
name: "{{ wp_db_name }}"
state: present
login_user: root
login_password: "{{ mysql_root_password }}"
- name: 创建WordPress用户
mysql_user:
name: "{{ wp_db_user }}"
password: "{{ wp_db_password }}"
priv: "{{ wp_db_name }}.*:ALL"
host: '%'
login_user: root
login_password: "{{ mysql_root_password }}"
#### roles/nginx/tasks/main.yml
---
- name: 安装Nginx
apt:
name: nginx
state: present
- name: 创建Nginx配置
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/wordpress
notify: 重启Nginx
- name: 启用WordPress站点
file:
src: /etc/nginx/sites-available/wordpress
dest: /etc/nginx/sites-enabled/wordpress
state: link
notify: 重启Nginx
- name: 启动Nginx服务
service:
name: nginx
state: started
enabled: yes
- name: 移除默认站点
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: 重启Nginx
#### roles/nginx/templates/nginx.conf.j2
server {
listen 80;
server_name {{ wp_url }};
root /var/www/wordpress;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
#### roles/php/tasks/main.yml
---
- name: 安装PHP和扩展
apt:
name: "{{ item }}"
state: present
loop:
- php-fpm
- php-mysql
- php-gd
- php-xml
- php-curl
- php-mbstring
- php-xmlrpc
- php-soap
- php-intl
- php-zip
- name: 启动PHP-FPM
service:
name: php7.4-fpm
state: started
enabled: yes
- name: 下载WordPress
unarchive:
src: https://wordpress.org/latest.tar.gz
dest: /var/www/
remote_src: yes
owner: www-data
group: www-data
creates: /var/www/wordpress
- name: 配置WordPress
template:
src: wp-config.php.j2
dest: /var/www/wordpress/wp-config.php
owner: www-data
group: www-data
#### roles/php/handlers/main.yml
---
- name: 重启PHP-FPM
service:
name: php7.4-fpm
state: restarted
#### roles/php/templates/wp-config.php.j2
<?php
define('DB_NAME', '{{ wp_db_name }}');
define('DB_USER', '{{ wp_db_user }}');
define('DB_PASSWORD', '{{ wp_db_password }}');
define('DB_HOST', '192.168.1.20');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
define('AUTH_KEY', 'put your unique phrase here');
define('SECURE_AUTH_KEY', 'put your unique phrase here');
define('LOGGED_IN_KEY', 'put your unique phrase here');
define('NONCE_KEY', 'put your unique phrase here');
define('AUTH_SALT', 'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT', 'put your unique phrase here');
define('NONCE_SALT', 'put your unique phrase here');
$table_prefix = 'wp_';
define('WP_DEBUG', false);
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');
require_once(ABSPATH . 'wp-settings.php');
#### 执行部署
ansible-playbook -i inventory.ini site.yml
4. 总结
本教程全面介绍了Ansible Playbook的编写方法,从基础概念到高级技巧,包括:
核心概念:解释了Ansible的基本组成部分和Playbook的作用
基础结构:展示了Playbook的基本格式和组成部分
模块详解:介绍了常用模块如包管理、服务管理、文件操作等
变量系统:讲解了变量的定义、使用方式和优先级
任务控制:演示了条件判断、循环和错误处理
6.





