Terraform 与 OpenTofu 基础设施即代码生产治理
基础设施即代码(IaC)真正的价值不是“用代码创建虚机”,而是把基础设施的期望状态、评审、审计、复用、漂移检测和恢复流程纳入工程体系。Terraform 与 OpenTofu 具有相似的 HCL 工作流,但生产落地必须处理 State、锁、模块、Provider、密钥、导入、销毁保护、流水线和组织权限。
apply、destroy、state rm、state push、force-unlock和资源替换都可能直接改变生产。不要在个人电脑上用管理员凭证对生产执行未经评审的计划。
一、IaC 的适用边界
适合:云资源、网络、负载均衡、DNS、IAM、集群基础、可重复的平台组件。谨慎管理:数据库内部数据、一次性迁移、频繁变化的业务配置、没有稳定 API 的设备和需要复杂人工确认的操作。
HCL 配置 + 模块 + Provider 锁文件
↓
fmt / validate / lint / security check
↓
Plan(可审阅变更)
↓ 审批
Apply(使用同一份 Plan)
↓
远端 State + 锁 + 审计日志
二、State 是核心资产
State 保存配置资源与真实对象的绑定,也可能包含密码、连接串和私有地址等敏感数据。它不是普通缓存。
生产要求:
- 使用支持访问控制、加密、版本控制和锁的远端 Backend;
- 不把 State 提交 Git;
- 限制下载和读取权限;
- 备份并验证恢复;
- 监控锁、版本和异常写入;
- 不直接手工编辑 JSON。
OpenTofu 支持 State/Plan 静态加密,但密钥丢失会导致 State 无法读取;加密也不防止文件损坏或旧 State 重放。
三、State 锁与并发
支持锁的 Backend 会在写操作前自动加锁,避免多个 Apply 同时修改同一 State。不要用 -lock=false 绕过生产锁。
terraform plan -lock-timeout=10m -out=prod.tfplan
terraform apply prod.tfplan
force-unlock 只能在确认原执行进程已结束、锁确实遗留,并核对 Lock ID 后使用。错误解锁可能造成多个写者和 State 损坏。
四、State 拆分策略
State 太大时,计划慢、影响面大、权限难隔离;拆得太碎又会产生依赖和输出耦合。常按以下边界拆分:
- 账号/订阅与区域;
- 网络基础与应用资源;
- 共享平台与业务团队;
- 生命周期和变更频率;
- 权限和故障域。
不要仅用 Workspace 代替所有环境隔离。高风险生产通常需要独立 Backend、凭证和审批边界。
五、模块设计与版本治理
模块应表达稳定能力,而不是把所有参数都暴露。优秀模块包含:
- 清晰输入、输出和类型约束;
- 合理默认值与前置条件;
- 标签、日志、加密等安全基线;
- 示例和升级说明;
- 单元/集成测试;
- 语义化版本和变更日志。
module "app_network" {
source = "git::ssh://git.example/iac/network.git?ref=v2.4.1"
name = "payment-prod"
cidr = "10.42.0.0/16"
enable_flow_logs = true
}
生产必须固定模块版本。直接引用 main 会让同一配置在不同时间产生不同结果。
六、Provider 与锁文件
Provider 是实际调用基础设施 API 的代码,应固定版本范围并提交 .terraform.lock.hcl。升级时阅读变更说明,在测试环境生成 Plan,确认 Schema 和默认行为变化。
terraform {
required_version = ">= 1.6, < 2.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
版本示例需要按组织实际兼容性调整,不能把“最新版本”当作自动安全。
七、变量、敏感值与凭证
sensitive = true 只控制部分界面输出,不保证敏感值不会进入 State。安全做法:
- 流水线使用短期身份或工作负载身份;
- 不在
.tfvars中提交生产密钥; - 从 Vault/KMS/CI Secret 注入;
- 限制 Plan 文件下载,因为 Plan 也可能包含敏感值;
- 避免通过 Output 暴露秘密;
- 定期轮换并审计 Provider 凭证。
八、生产流水线
推荐阶段:
fmt -check;init(只读或受控插件源);validate;- Lint、安全与策略检查;
- 生成二进制 Plan;
- 将人类可读摘要附加到 PR;
- 生产审批;
- 使用同一份已审批 Plan 执行 Apply;
- 保存日志、State 版本和变更结果。
Plan 与 Apply 之间若配置、变量、Provider 或 State 已改变,应重新 Plan,而不是继续执行旧结果。
九、Plan 审阅重点
审阅不能只看“3 add, 1 change, 0 destroy”。需要判断:
- 是否出现 Replace;
- 数据库、磁盘、IP、DNS、IAM 是否被删除或重建;
- 安全组是否扩大到公网;
- 加密、日志、备份是否被关闭;
- Count/for_each Key 改动是否造成地址整体变化;
- Provider 默认值是否改变;
- 资源依赖是否导致级联更新;
- 未知值是否掩盖关键差异。
十、策略即代码
通过 OPA/Conftest、Sentinel 或组织策略阻止高风险配置,例如:
- 公网开放管理端口;
- 未加密磁盘或数据库;
- 未启用审计日志;
- 生产资源缺少删除保护;
- 标签、区域或实例规格不合规;
- 计划中删除数量超过阈值。
策略应有分级:硬阻断、需审批、仅提示。一次性将所有规则设为阻断,会导致团队绕过治理。
十一、导入存量资源
导入只是把真实对象与资源地址绑定,不会自动生成完美配置。安全流程:
- 盘点真实资源和依赖;
- 编写最小配置;
- 在独立 State 中导入;
- 多次 Plan,逐步补齐差异;
- 确保最终 Plan 为无变更或仅包含明确变更;
- 再纳入正式流水线。
import {
to = aws_s3_bucket.logs
id = "company-prod-logs"
}
不要在第一次导入后立即 Apply 大量“默认值修正”。
十二、重构资源地址
模块重命名、count 改 for_each 或目录重构可能让工具误判资源需删除重建。优先使用 moved 块表达地址迁移。
moved {
from = aws_instance.web
to = module.web.aws_instance.this
}
重构与业务参数变更应分开提交,让 Plan 更容易审阅。
十三、漂移检测
手工控制台变更、自动伸缩、外部控制器和紧急处置都会造成漂移。定期执行只读 Plan,并把结果分类:
- 应回写代码的有效变化;
- 应由 IaC 恢复的违规变化;
- 应通过
ignore_changes精确忽略的外部管理字段; - 应重新划分资源所有权的冲突。
不要用大范围 ignore_changes = all 消除噪声。
十四、销毁与替换保护
关键资源可配置生命周期保护,但它不是唯一防线。
lifecycle {
prevent_destroy = true
}
还应结合云侧删除保护、备份、审批、策略和权限隔离。prevent_destroy 被从配置中删除后不再生效,因此不能替代组织控制。
十五、故障场景
Apply 中断
先检查真实资源与 State,不要立即重跑。Provider 可能已经创建资源但尚未写回 State。重新 Plan,确认是继续、导入还是清理。
State 写回失败
保护本地生成的恢复 State,暂停其他执行,核对 Backend 当前版本后再按官方流程 Push。state push -force 是最后手段。
资源在控制台被删除
Plan 通常会尝试重建。先确认删除是事故还是有意操作,检查数据恢复和依赖后再 Apply。
锁长期存在
确认流水线执行器、进程和后台任务已经结束,核对锁持有者与时间,再决定解锁。
十六、灾难恢复
需要备份:代码仓库、模块仓库、Provider 镜像或缓存策略、Backend State 历史、加密密钥、流水线配置、策略、变量和身份配置。
演练方式是在隔离账号或沙箱中恢复 State,执行 plan -refresh-only 或等价检查,确认能识别真实资源且不会大规模重建。仅保存 HCL 而没有 State,并不能无损接管现有基础设施。
十七、监控与度量
关注:Plan/Apply 成功率、执行时长、等待锁时间、漂移数量、Replace/Destroy 数、策略阻断、人工控制台变更、模块版本分布、失败恢复时间和生产紧急变更次数。
十八、上线检查清单
- State 使用加密、版本控制、锁和最小权限远端 Backend;
- 配置、模块和 Provider 版本固定;
- 生产凭证使用短期身份,不落地个人电脑;
- Plan 经过机器策略与人工审阅;
- Apply 使用已审批的同一份 Plan;
- 高风险资源具备多层删除保护;
- 定期漂移检测;
- State、密钥和流水线完成恢复演练。
参考资料
- Terraform State:
https://developer.hashicorp.com/terraform/language/state - Terraform State Locking:
https://developer.hashicorp.com/terraform/language/state/locking - OpenTofu State Backends:
https://opentofu.org/docs/language/state/backends/ - OpenTofu State and Plan Encryption:
https://opentofu.org/docs/language/state/encryption/