有时候一个任务需要大量的操作,继续执行ad-hoc命令不合适,这个时候用剧本(playbook)。
playbook:批处理任务,但是他有自己的语法格式。
playbook文件格式
- YAML语言编写。
- YAML是一个类似XML、JSON的标记性语言,YAML强调以数据为中心。
- YAML本身定义比较简单,号称"一种人性化的数据格式语言"
YAML语言规范:
- 大小写敏感
- 使用空格作为嵌套缩进工具,缩进时不允许使用Tab键。
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。
- 使用"-"(横线)+单个空格:表示单个列表项。
- 使用":"+空格:表示单个键值对。
- 使用"{}"表示一个键值表。
playbook文件执行
通过ansible-playbook命令进行解析的,ansible-playbook命令会根据自上而下的顺序依次执行playbook文件中的内容。
它允许传输某个命令的状态到后面的指令,也可以从一台机器的文件中抓取内容并附为变量,然后在另外一台机器中使用,这样可以实现一些复杂的部署机制。
解释:
传输命令的状态到后面的指令
含义:执行一个命令,根据它的执行结果(成功/失败/输出内容),决定后续指令是否执行或如何执行。
- name: 根据服务状态决定操作
hosts: web_servers
tasks:
# 1. 检查 nginx 是否运行
- name: 获取 nginx 状态
command: systemctl is-active nginx
register: nginx_status # 将命令的状态(输出/返回码)保存到变量
ignore_errors: yes # 即使服务未运行也不停止
# 2. 根据状态执行不同操作
- name: 如果 nginx 正在运行,则重载配置
command: systemctl reload nginx
when: nginx_status.stdout == "active" # 只有状态为 active 才执行
- name: 如果 nginx 未运行,则启动它
command: systemctl start nginx
when: nginx_status.stdout != "active"
从一台机器的文件中抓取内容作为变量,在另一台机器使用
含义:在机器 A 读取文件内容 → 保存为变量 → 在机器 B 使用这个变量(比如部署时保持配置一致性)。
- name: 跨主机传递变量
hosts: master_server
tasks:
# 1. 从 master 机器读取版本号文件
- name: 抓取当前部署版本
slurp: # slurp 模块用于抓取文件内容(base64编码)
src: /app/version.txt
register: version_file
- name: 解码文件内容
set_fact:
current_version: "{{ version_file.content | b64decode | trim }}"
# 2. 将版本号写入一个变量(便于其他主机使用)
- name: 设置全局变量
set_fact:
deploy_version: "{{ current_version }}"
delegate_to: localhost # 暂存在控制机
- name: 在目标服务器上使用该变量
hosts: app_servers
tasks:
- name: 根据版本号拉取对应镜像
command: "docker pull myapp:{{ hostvars['master_server']['deploy_version'] }}"
# hostvars['master_server']['deploy_version'] 访问另一台机器的变量
- name: 部署指定版本
command: "docker run -d myapp:{{ hostvars['master_server']['deploy_version'] }}"
playbook文件的构成
playbook是由一个或者多个"play"组成的列表。play的主要功能在于,将事先合并为一组的主机封装成事先通过ansible定义好的角色。将多个play组织在一个playbook中就可以让他们连同起来按照事先安排好的机制完成一些列复杂的任务。
解释:
实际场景:部署一个完整的 Web 应用 (包含负载均衡、应用服务器、数据库)。
概念图:

