LabHub
学习 学习路径 课程

Terraform/OpenTofu 基础

怎么读 plan — 符号、退出码、漂移

在 LabHub 中继续学习

一句话总结

applyplan这只是执行我已经做出的决定。阻止事故的唯一时间是阅读计划的几分钟,而这些几分钟最终不是由人来完成,而是由脚本来完成。

概念图: 保存为文件 · 以机器可以读取的形式提取出来,并用规则进行判断 · 不可改变的因素 · 在查询阶段发现的代码以外的变更

为什么需要这个?

基础设施代码中最昂贵的错误是“错误地将可再生性误认为是原地修改”。在屏幕上出现的-/+~误以为这样,生产数据库就会被删除后重新创建。计划输出很长,部署通常在晚些时候进行,人的眼睛如果看同一屏幕三次就会开始不看懂。

所以成熟的团队会做两件事。第一,把计划保存为文件,然后按照评论过的计划进行应用。如果不保存,评论过的计划和实际应用的计划可能会不同,因为在这期间有人可能会更改其他内容。第二,把计划以机器可以读取的形式提取出来,并用规则进行判断。像“如果有任何删除,必须由人批准”这样的规则,不是由人来遵守,而是由管道来遵守。

怎么行动

人们阅读的计划上附有符号。

符号 意思 危险度
+ 生成
~ 原地修正 普通
- 删除
-/+ 删除后再生 非常高
+/- 生成后删除(create_before_destroy)
<= 读取数据源

产生再生性的原因通常是因为触及了不可改变的因素,对于这一点# forces replacement附有标记。最下面是缩写总结的Plan: X to add, Y to change, Z to destroy在再生中,再生量在add和destroy两边各被扣除1个,也需要记住这一点。

自动化所需的不是符号,而是两个。一个是-detailed-exitcode全部。

结束代码 含义
0 无变更
1 错误
2 有变更

多亏了这三个值,我们就可以用一行代码实现“如果有变化就通知”这样的定期漂移检测。另一个是JSON表示法。将计划文件转换为JSON的话resource_changes排列出来,各项目的change.actions["create"]["update"]["delete"],如果是可再生的话["delete","create"]正在进去。还有resource_drift中包含了在查询阶段发现的代码以外的变更。这意味着可以区分反映在计划中的变更和已经发生的变更。

-target这是只选择部分曲线进行处理的选项。使用时,工具会发出警告。因为只应用部分,剩下的可能会与代码不一致,状态不一致。这是恢复情况的工具,不是平时的工作流程。

在现场相遇的样子

**第一,提供商升级后的大量漂移。**上传主要版本后,由于新添加的属性的默认值,数百个资源都会发生变化。大部分不是改变实际基础设施,而是填充状态中的属性。不区分这两个,惊慌失措地撤销或相反,在没有确认的情况下应用都会导致事故。用JSON提取,按操作数算的话判断会更快。

第二,检测本身的成本。plan对正在管理的所有资源调用提供商API。规模越大,就会受到API限制,在检测过程中会锁定状态,与部署发生冲突。检测周期不是免费的。

**第三,留在计划日志中的秘密。**计划输出时,资源属性会保持原样。将通知渠道或CI日志全部粘贴的习惯会成为泄露路径。只发送摘要,将全部放在受控的地方比较安全。

计划中必须用眼睛确认的三件事

terraform plan的输出很长。因为不能全部读完,所以先确定要找什么 看。

第一,总结行中的destroy个数。Plan: 3 to add, 1 to change, 2 to destroy从 不是0,而是destroy,总是有理由停下手来。如果是故意的,什么会被抹掉呢? 通过名称确认,如果不是故意的,通常是资源地址变更了(模块 搬过来了或者countfor_each换成了)。当时不是删除了而是做了。 moved只把地址用区块转移。

moved {
  from = aws_instance.web[0]
  to   = aws_instance.web["a"]
}

**第二,# forces replacement附加的属性。**只是换了一个名字,但数据库 被替换的计划在这里显现出来。在有状态的资源中prevent_destroy挂起来 一想,在计划阶段就完全被阻挡了。

lifecycle { prevent_destroy = true }

**第三,(known after apply)挂在哪里了。如果这个价格高的话,计划 意思是很难提前知道具体要做什么。其他资源还没有的属性 因为是用来参考的,虽然本身是正常的,但是重要的决定(安全组规则、政策文件) 如果取决于这个价格,**应该知道在应用前不能进行审查。

**将计划作为文件接收并应用。**计划和应用之间有人改变了什么, 适用与本条不同的事项。

terraform plan -out=tfplan
terraform show -json tfplan | jq '[.resource_changes[]
  | select(.change.actions | index("delete"))] | map(.address)'
terraform apply tfplan

在自动化中使用终止代码。-detailed-exitcode是0(没有变化), 退回1(错误)、2(有变更)。在CI中“如果有变更,需要获得人的批准” 流向在这里开始。

下次实习要做的事情

/root/tf/plan将计划从中保存为文件,转换为JSON,并按操作数统计并记录。-detailed-exitcode直接确认的两个结束代码,让原地修改和可重现性一起纳入一个计划。用手修改正在管理的文件。resource_drift看到,-target缩小范围,阅读工具的警告。最后,制作包含删除计划的自己判断的评论报告。