清单 — 自动化的通讯录
一句话总结
Ansible 在问“做什么”之前,先问“对谁做”。答案就是 inventory。
为什么需要它
三台服务器时,可以打开三个终端;七台时,就会发生选错窗口的事故。从 home lab 由 3 节点扩到 7 节点的记录可以看到,添加节点本身并不困难,错误从**人开始记忆“在哪个节点做过什么”**的瞬间出现。新加入的两个节点还残留旧集群内容(旧版 kubelet、旧证书),手工逐台清理时总会漏掉一台。
inventory 把问题反转为“把清单写进文件”。文件中没有的服务器不属于自动化对象,文件中列出的服务器全部接受相同处理。这条简单规则从结构上消除了“只有一台配置不同”的情况。
如何工作
inventory 包含三种内容。
| 元素 | 是什么 | 示例 |
|---|---|---|
| host | 一个连接目标 | web1 |
| group | host 集合 | [web]、[db] |
| variable | 绑定到 host/group 的值 | ansible_port=2222、app_port=8080 |
关键是group 可以形成层级。在 [prod:children] 下放入 web 与 db,prod 就包含两个 group 的所有 host;每个 host 还会自动属于 all group。变量从上向下传递,但范围越窄优先级越高——group 值覆盖 all,host 值再覆盖 group。
格式有 INI 与 YAML 两种。INI 简短,适合手写;YAML 适合嵌套结构与复杂变量。无论使用哪种,通过 ansible-inventory --list 输出后最终都是同一 JSON。怀疑 inventory 时,不要盯着文件看,而要用这条命令查看真实解析结果。 一个拼写错误让整个 group 变空,三秒内就能发现。
在实际项目中
第一,“为什么只有一台没执行”。 有人缩小目标时不用 --limit,而是暂时从 inventory 删除 host。结果该文件被直接提交,几天后只有这台服务器漏掉补丁。缩小目标应使用执行参数,而不是修改 inventory。
第二,连接信息也是变量。 ansible_host、ansible_port、ansible_user 看似特殊,其实仍是普通变量。因此,可以在 inventory 中吸收跳板机、非标准端口等环境差异,无需修改代码。本实验的 sshd 运行在 127.0.0.1:2222,也通过 ansible_port 表达差异。
第三,动态 inventory。 云环境中的服务器清单每天变化,因此会由脚本生成,而不是写死在文件中。但概念相同——脚本只是在生成包含 group 与 variable 的 JSON。理解静态 inventory 后,动态 inventory 只是输出形式不同的同一种东西。
变量应该放在哪里
把大量变量写进 inventory 文件很快会难以阅读,所以实务中会把变量移到独立目录。在 inventory 旁放置 group_vars/ 与 host_vars/ 后,group_vars/web.yml 应用于整个 web group,host_vars/web1.yml 只应用于该 host。文件名就是作用范围,不必猜该改哪里。
优先级仍遵循范围越窄越强。完整规则很长,常见部分如下。
- 命令行
-e(最强) - 直接写在 task 或 block 中的值
host_vars/group_vars/(子 group 覆盖父 group)group_vars/all- role 默认值(
defaults/,最弱)
role 默认值最弱是刻意设计。role 只负责表达“没有提供这个值时,我使用这个默认值”,调用方应始终可以覆盖。 相反,role 内部 vars/ 优先级高、难以覆盖,只应用于确实不能变化的值。
再补充一条实务规则:不要在 inventory 中明文保存秘密。 一旦提交进存储库就无法真正撤回,删除后仍留在历史中。可以使用加密变量文件或外部秘密存储,但无论选择哪种,都应把秘密值与普通值拆分到不同文件。混在一起后,每改一个普通设置都要解密,久而久之大家会放弃加密。
下一实验要做什么
在 /root/ans/inventory/hosts.ini 中创建 web、db group 及上层 prod group,再用 YAML 表示相同结构。随后用 ad-hoc 命令一次连接三个 host,并通过 --limit 缩小目标。最后把 inventory 输出为 JSON,生成按 group 分类的 host 报告。