具体实现:
---
# 这是一个 Playbook,包含 3 个 Play
# ============================================
# Play 1: 配置负载均衡器
# ============================================
- name: 配置负载均衡服务器
hosts: lb_servers # 作用于负载均衡器主机组
become: yes # 使用 sudo 权限
tasks:
- name: 安装 Nginx
apt:
name: nginx
state: present
- name: 配置 Nginx 上游服务器
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: reload nginx # 配置变更时触发 handler
- name: 启动 Nginx 服务
systemd:
name: nginx
state: started
enabled: yes
handlers:
- name: reload nginx
systemd:
name: nginx
state: reloaded
# ============================================
# Play 2: 部署应用服务器
# ============================================
- name: 部署应用服务器
hosts: app_servers # 作用于应用服务器主机组
become: yes
tasks:
- name: 安装 Java 11
apt:
name: openjdk-11-jdk
state: present
- name: 创建应用目录
file:
path: /opt/myapp
state: directory
mode: '0755'
- name: 复制应用 JAR 包
copy:
src: /jenkins/workspace/myapp.jar
dest: /opt/myapp/myapp.jar
- name: 配置应用启动参数
template:
src: application.yml.j2
dest: /opt/myapp/application.yml
- name: 启动应用
systemd:
name: myapp
state: started
enabled: yes
daemon_reload: yes
# ============================================
# Play 3: 配置数据库服务器
# ============================================
- name: 配置数据库服务器
hosts: db_servers # 作用于数据库服务器主机组
become: yes
tasks:
- name: 安装 MySQL
apt:
name: mysql-server
state: present
- name: 创建应用数据库
mysql_db:
name: myapp_production
state: present
- name: 创建数据库用户
mysql_user:
name: myapp_user
password: "{{ db_password }}"
priv: 'myapp_production.*:ALL'
state: present
- name: 导入初始数据
mysql_db:
name: myapp_production
state: import
target: /opt/sql/init.sql
5个核心组件:
target部分:定义将要执行playbook的远程主机。
variable部分:定义playbook运行时需要使用的变量
task部分:定义将要在远程主机上将要执行的任务列表
handler部分:定义task执行完成以后需要调用的任务
#部署一个完整的 Web 应用
# 假设我们需要部署一个 Nginx + PHP + MySQL 的 Web 应用,通过 Role 来组织。
---
# site.yml - 主 Playbook
- name: 部署完整的 Web 应用
hosts: web_servers # 1️⃣ target:目标主机
become: yes
vars: # 2️⃣ variable:变量定义
app_name: mywebapp
app_port: 8080
db_host: 192.168.1.100
db_name: webapp_db
db_user: webapp_user
tasks: # 3️⃣ task:任务列表
- name: 确保 Nginx 已安装
apt:
name: nginx
state: present
- name: 复制配置文件
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx # 4️⃣ handler:触发条件
- name: 启动 Nginx
systemd:
name: nginx
state: started
handlers: # 4️⃣ handler:任务完成后调用的任务
- name: restart nginx
systemd:
name: nginx
state: restarted
Roles:角色
对应的5个层目录:
vars变量层
task任务层
handlers触发条件
files文件
template模板
roles/
├── common/ # 通用基础角色
│ ├── defaults/ # 1️⃣ vars变量层(默认变量)
│ │ └── main.yml
│ ├── vars/ # 1️⃣ vars变量层(高优先级变量)
│ │ └── main.yml
│ ├── tasks/ # 2️⃣ task任务层
│ │ └── main.yml
│ ├── handlers/ # 3️⃣ handlers触发条件层
│ │ └── main.yml
│ ├── files/ # 4️⃣ files文件层(静态文件)
│ │ ├── sshd_config
│ │ └── hosts
│ ├── templates/ # 5️⃣ template模板层(Jinja2模板)
│ │ ├── nginx.conf.j2
│ │ └── app.yml.j2
│ └── meta/ # 元数据(依赖关系)
│ └── main.yml
│
├── nginx/ # Nginx 角色
│ ├── defaults/
│ │ └── main.yml # 默认配置
│ ├── tasks/
│ │ └── main.yml
│ ├── handlers/
│ │ └── main.yml
│ ├── templates/
│ │ └── nginx.conf.j2
│ └── files/
│ └── nginx_logrotate
│
└── mysql/ # MySQL 角色
├── defaults/
├── tasks/
├── handlers/
├── templates/
│ ├── my.cnf.j2
│ └── init.sql.j2
└── files/
└── mysql_secure.sql
playbook组件
playbook中的每一个play的目的都是为了让特定主机以某个指定的用户身份执行任务。
hosts用于指定要执行指定任务的主机,需要事先定义在主机清单。
host
vim /etc/ansible/hosts
# hosts 文件
[all]
192.168.79.139
192.168.79.140
192.168.79.141
192.168.79.142
192.168.79.143
192.168.79.144
192.168.79.145
[all:vars]
ansible_user=root
ansible_ssh_pass=root
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
[webservers]
192.168.79.139
192.168.79.140
[db]
192.168.79.141
192.168.79.142
192.168.79.143
192.168.79.144
192.168.79.145
[docker:children]
webservers
db
---
- name: 基础服务器信息收集
hosts: all
gather_facts: yes
tasks:
# 1. 基础连接测试
- name: 测试连接
ping:
# 2. 系统信息
- name: 收集系统信息
debug:
msg: |
====================================
主机名: {{ ansible_hostname }}
系统: {{ ansible_system }} {{ ansible_kernel }}
发行版: {{ ansible_distribution }} {{ ansible_distribution_version }}
CPU: {{ ansible_processor_cores }} 核心
内存: {{ ansible_memtotal_mb }} MB
架构: {{ ansible_architecture }}
====================================
# 3. 网络信息
- name: 显示网络接口
debug:
var: ansible_interfaces
- name: 显示默认网关
debug:
msg: "默认网关: {{ ansible_default_ipv4.gateway }}"
when: ansible_default_ipv4.gateway is defined
# 4. 时间信息
- name: 显示系统时间
debug:
msg: "当前时间: {{ ansible_date_time.date }} {{ ansible_date_time.time }}"
# 5. 用户信息
- name: 显示当前用户
debug:
msg: "执行用户: {{ ansible_user_id }}"
# 6. 简单命令执行
- name: 执行 uptime 命令
command: uptime
register: uptime_result
- name: 显示 uptime 输出
debug:
var: uptime_result.stdout
# 1. 列出将要执行的主机(不实际执行)
ansible-playbook -i hosts first.yml --list-host

task:
# 2. 查看所有任务(不执行)
ansible-playbook -i hosts first.yml --list-task

# 3. 试运行(检查语法和逻辑)
ansible-playbook -i hosts first.yml --check

# 4. 实际执行(详细输出)
ansible-playbook -i hosts first.yml -v

# 5. 只对特定主机执行
ansible-playbook -i hosts first.yml --limit webservers

# 6. 执行并询问确认
ansible-playbook -i hosts first.yml --step
