Ansible Playbook 与变量体系实战
Ansible Playbook 与变量体系实战
ad-hoc 命令解决”临时干一票”的问题,但真正要重复用的东西必须落成 playbook。我写第一个剧本时的最大困惑不是语法,而是变量:同一个值,写在剧本里、写在清单里、用 -e 传进来,到底谁说了算?这篇把 playbook 的基础和变量体系一起理清楚——变量搞明白了,剧本才谈得上复用。
一、Playbook 是什么
Playbook(剧本)是 Ansible 的编排文件,核心就两个概念:
- play:定义”在哪批主机上执行”,对应
hosts; - task:定义”具体做什么”,一条 task 调一个模块。
一个 playbook 可以包含多个 play,一个 play 可以包含多个 task。和 ad-hoc 的区别:ad-hoc 是一条命今一次执行,playbook 有明确的执行顺序和依赖关系,而且可以持久保存、重复执行。
二、YAML 语法速记
剧本用 YAML 写,语法规则不多,但每条都必须严守,而且错得悄无声息(缩进错了直接语法报错,写错了往往不报错但行为不对):
- 只用空格缩进,绝对不能用 Tab;
- 同层级左对齐,一层缩进 2 个空格;
- 冒号后面必须有一个空格:
name: wget,写name:wget会解析成字符串; - 大小写敏感,Ansible 的关键字全是小写;
- 注释用
#; - 开头
---标识 YAML 文档。
两种核心结构:字典(键值对)和列表(- 开头)。写多行内容用 | 保留换行,配配置文件时常用:
1 | - name: 写入 nginx 默认站点配置 |
三、第一个剧本与语法检查
1 |
|
写完先别执行,跑一遍语法检查:
1 | ansible-playbook --syntax-check install_base.yml |
--syntax-check 只检查语法不连远端,顺手还能用 -C(干跑)模拟执行看变更范围。这两个参数我建议当成剧本的”编译检查”来用,尤其是改高危剧本的时候。
四、变量定义在哪里,谁说了算
变量定义的位置有四种,优先级从低到高:
1 | Inventory 文件里的变量 < playbook 里的 vars / vars_files < 命令行 -e 传入 |
最常见的是 playbook 里定义:
1 | # 方式一:列表变量,适合一组软件包 |
1 | # 方式二:vars_files 外部文件,多个剧本共享 |
vars_files 是比 vars 更工程化的做法——同一个变量文件可以被多个剧本引用,改版本号只改一处。再进一步就是分层变量:
1 | # vars_file.yml 里按业务分层 |
1 | # 引用时两种写法,推荐后者(官方写法,键名有特殊字符也不怕) |
命令行传入变量用于临时覆盖(换环境、换版本):
1 | ansible-playbook deploy.yml -e "web_server=nginx" -e "db_server=mariadb-server" |
-e 的优先级最高,适合”同一套剧本多环境复用”:剧本里写默认值,执行时按环境覆盖。但要注意它同时也是最容易埋雷的——传错一个变量,剧本会静默用错值跑完,所以我只在明确的场景用,日常变量一律落在文件里。
五、魔法变量:跨主机协作的钥匙
魔法变量和 Inventory 强相关,做批量分组、跨主机协作全靠它们。生产上最常用的五个:
1 | # 1. inventory_hostname:主机在清单里的名字,做唯一标识 |
hostvars 有一个使用前提:目标主机的 facts 得先采集过。如果 play 里关了 gather_facts,跨主机取到的值可能是空的——这个坑我第一次用模板生成 upstream 时踩得很实,排查半天才发现是 facts 没采。
六、facts:系统信息自动采集
剧本默认会执行 gather_facts,把被控端信息采集到 ansible_facts 里。巡检、按硬件差异化生成配置,都靠它:
1 | - debug: |
几个高价值的场景:
1 | # 1. 按系统大类分发包管理器(RedHat 系用 yum,Debian 系用 apt) |
这里有个常见误区:Ubuntu 的 os_family 是 Debian,不是 Ubuntu。判断系统家族时先想清楚这层关系,条件写错了,剧本在 Ubuntu 上直接跳过安装步骤。
1 | # 2. 按内存大小生成配置:模板里直接算 |
同样一套 MySQL 剧本,撒到 2G、4G、16G 的机器上,配置文件自动适配——这就是”事实驱动”的威力。不用为每种规格写一份剧本。
七、变量注册:把执行结果接出来
模块执行完的返回值想复用,就用 register 接住:
1 | - hosts: web_group |
技巧在于 stdout_lines:直接 msg: "{{ list_dir }}" 会带一堆 return code、耗时之类的杂项,stdout_lines 只取命令输出并按行拆好。之后配合 when: list_dir.rc == 0 之类的判断,就能做出”命令失败则跳过后续”的逻辑。
八、facts 缓存与执行优化
facts 采集需要额外的时间。机器多的时候有两个选择:
1 | # 选项一:这个 play 不需要系统信息,直接关掉 |
1 | # 选项二:缓存 FACTS,一段时间内复用(生产大环境推荐) |
判断标准很简单:剧本里用不到 ansible_facts 就关掉;用得到但机器多,就用缓存。
九、变量优先级的记忆法
Ansible 的完整优先级列表有二十多条,背是不现实的。日常工作记住三个原则就够用:
- 就近原则:越”贴近这台主机”的定义优先级越高——单机变量 > 组变量 > 全局变量;
- 外部压内部:命令行
-e永远最高,用来做环境覆盖; all就是全局默认值:group_vars/all.yml是所有机器的兜底。
调试变量时有个必会动作:ansible-inventory -i inventories/prod/hosts --host db01,会把这台机器生效的全部变量、以及从哪层继承的,完整列出来。变量不生效的时候,先跑这条命令,比猜快得多。
小结
Playbook 的入门门槛在 YAML 语法,真正拉开差距的是变量。把”会变的东西”从剧本里赶出去——版本号进变量文件、环境差异进 group_vars、临时覆盖走 -e——剧本才能从”这台机器能跑”变成”哪台机器都能跑”。


