TT Lab
开始
学习 学习路径 课程

基础设施即代码

周五晚上手动改的规则,周一的 apply 想把它删掉

在 TT Lab 中继续学习

目标

用 plan -refresh-only 单独读取在代码之外产生的变更,在计划 JSON 中找出原样应用就会消失的内容,亲手完成吸收、回退、排除这三种处理,并创建每天运行的检测脚本。

为什么重要

声明式工具以代码为真相,所以即使是在控制台中做的正确修改,也会在下一次应用时被改回去。因此发现漂移后,必须先读出将要消失的内容,再由人来决定是吸收还是回退。ignore_changes 并不是能隐藏所有漂移的开关,provider 不读取的属性发生变更,计划中根本不会出现。只有了解这一局限,才能在检测结果为空时分清“一致”和“看不见”。

步骤

  1. 在 /root/iac-drift/main.tf 中放置 rules 变量(默认值 ["allow 443"])、按每条规则一行写入 out/firewall.rules 的 local_file.fw,以及向 out/motd.txt 写入一行 maintenance window: sun 02:00 的 local_file.motd,然后执行 init·apply。
  2. 在故障响应过程中,有人手工在 /root/iac-drift/out/firewall.rules 末尾加了一行 allow 8443(自己加上)。不要动代码,用 tofu plan -refresh-only 只查看在外部产生的变更,把输出保存到 /root/iac-drift/drift.txt。
  3. 不要修改代码,用 tofu plan -out=/root/iac-drift/monday.tfplan 保存计划(不要应用)。把该计划将要写入 firewall.rules 的内容与当前文件比较,只把应用后会消失的行写入 /root/iac-drift/lost.txt。
  4. 判断这次紧急修改是正确的。把 rules 的默认值改为 ["allow 443", "allow 8443"] 并 apply。之后 firewall.rules 必须是两行,且计划必须是干净的。
  5. 这次有人把 out/motd.txt 改成了 maintenance window: none(自己改)。这是错误的变更。在回退之前,把 tofu plan -refresh-only 的输出保存到 /root/iac-drift/revert-drift.txt,然后保持代码不变,执行 apply,把公告改回代码中的内容。
  6. 在 main.tf 中加入写入 out/cache.conf 的 cache v1、并设置了 lifecycle { ignore_changes = [content] } 的 local_file.cache,然后 apply。(a)用手把文件改成 cache tampered 后,把 tofu plan 的输出保存到 /root/iac-drift/ignore.txt,并用 apply 改回去。(b)把代码中的内容改为 cache v2,查看计划是否干净(不要应用)。(c)执行 chmod 600 out/firewall.rules 之后,把 tofu plan -detailed-exitcode 的退出码用 perm=<코드>(占位符为退出码)写入 /root/iac-drift/perm.txt。
  7. 创建 /root/iac-drift/detect.sh <작업디렉터리>(占位符为工作目录)。把 plan -refresh-only 保存为计划文件,汇总在外部发生变化的资源的地址:没有漂移则输出 OK 并以 0 结束;有漂移则输出 DRIFT <주소들(공백 구분, 정렬)>(占位符为按空格分隔、已排序的资源地址)一行并以 2 结束;失败则输出 ERROR 并以 1 结束。不要在工作目录中留下文件。评分器会在 /root/iac-drift 的副本中不断修改文件来检查。

参考

基线:用代码应用防火墙规则和公告

在 /root/iac-drift/main.tf 中放置 rules 变量(默认值 ["allow 443"])、按每条规则一行写入 out/firewall.rules 的 local_file.fw,以及向 out/motd.txt 写入一行 maintenance window: sun 02:00 的 local_file.motd,然后执行 init·apply。

用 for 表达式给每条规则加上换行,再用 join 连接起来即可。应用之后计划为空,才算基线。

在计划中查看周五晚上的紧急修改

在故障响应过程中,有人手工在 /root/iac-drift/out/firewall.rules 末尾加了一行 allow 8443(自己加上)。不要动代码,用 tofu plan -refresh-only 只查看在外部产生的变更,把输出保存到 /root/iac-drift/drift.txt。

-refresh-only 不会计划代码的变更,只显示真实情况与状态之间的差异。如果文件内容与记录不同,local_file provider 会把这个文件报告为“已删除”——在输出中确认,它并不会显示成某一个属性被改变。

周一如果直接 apply,会有什么消失

不要修改代码,用 tofu plan -out=/root/iac-drift/monday.tfplan 保存计划(不要应用)。把该计划将要写入 firewall.rules 的内容与当前文件比较,只把应用后会消失的行写入 /root/iac-drift/lost.txt。

计划 JSON 的 resource_changes 中,local_file.fw 的 change.after.content 就是应用之后的文件内容。只存在于当前文件中的行,就是会消失的行。可以用 comm 或 grep -vxF -f 求出按行的差异。

修改是正确的——吸收进代码

判断这次紧急修改是正确的。把 rules 的默认值改为 ["allow 443", "allow 8443"] 并 apply。之后 firewall.rules 必须是两行,且计划必须是干净的。

吸收,就是让代码符合现实。由于这个 provider 把文件视为“已删除”,所以修改代码之后的计划也是创建而不是修改——写入的内容与当前文件相同,所以结果是一样的。

修改是错误的——按代码改回去

这次有人把 out/motd.txt 改成了 maintenance window: none(自己改)。这是错误的变更。在回退之前,把 tofu plan -refresh-only 的输出保存到 /root/iac-drift/revert-drift.txt,然后保持代码不变,执行 apply,把公告改回代码中的内容。

回退,就是让现实符合代码。回退之前先确认为什么会有这样的变更,本身就是流程的一部分——这里通过保存 drift 输出来留下这份痕迹。

ignore_changes 能拦住的、拦不住的,以及根本看不到的

在 main.tf 中加入写入 out/cache.conf 的 cache v1、并设置了 lifecycle { ignore_changes = [content] } 的 local_file.cache,然后 apply。(a)用手把文件改成 cache tampered 后,把 tofu plan 的输出保存到 /root/iac-drift/ignore.txt,并用 apply 改回去。(b)把代码中的内容改为 cache v2,查看计划是否干净(不要应用)。(c)执行 chmod 600 out/firewall.rules 之后,把 tofu plan -detailed-exitcode 的退出码用 perm=<코드>(占位符为退出码)写入 /root/iac-drift/perm.txt。

ignore_changes 的意思是“即使配置中所写的值发生变化,也不要生成修改计划”。它拦不住 provider 读取实物后判断它已经不存在。而且,provider 不读取的属性发生变更,不会被当作漂移捕捉到。

每天早上运行的漂移检测

创建 /root/iac-drift/detect.sh <작업디렉터리>(占位符为工作目录)。把 plan -refresh-only 保存为计划文件,汇总在外部发生变化的资源的地址:没有漂移则输出 OK 并以 0 结束;有漂移则输出 DRIFT <주소들(공백 구분, 정렬)>(占位符为按空格分隔、已排序的资源地址)一行并以 2 结束;失败则输出 ERROR 并以 1 结束。不要在工作目录中留下文件。评分器会在 /root/iac-drift 的副本中不断修改文件来检查。

计划 JSON 中除了 resource_changes 之外,还有 resource_drift——在外部产生的变更会单独放在那里。-detailed-exitcode 可以与 -refresh-only 一起使用。