把清单挪进目录后,组只剩下一半
目标
逐一实测,当 inventory 来源有多个时,什么会被合并,什么会获胜。最后亲手创建一个回答“这个变量到底哪个值获胜”的工具。
为什么重要
inventory 只有一个文件时,读一读就明白了。但只靠一个文件的组织很少——公共主机由平台团队管理,各服务的主机由各团队管理,生产和预发布把文件分开,云资源由动态来源生成。这样事故就分成两种。因为同名的组,不该有的服务器进入了对象,以及因为旧文件或新的 group_vars,不该获胜的值获胜了。这两种都无法用读文件来推断的方式防止。防止的方法只有一个——在运行之前,向机器询问合并后的结果。本实验会把这个问题问八遍,最后留下一个替你来问这个问题的工具。
步骤
- 在
/root/ans/inv/src/base.ini中写入web组(web1、web2)和[web:vars] tier=base,在/root/ans/inv/src/extra.ini中写入web组(web3)、db组(db1)和[web:vars] tier=extra。所有主机都是ansible_host=127.0.0.1,并在base.ini的[all:vars]中设置ansible_port=2222和ansible_user=root。给出两次-i(base.ini在前),把合并后的结果保存到/root/ans/inv/out/merged.json,把图保存到/root/ans/inv/out/merged-graph.txt,并把合并之后web1的tier是什么,以tier=<값>(占位符为值)一行写入/root/ans/inv/out/two-sources.txt。 - 创建
/root/ans/inv/dir/,并把与第 1 步两个文件相同的内容,不带扩展名地分别放入10-base和20-extra。再在其中放一个/root/ans/inv/dir/30-late.ini,这个文件在web组中放入web9,并写入[web:vars] tier=late。把用-i /root/ans/inv/dir合并后的结果保存到/root/ans/inv/out/dir-merged.json,并在/root/ans/inv/out/dir-order.txt中写两行——第一行是web1的tier,写成tier=<값>(占位符为值),第二行是遍历目录时被跳过的文件名,写成skipped=<파일이름>(占位符为文件名)。 - 在
/root/ans/inv/dir/group_vars/all.yml中写入pool: gv-all和owner: platform,在/root/ans/inv/dir/group_vars/web.yml中写入pool: gv-web,在/root/ans/inv/dir/host_vars/web1.yml中写入pool: hv-web1。然后在/root/ans/inv/out/levels.json中,用web1、web2、db1三个键的 JSON,写出这三台主机最终拥有的pool值。 - 在
/root/ans/inv/dir/20-extra的[web:vars]中加入region=inline-extra,并在/root/ans/inv/dir/group_vars/web.yml中加入region: file-web。然后在/root/ans/inv/out/inline-vs-file.txt中写两行——第一行是web2最终拥有的值,写成region=<값>(占位符为值),第二行是该值来自哪里,写成source=inline或source=group_vars之一。 - 创建
/root/ans/inv/prio/hosts.ini——主机app1同时属于alpha和zulu两个组,在[alpha:vars]中写入stack=from-alpha,在[zulu:vars]中写入stack=from-zulu。然后在[alpha:vars]中加入ansible_group_priority=10,让alpha获胜。在/root/ans/inv/out/priority-before.txt中写没有那一行时获胜的值,在/root/ans/inv/out/priority-after.txt中写有那一行时获胜的值,各写成一行stack=<값>(占位符为值)。 - 创建
/root/ans/inv/tree/hosts.ini——在web组中放入web1,在db组中放入db1,并把两个组绑定为zprod的子组。在[zprod:vars]中写入env=from-zprod和owner=platform,在[web:vars]中写入env=from-web。然后在/root/ans/inv/out/children.txt中按字典序原样写三行——db1.env=<값>·web1.env=<값>·web1.owner=<값>(占位符均为值)。 - 创建
/root/ans/inv/pat/hosts.ini——在web组中放入web1、web2、web3,在db组中放入db1,把两个组绑定为prod的子组,并在staging组中放入web3和db1。然后只把匹配模式web:&prod:!staging的主机名,每行一个、按字典序保存到/root/ans/inv/out/pattern-hosts.txt,并把图保存到/root/ans/inv/out/pat-graph.txt。 - 编写
/root/ans/inv/whowins.sh。它接收三个参数——inventory 路径、主机名、变量名。它要把该主机最终拥有的值输出为一行<변수>=<값>(占位符依次为变量与值),如果该变量没有定义,就要把原因写到标准错误,并以非 0 的值结束。用这个工具创建/root/ans/inv/out/merge-report.txt的四行——顺序是dir中web1的tier、dir中web1的pool、dir中web2的region、prio/hosts.ini中app1的stack。
参考
- 工作目录是
/root/ans/inv。本实验不运行 playbook——只使用解析 inventory 的命令。 - 三个问题:
--graph回答结构,--list以 JSON 回答全部,--host回答一台主机的最终变量。 - 常见错误:在目录内放了带扩展名的 inventory 文件,却不知道为什么读不到而折腾。
- 常见错误:用眼睛在文件里找值然后写下来。来源有两个以上时,用眼睛会出错。
- 常见错误:模式的结果为空,却因为不是错误,而在没有任何对象的情况下以绿灯结束。
- 创建 inventory · 主机模式 · ansible-inventory 命令 · 使用变量 · 配置项列表
给出两个来源,什么会被合并
在 /root/ans/inv/src/base.ini 中写入 web 组(web1、web2)和 [web:vars] tier=base,在 /root/ans/inv/src/extra.ini 中写入 web 组(web3)、db 组(db1)和 [web:vars] tier=extra。所有主机都是 ansible_host=127.0.0.1,并在 base.ini 的 [all:vars] 中设置 ansible_port=2222 和 ansible_user=root。给出两次 -i(base.ini 在前),把合并后的结果保存到 /root/ans/inv/out/merged.json,把图保存到 /root/ans/inv/out/merged-graph.txt,并把合并之后 web1 的 tier 是什么,以 tier=<값>(占位符为值)一行写入 /root/ans/inv/out/two-sources.txt。
主机和组取并集,同名变量由后读取的来源获胜。查看合并结果的命令有 --list 和 --graph 两种,只想看一台主机的最终变量,则使用 --host。不要靠猜测来写值,请实际询问之后再写——评分器也会用同样的方法重新计算并核对。
给出目录时,文件名决定顺序
创建 /root/ans/inv/dir/,并把与第 1 步两个文件相同的内容,不带扩展名地分别放入 10-base 和 20-extra。再在其中放一个 /root/ans/inv/dir/30-late.ini,这个文件在 web 组中放入 web9,并写入 [web:vars] tier=late。把用 -i /root/ans/inv/dir 合并后的结果保存到 /root/ans/inv/out/dir-merged.json,并在 /root/ans/inv/out/dir-order.txt 中写两行——第一行是 web1 的 tier,写成 tier=<값>(占位符为值),第二行是遍历目录时被跳过的文件名,写成 skipped=<파일이름>(占位符为文件名)。
目录内的文件按名称顺序读取。但三个当中有一个读取不到——看看它想放入的主机在结果中有没有,马上就知道了。请用 ansible-config list 确认配置项 inventory_ignore_extensions 的默认列表。只写文件名(不是路径)。
组变量和主机变量形成的三个层次
在 /root/ans/inv/dir/group_vars/all.yml 中写入 pool: gv-all 和 owner: platform,在 /root/ans/inv/dir/group_vars/web.yml 中写入 pool: gv-web,在 /root/ans/inv/dir/host_vars/web1.yml 中写入 pool: hv-web1。然后在 /root/ans/inv/out/levels.json 中,用 web1、web2、db1 三个键的 JSON,写出这三台主机最终拥有的 pool 值。
这些目录必须位于 inventory 旁边才会被读取。按从宽到窄的顺序覆盖,窄的获胜——对所有人生效的、只对该组生效的、只对该主机生效的。不要靠手工推断这三个值,请对每台主机分别询问之后再写。
inventory 文件内部的组变量最弱
在 /root/ans/inv/dir/20-extra 的 [web:vars] 中加入 region=inline-extra,并在 /root/ans/inv/dir/group_vars/web.yml 中加入 region: file-web。然后在 /root/ans/inv/out/inline-vs-file.txt 中写两行——第一行是 web2 最终拥有的值,写成 region=<값>(占位符为值),第二行是该值来自哪里,写成 source=inline 或 source=group_vars 之一。
即使在同一个目录中,这两个位置的强度也不同。在官方优先级列表中,inventory file group vars 和 inventory group_vars 哪一个在下面,就是答案。要用 web2 而不是 web1 来询问,才不会被主机变量遮住。
相同深度的组按字典序,以及颠倒它的旋钮
创建 /root/ans/inv/prio/hosts.ini——主机 app1 同时属于 alpha 和 zulu 两个组,在 [alpha:vars] 中写入 stack=from-alpha,在 [zulu:vars] 中写入 stack=from-zulu。然后在 [alpha:vars] 中加入 ansible_group_priority=10,让 alpha 获胜。在 /root/ans/inv/out/priority-before.txt 中写没有那一行时获胜的值,在 /root/ans/inv/out/priority-after.txt 中写有那一行时获胜的值,各写成一行 stack=<값>(占位符为值)。
没有旋钮时的默认规则是名称——按字典序合并,后面的获胜。要确认这个值,只需要暂时创建一个去掉那一行的副本来询问即可(原件保持不动)。这个变量必须写在 inventory 来源内部才会生效。写在 group_vars 文件中,什么都不会发生。
子组胜过父组
创建 /root/ans/inv/tree/hosts.ini——在 web 组中放入 web1,在 db 组中放入 db1,并把两个组绑定为 zprod 的子组。在 [zprod:vars] 中写入 env=from-zprod 和 owner=platform,在 [web:vars] 中写入 env=from-web。然后在 /root/ans/inv/out/children.txt 中按字典序原样写三行——db1.env=<값> · web1.env=<값> · web1.owner=<값>(占位符均为值)。
父组的名称是故意起成字典序靠后的——这样才能显现出获胜的原因是深度而不是名称。父组宽、子组窄,始终是窄的获胜。子组没有定义的变量,会原样沿用父组的值。三个值都请询问之后再写。
用模式缩小对象,并用眼睛确认
创建 /root/ans/inv/pat/hosts.ini——在 web 组中放入 web1、web2、web3,在 db 组中放入 db1,把两个组绑定为 prod 的子组,并在 staging 组中放入 web3 和 db1。然后只把匹配模式 web:&prod:!staging 的主机名,每行一个、按字典序保存到 /root/ans/inv/out/pattern-hosts.txt,并把图保存到 /root/ans/inv/out/pat-graph.txt。
模式符号有并集、交集、差集三个。有一个只列出对象列表的选项,它输出的第一行是个数,而不是主机名。空白也会一起进来,所以请整理之后再保存。差集即使结果为空也不是错误,所以养成运行之前用眼睛数一数的习惯很重要。
这个变量到底哪个值获胜——做成工具
编写 /root/ans/inv/whowins.sh。它接收三个参数——inventory 路径、主机名、变量名。它要把该主机最终拥有的值输出为一行 <변수>=<값>(占位符依次为变量与值),如果该变量没有定义,就要把原因写到标准错误,并以非 0 的值结束。用这个工具创建 /root/ans/inv/out/merge-report.txt 的四行——顺序是 dir 中 web1 的 tier、dir 中 web1 的 pool、dir 中 web2 的 region、prio/hosts.ini 中 app1 的 stack。
如果用 grep 在文件里找答案,group_vars、组优先级、子组都看不到。请直接询问给出合并结果的命令。要区分“没有值”和“值是空字符串”,必须先确认键是否存在。评分器也会用它自己创建的 inventory 来运行这个工具,并检查询问未定义变量时如何结束。