4. 远程State与后端治理
本地 terraform.tfstate 只适合单人验证。一旦进入团队协作,State 就必须被当作“生产数据库”来治理:它记录资源与真实基础设施的映射关系,任何并发写入、误删或版本回退都可能让 Terraform 认为资源不存在,从而触发重复创建或错误销毁。本节围绕后端选型、锁机制、版本控制、共享隔离和损坏恢复五个方面展开。
一、State 存储选型:先看约束,再看产品
选型的核心不是“哪个后端最流行”,而是四个约束:是否支持原生锁、是否支持版本化、是否支持加密与访问审计、是否与现有身份体系集成。常见后端对比如下。
| 后端 | 锁机制 | 版本控制 | 适用场景 |
|---|---|---|---|
| local | 无 | 无 | 单人实验、临时验证 |
| S3 + DynamoDB | DynamoDB 条件写入 | S3 版本控制 + 对象锁 | AWS 为主、跨团队共享 |
| Azure Blob | Blob Lease | Blob 版本 + 软删除 | Azure 生态 |
| GCS | 对象 generation 前置条件 | Object Versioning | GCP 生态 |
| Terraform Cloud/Enterprise | 平台内置 | 平台内置历史 | 需要审批、策略、Run 编排 |
| PostgreSQL | 行级锁 | 需自行实现 | 已有数据库运维能力 |
以 S3 为例,后端配置通常写成:
terraform {
backend "s3" {
bucket = "acme-tfstate-prod"
key = "network/vpc/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "acme-tfstate-lock"
encrypt = true
kms_key_id = "arn:aws:kms:ap-southeast-1:123456789012:key/abcd-..."
}
}
其中 key 是隔离边界的关键:不同环境、不同团队、不同资源域应使用不同前缀,而不是共用一个 terraform.tfstate。DynamoDB 表只需一个字符串主键 LockID,Terraform 会在 plan 和 apply 期间写入锁记录,完成后删除。
二、锁机制:谁在写、写多久、卡住怎么办
锁的本质是“同一 State 同一时刻只允许一个写操作”。执行 terraform apply 时,Terraform 先获取锁,再刷新、计划、执行、写回 State,最后释放锁。若锁已被占用,会看到类似提示:
Error: Error acquiring the state lock
Lock Info:
ID: 8f3c1e2a-...
Path: acme-tfstate-prod/network/vpc/terraform.tfstate
Operation: OperationTypeApply
Who: ci-runner@build-07
Created: 2025-01-12 08:31:44.120 +0000 UTC
处理原则是:先确认持有者是否仍在运行。若 CI 任务已崩溃且锁未释放,可使用 terraform force-unlock <LOCK_ID>,但必须确保没有其他进程正在写同一 State。强制解锁是“最后手段”,不是常规操作。为避免长时间持锁,应把 apply 拆小、避免在 apply 期间做人工审批,并给 CI 设置超时。
三、版本控制与跨团队共享隔离
State 文件本身不应提交到 Git,但 State 的“版本历史”必须保留。S3 开启版本控制后,每次写回都会生成新版本,配合生命周期规则可保留 90 天。恢复时先 aws s3api list-object-versions 找到目标 VersionId,再下载比对,确认无误后回传。
跨团队共享的推荐做法是“按边界拆分 State,用 terraform_remote_state 或数据源只读引用”。例如网络团队维护 VPC State,应用团队通过 data source 读取子网 ID,而不是把 VPC 和应用资源塞进同一个 State。这样锁竞争小、爆炸半径小、权限也能按前缀划分。
四、State 损坏的典型表现与恢复演练
State 损坏常见于三种情况:并发 apply 导致后写覆盖前写、误删对象、手工编辑 JSON 破坏结构。表现包括 terraform plan 报 Error: Failed to load state、资源被计划重建、serial 回退导致 Terraform 拒绝写入。恢复前必须先停止所有可能写该 State 的流水线,并确认锁已释放。
恢复演练可按以下步骤执行(示例命令以 S3 为例):
- 列出历史版本:
aws s3api list-object-versions --bucket acme-tfstate-prod --prefix network/vpc/terraform.tfstate,记录目标VersionId。 - 下载候选版本:
aws s3api get-object --bucket acme-tfstate-prod --key network/vpc/terraform.tfstate --version-id <VID> /tmp/state-candidate.json。 - 比对关键字段:
serial应大于当前损坏版本,lineage必须与当前一致,否则说明来自不同 State 谱系,不能直接回传。 - 回传:
aws s3api copy-object --bucket acme-tfstate-prod --key network/vpc/terraform.tfstate --copy-source acme-tfstate-prod/network/vpc/terraform.tfstate?versionId=<VID>。 - 验证:执行
terraform plan,预期输出No changes或仅显示已知漂移;若出现大量资源重建,说明回传版本不正确,应重新选择版本。
terraform state push 覆盖一个 serial 更小的 State,除非明确知道自己在做什么。Terraform 会拒绝 serial 回退,这是保护机制,不是故障。五、治理清单与常见误区
- 每个 State 必须有唯一
key前缀,禁止多团队共用一个 State。 - 锁表与 State 桶同区域部署,减少跨区延迟与权限复杂度。
- 开启版本控制、服务端加密、访问日志,并设置保留策略。
- CI 中禁止
force-unlock自动执行,必须人工确认。 - 定期做恢复演练:至少每季度从历史版本恢复一次到隔离环境并跑
plan。
terraform plan 无意外变更、serial 单调递增、锁表无残留记录。三者同时满足,才说明后端治理处于可控状态。