一、 核心定位:剧本 vs. 临时命令
要理解 Playbook,首先要区分它与 Ad-Hoc 命令(如 ansible all -m ping)的区别:
-
Ad-Hoc 命令:适合临时、一次性的任务(如重启服务、查看进程)。是一条命令。
-
Playbook :适合复杂、重复、需要长期维护的任务。它是一个脚本文件。
核心优势:
-
幂等性:这是 Playbook 最重要的特性。意思是,同一个 Playbook 执行 1 次和执行 10 次的结果应该是一样的。只有状态不符合定义时才会修改,符合定义时则跳过。这保证了服务器配置不会因为重复执行而错乱。
-
结构化:像代码一样管理基础设施(IaC)。
-
编排:可以定义复杂的执行流程(先配置 Web,后配置 DB,发邮件通知等)。
二、 语法基础:YAML
Playbook 严格遵循 YAML 语法。
关键点:
-
缩进 :必须使用空格,不能使用 Tab。通常缩进为 2 个空格。
-
短横线 :
-用于表示列表(List/Array)。 -
冒号 :
:用于表示字典(Key/Value)。冒号后面必须跟一个空格。
三、 Playbook 的核心构成(重点)
一个 Playbook 文件通常包含一个或多个 Play 。每个 Play 对应在一组主机上执行的一系列 Task(任务)。
1. Play 定义
每个 Play 通常由以下几个顶级关键字构成:
| 关键字 | 作用 | 示例 |
|---|---|---|
name |
描述这个 Play 的作用(虽然不是强制,但必须写,用于日志输出) | name: Install and configure web server |
hosts |
目标主机。指定哪些主机执行该 Play。 | hosts: webservers (对应 inventory 中的组) |
become |
权限提升。是否需要 sudo 或 root 权限。 | become: yes |
vars |
定义该 Play 内使用的变量。 | vars: http_port: 80 |
tasks |
核心。要执行的具体任务列表。 | 见下文 |
handlers |
特殊任务。只在被通知时触发(通常用于重启服务)。 | 见下文 |
2. Tasks (任务)
Tasks 是一个列表,从上到下按顺序执行。每个 Task 必须包含一个 模块 的名称。
Task 的结构:
yaml
tasks:
- name: 确保 Nginx 已安装 # 任务名称
ansible.builtin.yum: # 使用的模块
name: nginx
state: present
# 注意:模块的参数是缩进在模块名下面的
关键概念:幂等性
-
模块必须支持幂等性。
-
例如:
state: present。如果 Nginx 已经安装了,这个 Task 会返回ok(绿色)且不会再次安装。
3. Handlers (触发器)
Handlers 与普通 tasks 类似,但只有在被 task 通知时才会执行 ,并且只在所有 tasks 执行完毕后的最后执行。
典型场景:配置文件变了,才重启服务。
yaml
tasks:
- name: 更新 Nginx 配置
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: 重启 Nginx # 通知 handler
handlers:
- name: 重启 Nginx
ansible.builtin.service:
name: nginx
state: restarted
- 注意 :如果配置文件没变,
notify不会被触发,Nginx 就不会被重启。这避免了不必要的服务中断。
四、 变量 (Variables) ------ 灵活性的核心
Playbook 中的变量优先级非常复杂,但必须掌握以下几种常用定义方式:
-
Play 内定义 (
vars):yaml
vars: package_name: nginx -
文件引用 (
vars_files):yaml
vars_files: - vars/redhat.yml # 引入外部变量文件 -
主机清单定义 (Inventory):
ini
[webservers] 192.168.1.10 http_port=8080 -
注册变量 (Register):捕获命令执行结果,供后续任务使用。
yaml
tasks: - name: 查看文件内容 ansible.builtin.shell: cat /etc/hostname register: hostname_result - name: 打印结果 ansible.builtin.debug: msg: "主机名是 {{ hostname_result.stdout }}"
五、 控制流程 (重点)
Playbook 不仅仅是顺序执行,还支持复杂的逻辑。
1. 条件判断 (when)
极其常用,用于区分操作系统版本、判断变量是否存在等。
yaml
tasks:
- name: 在 CentOS 上安装 httpd
ansible.builtin.yum:
name: httpd
when: ansible_facts['os_family'] == "RedHat"
- name: 在 Ubuntu 上安装 apache2
ansible.builtin.apt:
name: apache2
when: ansible_facts['os_family'] == "Debian"
2. 循环 (loop)
取代了旧版的 with_items。
yaml
tasks:
- name: 创建多个用户
ansible.builtin.user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- charlie
3. 块 (Blocks)
用于组合 tasks,进行分组或异常处理(类似编程中的 try-catch-finally)。
yaml
tasks:
- block:
- name: 安装软件包
ansible.builtin.yum:
name: "{{ package }}"
- name: 复制配置文件
ansible.builtin.template:
src: app.conf.j2
dest: /etc/app.conf
rescue:
- name: 如果上面报错,执行回滚
ansible.builtin.debug:
msg: "安装失败,执行清理"
always:
- name: 无论是否报错,最后都会执行
ansible.builtin.debug:
msg: "任务结束"
六、 模板 (Templates) ------ Jinja2
Playbook 通常结合 Jinja2 模板引擎来动态生成配置文件。
-
文件后缀通常为
.j2。 -
使用
template模块。
模板示例 (nginx.conf.j2):
jinja2
# 动态渲染端口
server {
listen {{ http_port }};
server_name {{ server_name }};
# 循环渲染
{% for item in whitelist_ips %}
allow {{ item }};
{% endfor %}
}
Playbook 调用:
yaml
- name: 部署动态配置
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: 重启 Nginx
七、 复用与组织 (Roles)
当 Playbook 变得庞大(超过几百行)时,必须使用 Roles(角色)。这是生产环境的标准实践。
Roles 是一种文件目录结构,它将变量、任务、处理器、模板等自动组合在一起。
目录结构:
text
roles/
common/ # 角色名
tasks/ # 包含 main.yml
main.yml
handlers/ # 包含 main.yml
templates/ # .j2 文件
files/ # 普通文件
vars/ # 优先级较高的变量
defaults/ # 优先级最低的默认变量
webserver/ # 另一个角色
...
Playbook 引用 Roles:
yaml
- name: 应用配置
hosts: all
roles:
- common # 先执行通用角色
- webserver # 再执行 Web 角色
八、 执行与调试
1. 执行命令
bash
# 检查语法 (强烈推荐)
ansible-playbook site.yml --syntax-check
# 模拟执行 (Dry Run),查看会改变什么,但不实际执行
ansible-playbook site.yml --check
# 执行并询问 sudo 密码
ansible-playbook site.yml --ask-become-pass
# 指定标签执行
ansible-playbook site.yml --tags "nginx"
2. 调试技巧
-
debug模块:打印变量。yaml
- name: 打印变量 ansible.builtin.debug: var: my_var -
ansible.builtin.setup:获取目标主机的 Facts(IP、OS、内存等)。 -
-v/-vvv:增加输出详细程度,-vvv可以看到 SSH 传输的原始数据。
九、 总结:优秀 Playbook 的编写原则
-
幂等性第一 :确保同一个脚本运行多次结果唯一。避免使用
shell/command模块做那些"非幂等"的操作(如mkdir),优先使用file模块。 -
命名清晰 :所有
name字段都要写得清晰明了,因为执行时会打印出来,方便排查日志。 -
利用 Facts :尽量使用
ansible_facts来适配不同的操作系统,而不是写死逻辑。 -
版本控制:Playbook 是代码,必须放在 Git 仓库中管理。
-
分层结构:
-
简单的几个任务 -> Playbook 文件。
-
复杂的应用栈 -> Roles。
-
如果你刚入门,建议先手写一个最简单的 Playbook(例如:安装并启动 Nginx),然后逐步引入变量、Handlers 和 Templates,最后再学习 Role 的组织方式。