文章

团队中的 Terraform:状态划分、影响范围与经评审的计划

如何划分 Terraform 状态、在 CI 中运行 plan、只应用经过评审的内容,让基础设施变更成为常规操作而不是冒险。

示例均为通用的说明性内容,并非取自任何客户环境:请根据您自己的系统调整阈值和配置。

状态文件才是真正的边界

每次 terraform apply 都可能修改其所操作状态中的任何内容。因此风险的单位是状态,而不是模块。如果整个环境只有一个状态,那么 DNS 记录里的一个笔误和对生产数据库的一次修改会出现在同一个 plan 中——而这个 plan 需要几分钟才能算完。

按生命周期和影响范围拆分

把一起变更、一起出故障的资源放在一组。常见的划分方式是在每个环境中分出:

  • 基础层:账号、网络、DNS 区域——很少变更。
  • 数据层:数据库、存储桶、队列——谨慎变更,并防止被删除。
  • 计算与服务层:集群、函数、负载均衡器——经常变更。

每一层读取下一层的输出,但从不管理其资源。各环境共用同一套模块,但状态和凭据彼此独立,这样在 staging 执行 apply 不可能影响生产环境。

远程状态、锁与版本

  • 将状态保存在启用了锁和版本管理的远程后端中——绝不放在 Git 里,也绝不放在笔记本电脑上。
  • 提交依赖锁文件(.terraform.lock.hcl)并固定模块版本,让今天的 plan 和下个月的 plan 运行同样的代码。
  • 用 prevent_destroy 以及云服务商自带的删除保护来保护有状态资源。

每个拉取请求都运行 plan,只应用评审过的内容

对每次变更都在 CI 中运行 terraform plan,并把结果发布到拉取请求上。审批之后,应用已保存的计划而不是重新 plan,确保生产环境收到的正是评审者看过的内容;如果期间状态发生了变化,Terraform 会拒绝这个过期的计划。流水线应通过 OIDC 联合身份使用短期凭据认证,而不是使用存为密钥的长期密钥。

terraform init -input=false
terraform plan -input=false -lock-timeout=5m -out=tfplan
terraform show -no-color tfplan > plan.txt   # attach to the pull request

# after approval, applying the same saved plan:
terraform apply -input=false tfplan

定期检测漂移

在控制台中手动修改仍然会发生,通常是在事故期间。定时运行 terraform plan -detailed-exitcode 的任务可以发现它们:退出码 0 表示无变化,2 表示实际基础设施与代码不一致,1 表示出错。把 2 转成工单,然后将该变更写入代码或予以撤销。

应用之前先过策略

“禁止公开存储桶”“每个资源都必须有负责人标签”这类规则,由机器检查比让评审者在漫长一天结束时检查更可靠。策略即代码工具,例如通过 Conftest 使用 Open Policy Agent,可以在 CI 中评估计划的 JSON 形式(terraform show -json)并阻止合并。

联系

想听听第二种意见?

发送一段对您系统的简短描述。资深工程师会在一个工作日内书面答复。

聊聊您的基础设施