变量优先级:先去测,再谈背表
一句话总结
在 Ansible 中,可以放置同名变量的位置有二十二个,与其背诵它们的顺序,减少位置的设计更能确实地防止事故。
为什么需要它
“明明在 group_vars 里把端口写成了 8080,显示的却是 9090。”这类报告几乎总是同一个形状。有人着急时在命令行加了 -e 运行,而这件事没有留在任何地方。或者 role 里有 vars/main.yml,它赢过了 play 的 vars_files。
这个问题之所以难,是因为错误的值不会以错误的形式出现。playbook 成功,文件被创建,只是内容不同。语法检查和 lint 都抓不到。能抓到的只有一件事——实际测量“此刻在这个位置上,这个名称解析成什么”。
所以本模块不去抄写官方文档的列表,而是把同一个名称一个位置一个位置地往里放,测量胜者如何变化。测量过一次的人,即使不背表,下次遇到同样的情况也知道先该怀疑什么。
工作原理
官方文档把位置从低到高排列了二十二个。实验中要测量的十二个位置,从低到高写出来是这样。
| 名次 | 位置 | 性质 |
|---|---|---|
| 1 | role defaults/main.yml |
递出去的“请在外面覆盖”的值 |
| 2 | inventory group_vars/all |
所有主机的底值 |
| 3 | inventory group_vars/<그룹>(占位符为组名) |
各组的值 |
| 4 | inventory host_vars/<호스트>(占位符为主机名) |
各主机的值 |
| 5 | play vars: |
只在这个 play 中 |
| 6 | play vars_files: |
这个 play 读入的文件 |
| 7 | role vars/main.yml |
“不要从外面碰”的 role 内部值 |
| 8 | block vars: |
只在 block 内 |
| 9 | task vars: |
只在一个 task 中 |
| 10 | set_fact |
运行中设定的值 |
| 11 | role 调用参数 | 调用 role 时传入的值 |
| 12 | 命令行 -e |
什么都能赢 |
这里让人吃惊的有三处。
第一,vars_files 比 play 的 vars: 更高。 在同一个 play 里,写在上面的 vars: 输给了下面读入的文件。这与按顺序用眼睛读的感觉相反。
第二,role 的 defaults 和 vars 正好相反。 它们在同一个 role 目录里并排放着,但 defaults 在最底层,vars 高得多。这个差别就是 role 作者的意图——可以从外面改的值放在 defaults,不能改的值放在 vars。 用别人的 role 时,如果“怎么覆盖都改不了”,那个值就在 vars/main.yml 里。
第三,范围越窄越高。 play < block < task 的顺序不是用来背的,而是规则。把它看作写在窄位置上的人有更具体的意图。
测量用的工具也值得了解。在 inventory 内部的较量,ansible-inventory --list 会以 JSON 显示已经合并好的结果。在 play 内部的较量,插入一个 debug task 运行一下最快。
ansible-inventory -i inventory --list # 인벤토리 세 자리가 합쳐진 결과
ansible-inventory -i inventory --graph # 그룹 구조
给 inventory 传目录时有个陷阱。目录中 .ini 扩展名的文件在默认设置下会被忽略(INVENTORY_IGNORE_EXTS)。用 -i 传单个文件时好好的 hosts.ini,一放进目录就消失了。如果一台主机都看不到,先怀疑这一点。
在现场相遇的样子
第一,字典不会合并。 在低位置放 svc_limits: {cpu: "1", memory: 1Gi},在高位置给出 svc_limits: {memory: 2Gi},结果是 {memory: 2Gi}。cpu 消失了。想合并就必须用 combine filter 明确指出。也可以用全局设置 hash_behaviour = merge 来改,但那个设置会改变整个仓库的行为,所以官方文档也不推荐。
第二,-e 无法撤销。 命令行的值比任何位置都高,在 playbook 内部没有覆盖它的办法。用着方便就开始频繁使用,最终“谁在什么时候给了什么”会从记录里消失。只用于事故应对,并且需要团队规则来留下使用过的事实。
第三,减少位置的设计。 比了解表更强的防御,是减少可放同名变量的位置。常见的规则有三条——按环境区分的值只放在 inventory 的 group_vars 一处,role 只把可以从外面改的值放在 defaults 中,-e 只在例外情况下使用。遵守这三条,即使不知道优先级表,也几乎不会出事故。
第四,vars_prompt。 也有运行时向人询问并接收的位置(在 play 的 vars: 和 vars_files: 之间)。在自动化流水线中运行会停住,所以要进入 CI 的 playbook 不要使用。本实验也是自动评分,所以不涉及。
下一项实验要做什么
把 svc_tier 这一个名称,一个位置一个位置地放进十二个位置,亲自测量胜者如何变化。inventory 的三个位置用 ansible-inventory --list,其余的运行 playbook 并用产出物确认。最后把测得的顺序留成人能读懂的表,并排确认字典不会合并,以及用 combine 合并的情形。