第4章:Playbook基础:YAML语法精讲、Playbook核心结构、Tasks与Handlers、变量定义与引用
好,咱们进入正题。Playbook 是 Ansible 的灵魂,说白了,它就是你的自动化剧本。你写好了剧本,Ansible 这个演员就按你的要求去执行。这一章,我会带你从零开始,把 Playbook 的底子打扎实。
4.1 YAML 语法精讲:别让格式坑了你
Playbook 是用 YAML 写的。YAML 的语法,嗯,其实很简单,但坑也不少。我在项目中见过太多人因为缩进问题排查半天,最后发现少了个空格。
核心规则就三条:
- 缩进:用空格,别用 Tab。我习惯用两个空格缩进,清晰明了。
- 键值对:冒号后面必须跟一个空格。比如
name: 安装nginx,冒号后的空格不能省。 - 列表:用短横线加空格开头。比如
- 192.168.1.1。
重要提醒:YAML 对缩进极其敏感。同一个层级的元素,缩进必须一致。我曾经有个同事,把 tasks 写成了两格缩进,结果 Ansible 直接报语法错误,查了半小时才发现。
来看个完整的 YAML 示例:
---
# 这是一个注释
web_servers:
- host: web01
ip: 192.168.1.10
ports:
- 80
- 443
- host: web02
ip: 192.168.1.11
ports:
- 80
你看,web_servers 是一个字典,它的值是一个列表。列表里的每个元素又是一个字典。这种嵌套结构在 Playbook 里非常常见。
小技巧:我写 YAML 时,习惯用 --- 开头,表示文档开始。虽然不写也行,但加上更规范,也方便后续用工具合并文件。
4.2 Playbook 核心结构:剧本的骨架
一个标准的 Playbook,长什么样?我直接给你看个例子:
---
- name: 配置Web服务器
hosts: webservers
become: yes
vars:
http_port: 80
max_clients: 200
tasks:
- name: 确保nginx已安装
apt:
name: nginx
state: present
- name: 启动nginx服务
service:
name: nginx
state: started
enabled: yes
结构其实很清晰:
- name:这个 Play 的名字,方便你识别。我建议每个 Play 都起个有意义的名字。
- hosts:目标主机,可以是主机组名,也可以是 IP。
- become:是否提权。设为
yes表示用 root 权限执行。 - vars:变量定义区,后面会细讲。
- tasks:任务列表,这是核心中的核心。
你想想看,这个结构是不是很像一个剧本?hosts 是演员,tasks 是台词,vars 是道具。Ansible 就是导演,按你的剧本一步步执行。
4.3 Tasks 与 Handlers:任务与触发器
Tasks 就是你要执行的操作。每个 task 都有一个 name 和一个具体的模块调用。比如:
tasks:
- name: 拷贝nginx配置文件
copy:
src: /local/nginx.conf
dest: /etc/nginx/nginx.conf
notify: 重启nginx
这里有个关键点:notify。它是什么意思?
Handlers 是特殊的 tasks,它们只在被 notify 触发时才执行。而且,就算你 notify 了多次,Handler 也只会在所有 tasks 执行完后,运行一次。
为什么要这样设计?我举个例子。你在部署时,可能改了配置文件,也可能没改。如果每次拷贝都重启服务,那效率太低了。Handler 就是解决这个问题的——只有配置真正变了,才触发重启。
handlers:
- name: 重启nginx
service:
name: nginx
state: restarted
注意:Handler 的执行顺序是按它们在 handlers 中定义的顺序,而不是 notify 的顺序。我曾经踩过这个坑,以为先 notify 的会先执行,结果 Handler 是按定义顺序来的。所以,写 Handler 时,把依赖关系理清楚。
4.4 变量定义与引用:让剧本活起来
变量是 Playbook 的灵魂。没有变量,你的剧本就是死的。有了变量,一个剧本可以适配不同的环境。
变量定义的方式有很多:
- 在 Playbook 中定义:用
vars关键字。 - 在文件中定义:用
vars_files引入外部文件。 - 在主机清单中定义:在 inventory 文件里给主机或组设置变量。
- 在命令行传入:用
--extra-vars参数。
来看个综合例子:
---
- name: 部署应用
hosts: appservers
vars:
app_version: "1.2.3"
app_port: 8080
vars_files:
- "vars/{{ env }}.yml"
tasks:
- name: 打印应用版本
debug:
msg: "当前版本是 {{ app_version }},端口是 {{ app_port }}"
变量引用用双花括号 {{ }}。注意,如果变量是字符串,最好用引号包起来,避免 YAML 解析出错。
变量优先级:记住一个原则——越具体的变量,优先级越高。命令行传入的 --extra-vars 优先级最高,其次是 Playbook 中定义的 vars,然后是主机清单中的变量。我建议,通用变量放在主机清单,环境相关变量放在 vars_files,临时覆盖用 --extra-vars。
我个人习惯,把变量按环境拆分到不同文件。比如 dev.yml、prod.yml,然后在 Playbook 里用 {{ env }} 动态加载。这样,一个 Playbook 就能适配开发、测试、生产三个环境,省心省力。
4.5 实战小技巧:写 Playbook 的避坑指南
嗯,这里我要分享几个我踩过的坑:
- 缩进问题:编辑器设置显示空格,别用 Tab。我推荐 VS Code 加 YAML 插件,能自动检查缩进。
- 变量名冲突:别用
host、port这种太通用的名字,容易和 Ansible 内置变量冲突。我习惯加个前缀,比如myapp_port。 - Handler 不触发:检查一下
notify的名字是否和 Handler 的name完全一致,大小写和空格都不能错。 - 调试用 debug:不确定变量值对不对?用
debug模块打印出来看看,比猜靠谱。
我的习惯:写 Playbook 时,先用 --check 模式跑一遍,看看会执行什么操作,确认无误后再真正执行。这个习惯帮我避免了好几次生产事故。
好了,这一章的内容就到这里。YAML 语法、Playbook 结构、Tasks 和 Handlers、变量定义与引用,这四个点你掌握了,写 Playbook 的基础就算打牢了。下一章,我们会深入模块的使用,到时候你会看到,这些基础知识点是如何组合起来解决实际问题的。