变量优先级与 facts 的处理
目标
将数值从代码中分离并放到不同位置,亲自确认发生冲突时哪个值优先。同时处理通过 fact 和 register 在运行过程中取得的值。
为什么重要
“变量改了却没有生效”这个问题,答案几乎总是优先级。Ansible 允许从多个位置接收同名变量,即使发生冲突也不会报错,而是静默采用优先级更高的值。这种特性虽然方便,却让调试更困难。因此需要具备这样的设计意识:允许覆盖的值放在弱优先级位置(role 的 defaults),必须严格保持的值放在强优先级位置。命令行 -e 可以覆盖一切,但它不会记录在任何地方;如果在故障处理中使用了它,就必须把变更补回代码,否则下一次部署会静默删除该值。
步骤
- 创建
/root/ans/vars/site.yml,并在 play 的vars:中定义app_name: checkout。将该值写入/root/ans/vars/out/app_name.txt。 - 在
/root/ans/vars/vars/common.yml中设置app_port: 9090,并通过vars_files读取。将值写入/root/ans/vars/out/app_port.txt。 - 在
/root/ans/inventory/group_vars/web.yml中设置node_role: frontend,在/root/ans/inventory/host_vars/web1.yml中设置node_role: canary。这两个目录必须与第一个实验创建的/root/ans/inventory/hosts.ini位于同一位置,才能自动读取。web2 应显示 frontend,web1 应显示 canary。 - 执行命令并用
register接收结果,然后只把标准输出保存到/root/ans/vars/out/uptime.txt。 - 启用
gather_facts,把收集到的 fact 中的主机名、架构和系统保存为 JSON 文件/root/ans/vars/out/facts.json。 - 使用
-e app_port=9999运行,并把该值保存到/root/ans/vars/out/precedence.txt。第 2 步创建的app_port.txt必须仍为 9090。 - 使用
set_fact拼接app_name和app_port,将结果保存到/root/ans/vars/out/endpoint.txt,格式为checkout:9090。 - 创建
/root/ans/vars/out/summary.json,其中必须有四个键:app(=checkout)、port(=9090)、role(web1 的主机变量值)和host(所收集 fact 中的主机名)。
参考
- 每个实验都会启动全新的实验 Pod。如果
/root/ans/inventory/hosts.ini不存在,请先重新创建与第一个实验相同的 inventory(web1·web2·db1,ansible_host=127.0.0.1、ansible_port=2222、ansible_user=root,并在[prod:children]中加入 web·db)。结构可以参考/opt/lab/fixtures/ansible/inventory.sample.ini。 - 使用
ansible-inventory -i /root/ans/inventory/hosts.ini --list可以查看组变量与主机变量实际如何合并。 - 可通过
debug模块的var:立即查看某个变量的最终值。 - 常见错误 1:第 4 步把整个 register 对象写入文件。只需要
.stdout。 - 常见错误 2:第 6 步覆盖同一个文件,导致前一步结果丢失。每一步都应保存到不同文件。
定义并使用 play 变量
创建 /root/ans/vars/site.yml,并在 play 的 vars: 中定义 app_name: checkout。将该值写入 /root/ans/vars/out/app_name.txt。
把值放在 play 的 vars: 下,并在 copy 模块的 content 中通过 Jinja2 引用。
拆分变量文件
在 /root/ans/vars/vars/common.yml 中设置 app_port: 9090,并通过 vars_files 读取。将值写入 /root/ans/vars/out/app_port.txt。
vars_files: 接受路径列表。变量增多后,通常会从 play 中拆分到单独文件。
创建组变量和主机变量
在 /root/ans/inventory/group_vars/web.yml 中设置 node_role: frontend,在 /root/ans/inventory/host_vars/web1.yml 中设置 node_role: canary。这两个目录必须与第一个实验创建的 /root/ans/inventory/hosts.ini 位于同一位置,才能自动读取。web2 应显示 frontend,web1 应显示 canary。
目录名称本身就是约定。group_vars 下的文件名对应组名,host_vars 下的文件名对应主机名。
将命令结果保存到变量
执行命令并用 register 接收结果,然后只把标准输出保存到 /root/ans/vars/out/uptime.txt。
register 得到的是一个对象。文件中只应写入其中的标准输出。
收集 fact 并保存为 JSON
启用 gather_facts,把收集到的 fact 中的主机名、架构和系统保存为 JSON 文件 /root/ans/vars/out/facts.json。
启用 gather_facts,从 ansible_facts 中选择所需部分并写成 JSON。to_nice_json 过滤器会有帮助。
使用命令行变量覆盖
使用 -e app_port=9999 运行,并把该值保存到 /root/ans/vars/out/precedence.txt。第 2 步创建的 app_port.txt 必须仍为 9090。
通过 -e 传入的值优先级最高。不要覆盖前一步保存的文件,请使用另一个文件名。
使用 set_fact 组合值
使用 set_fact 拼接 app_name 和 app_port,将结果保存到 /root/ans/vars/out/endpoint.txt,格式为 checkout:9090。
把两个变量拼成一个新值并写入文件,格式为 이름:포트。
汇总变量、fact 与主机变量
创建 /root/ans/vars/out/summary.json,其中必须有四个键:app(=checkout)、port(=9090)、role(web1 的主机变量值)和 host(所收集 fact 中的主机名)。
创建包含四个键(app、port、role、host)的 JSON。role 来自 web1 的主机变量,host 来自收集到的 fact。