概述
Terraform 的执行过程是读取 state、计算差异、调用云 API、写回 state。两个针对同一份 state 的运行可能交错执行,最终写出既不描述旧状态也不描述新状态的中间结果。加锁通过让后端在运行期间记录持有者来串行化这个过程。S3 加 DynamoDB 是长期使用的标准方案,较新的 Terraform 可以用 use_lockfile = true 配合 S3 条件写入,省去额外的表。无论哪种方式,只有所有写入方都遵守锁才有意义,因此流水线和人工操作必须使用同一套后端配置。
步骤
- 配置带锁与加密的远程后端:
terraform {
backend "s3" {
bucket = "acme-tfstate"
key = "payments/prod.tfstate"
region = "eu-west-1"
dynamodb_table = "acme-tfstate-locks"
encrypt = true
}
}
- 同时启动两个 plan,确认锁生效:
terraform plan -lock-timeout=0 & terraform plan -lock-timeout=0; wait
- 解锁前先检查锁的持有者:
aws dynamodb get-item --table-name acme-tfstate-locks \
--key '{"LockID":{"S":"acme-tfstate/payments/prod.tfstate"}}'
- 只解锁已确认的具体锁 ID:
terraform force-unlock "$LOCK_ID"
验证
第二个并发 plan 应立即失败,并输出锁 ID 与创建时间,而不是等待超时;检查锁记录后应能看到持有者与时间戳。解锁后新的 plan 应正常获取锁。若怀疑 apply 被中断,解锁后立即执行只读 plan 或 refresh,确认 state 与云端一致,再继续发布。
注意事项
- 锁只保护 state 写入,不保护云 API,资源命名冲突仍需通过唯一命名解决。
force-unlock会跳过安全检查,滥用可能导致两个写入方同时运行。- 取消 CI 任务后应等待进程真正退出再解锁,避免仍在执行的 apply 继续写入。
- 使用
use_lockfile需要 Terraform 1.10 及以上,并依赖 S3 条件写入。 - 在非生产环境演练一次“进程被杀—检查锁—解锁—重新 plan”的流程。