Terraform 状态管理与多环境协作规范
IaC 项目最容易翻车的不是 HCL 语法,而是状态文件。状态一旦损坏、并发写、或者被误删,恢复成本远高于手工点控制台。本文整理一套可直接抄用的小型团队规范:状态后端选型、目录布局、工作流与防护栏。
一、为什么不能用本地 state
本地 terraform.tfstate 有三个致命问题:无法共享、无锁、历史只在你机器上。任何两人以上协作都会出现"我 apply 把你的资源 plan 掉了"。结论很直接:团队协作一律用远端后端 + 锁。
| 后端 | 锁 | 适用 | |---|---|---| | S3 + DynamoDB | 支持 | 云上团队主流 | | GCS + GCS 原生锁 | 支持 | GCP 团队 | | GitLab HTTP | 支持 | 已有 GitLab | | Terraform Cloud | 支持 | 想省事 | | 本地文件/NFS | 不可靠 | 仅个人实验 |
terraform {
backend "s3" {
bucket = "okgoes-tfstate"
key = "prod/network.tfstate"
region = "ap-east-1"
dynamodb_table = "okgoes-tflock"
encrypt = true
}
}
二、目录布局:按爆炸半径切 state
最常见的错误是"一个巨型 monorepo 一个 state"。这样改一台开发机安全组会 plan 出生产资源。正确切法是按独立可回滚单元,也就是爆炸半径。
infra/
modules/
vpc/
rds/
ack-nodepool/
envs/
dev/
main.tf variables.tf outputs.tf backend.tf
staging/
prod/
envs/
global/ state: global/vpc.tfstate # VPC、DNS、RAM
prod/ state: prod/app.tfstate # prod 计算与数据
dev/ state: dev/app.tfstate
规则:基础网络单独一个 state,环境各一个 state。上游 state 通过 data source 或 terraform_remote_state 读取,不要重复定义。
data "terraform_remote_state" "vpc" {
backend = "s3"
config = { bucket = "okgoes-tfstate", key = "global/vpc.tfstate", region = "ap-east-1" }
}
resource "aws_security_group" "app" {
vpc_id = data.terraform_remote_state.vpc.outputs.vpc_id
}
三、变量与版本的三重锁定
可复现性是审核心。锁三样东西:
terraform {
required_version = "~> 1.9.0"
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.60.0" }
}
}
再加版本锁定文件:
terraform providers lock -platform=linux_amd64 -platform=darwin_arm64
terraform plan -lockfile=readonly # CI 里禁止隐式改锁
-lockfile=readonly 很关键,它能防止 CI 静默升级 provider 导致意外 diff。
四、标准工作流与防护栏
terraform init -backend-config=envs/prod/backend.conf
terraform plan -out prod.tfplan
terraform show -json prod.tfplan > prod.json # 交给策略检查
terraform validate
把危险动作在 CI 里挡住。用 OPA/Sentinel 检查 plan JSON:
deny[msg] {
r := input.resource_changes[_]
r.type == "aws_db_instance"
r.change.actions[_] == "delete"
msg := "禁止删除数据库实例,必须走人工评审"
}
再配两条硬规矩:
apply只在流水线里执行,且必须引用同一次plan产出的.tfplan(避免 plan 与 apply 之间状态漂移);- 生产
apply需要第二人审批。
五、state 的常见事故与处置
误删资源:先用 terraform state list 确认,再 terraform import 拉回,切勿手工 apply 重建。
terraform import aws_instance.web i-0f1a2b3c4d5e6f7a8
想纳管已有资源(迁移期最常见):
terraform plan -generate-config-out=generated.tf
Terraform 会自动生成资源骨架,人工审一遍再纳入。
state 损坏或误改:远端后端务必开启对象版本控制,回滚就是恢复上一版对象。这是唯一可靠的保险。
改名导致的删建:HCL 里的资源地址变了,Terraform 视作删除再创建。用 moved 块声明重构:
moved {
from = aws_instance.app_old
to = aws_instance.app
}
要移除纳管但不删资源:删 HCL 里的块,再用 terraform state rm;千万别 apply 完再抢救。
六、模块化的度
模块不要过度抽象。判断标准:被至少两个环境以不同参数使用才抽模块;只出现一次的资源直接写在环境目录里。经验上把模块控制在 3~5 个输入变量最舒服——超过十个变量的模块基本无法维护。
模块版本必须打 tag 引用,禁止用 source = "../modules/vpc" 这种相对路径,否则任何模块改动会立刻击穿所有环境。
module "vpc" {
source = "git::ssh://git@github.com/okgoes/infra-modules.git//vpc?ref=v1.4.2"
cidr = "10.20.0.0/16"
az_count = 3
}
小结
Terraform 的稳定性来自四件事:远端 state + 锁 + 版本控制、按爆炸半径切 state、plan 与 apply 同源且过策略、import/moved/rm 的规范操作。把这四条固化进流水线与代码评审清单,IaC 才真正具备可回滚的工程属性,而不是另一个需要供起来的魔法目录。
评论(0)
登录 后参与评论。