1. 环境隔离的整体思路
一句话总结: 多环境管理的核心是「状态隔离 + 配置差异」,先确定隔离粒度(workspace 还是目录),再配套 backend、变量与 promotion 流程。
dev/staging/prod 三套环境不是「三份代码」,而是同一份代码 + 三份配置 + 三个独立状态。任何环境之间共享状态都是灾难,因此多环境设计的第一决策点是:状态怎么隔开。
# 隔离的三种粒度
# 1. workspace:同一目录,用名称区分状态
terraform workspace new dev
terraform workspace select dev
# 2. 目录布局:每个环境一套目录,各有独立 backend
# envs/dev/ envs/staging/ envs/prod/
# 3. 独立账户/订阅:云平台层面的硬隔离,状态天然分开
1.1 隔离不只是状态
| 维度 | 手段 |
|---|---|
| 状态隔离 | 独立 backend 或独立 state key |
| 配置隔离 | tfvars / 环境变量注入差异 |
| 权限隔离 | 每个环境独立的 IAM 角色 |
| 网络隔离 | 独立 VPC / 独立云账号 |
| 数据隔离 | 独立 RDS/存储实例 |
真正的生产级隔离需要「状态 + 权限 + 数据」三层都分开,只靠 workspace 名称隔开状态是不够的。
2. workspace 与目录布局
一句话总结: workspace 适合「同一套代码的小差异」,目录布局适合「结构差异较大的多环境」;两者的选用取决于配置差异程度与团队协作模式。
2.1 workspace 的用法
workspace 在同一个目录下创建多个命名空间,每个 workspace 有独立的 state,但代码完全相同,用 terraform.workspace 引用当前环境名。
resource "aws_instance" "web" {
ami = var.ami
instance_type = terraform.workspace == "prod" ? "t3.large" : "t3.micro"
tags = {
Name = "${terraform.workspace}-web"
}
}
terraform workspace new staging
terraform workspace select staging
terraform workspace list
terraform workspace show
2.2 目录布局的选型
当不同环境的资源结构差异明显(比如 prod 有完整高可用,dev 只要单机)时,用目录布局更清晰:每个目录一份根配置,共享部分抽成模块。
infra/
├── modules/
│ ├── network/ # 共享网络模块
│ └── compute/ # 共享计算模块
├── envs/
│ ├── dev/
│ │ ├── main.tf # 调用 modules,覆盖默认参数
│ │ └── dev.tfvars
│ ├── staging/
│ │ ├── main.tf
│ │ └── staging.tfvars
│ └── prod/
│ ├── main.tf
│ └── prod.tfvars
└── global/
└── iam/ # 全局唯一资源(IAM、DNS)
一句话:小差异用 workspace,大差异用目录;两者也能混用(目录隔离环境、workspace 隔离同环境的泳道),但混用会增加心智负担,团队需约定清楚。
3. backend 隔离
一句话总结: backend 决定 state 存哪、锁怎么加;多环境隔离的本质就是给每个环境分配互不干扰的 state 存储位置。
remote backend(如 S3/GCS/AzureRM)在「存储」之外还提供「锁」,防止两个 apply 同时写坏 state。多环境隔离的关键是 state key 按环境区分。
3.1 每个环境一个 state key
terraform {
backend "s3" {
bucket = "my-company-terraform-state"
key = "terraform/${var.environment}/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
注意:backend 块内不允许使用变量(var.environment 在 backend 里不合法)。所以更常见的做法是「每个环境独立目录 + 各自写死 key」,或用 terraform init -backend-config 传参。
# 目录布局下,每个环境 init 时传入自己的 backend 配置
terraform init \
-backend-config="key=terraform/staging/terraform.tfstate" \
-backend-config="bucket=my-company-terraform-state"
# 或直接写成 backend-config 文件
terraform init -backend-config=staging.backend.tfvars
3.2 workspace 的 backend 键
使用 workspace 时,key 可以带上 workspace 名,让每个 workspace 落到不同 state:
terraform {
backend "s3" {
bucket = "my-company-terraform-state"
key = "terraform/workspaces/${terraform.workspace}.tfstate"
region = "ap-northeast-1"
}
}
${terraform.workspace} 在 backend 中可用,这是 workspace 方案天然支持 state 隔离的原因。
3.3 锁与并发
| backend | 锁实现 | 说明 |
|---|---|---|
| local | 无锁 | 仅单机,禁止团队共用 |
| s3 + DynamoDB | DynamoDB 表锁 | AWS 标准方案 |
| GCS | 对象锁 | GCP 标准方案 |
| Terraform Cloud | 托管锁 | SaaS,自带计划/审批 |
4. 环境变量与 tfvars
一句话总结: tfvars 是声明式地「给环境喂参数」,环境变量是运行时注入;敏感值走密钥系统,不要在 tfvars 里提交密码。
4.1 tfvars 文件与自动加载
Terraform 会自动加载 terraform.tfvars 与 *.auto.tfvars;带环境后缀的文件(如 staging.tfvars)需要显式 -var-file 传入。
# prod.tfvars
environment = "prod"
instance_type = "t3.large"
replica_count = 2
terraform apply -var-file=prod.tfvars
4.2 环境变量注入
变量也可以在运行时通过 TF_VAR_ 前缀注入,适合 CI 中传入动态值:
export TF_VAR_environment=staging
export TF_VAR_db_password=${STAGING_DB_PASSWORD}
terraform plan
优先级从低到高:变量默认值 → terraform.tfvars → *.auto.tfvars → -var 与 -var-file 命令行 → 环境变量 TF_VAR_*。
4.3 敏感值绝不进仓库
variable "db_password" {
type = string
sensitive = true
}
CI 里从密钥系统读取后再以 TF_VAR_db_password 注入;terraform.tfvars 只放非敏感差异项,敏感项一律不入库。
5. promotion 策略
一句话总结: promotion(环境晋级)有两种路线——「同代码重放」与「产物晋升」;要保证 dev 验证过的配置原样流到 prod,而不是重新手写。
promotion 的目标是:staging 验证通过的那份代码与配置,以最小改动到达 prod。
5.1 同代码重放
最朴素也最推荐的路线:代码库一个,各环境 -var-file 不同,apply 同一份 .tf 文件。
# staging 验证通过后,切到 prod 目录重放同一提交
git checkout v1.2.0
terraform apply -var-file=prod.tfvars
好处是「代码即真相」,坏处是 prod 的 apply 仍需人来执行,需要用审批流程把关。
5.2 产物晋升
更工程化的做法:先用 terraform plan -out 产出二进制计划文件,评审通过后晋升到下一环境执行。
# CI 中生成计划产物
terraform plan -out=plan.staging.bin
# 评审通过后,在 prod 环境 apply 同一产物
terraform apply plan.staging.bin
注意 plan 文件携带了 provider 版本与 state 快照,跨环境晋升时目标 state 必须匹配,否则 apply 会拒绝。
5.3 晋升门禁清单
| 关卡 | 检查项 |
|---|---|
| 代码评审 | PR 由团队成员审查通过 |
| 计划评审 | plan 差异无未预期的删除/变更 |
| 密钥就绪 | 目标环境的密钥系统已配置 |
| 回滚预案 | 已确认回滚流程与备份 |
| 数据安全 | 确认不破坏目标环境存量数据 |
6. 环境锁定与并发安全
一句话总结: 多环境并发 apply 由 backend 锁保护,但「force-unlock 滥用」「同一环境多人操作」仍是高发事故,需要流程与权限双约束。
6.1 锁的保护与释放
remote backend 自带锁:第一个 apply 拿锁,第二个会阻塞等待或超时失败。锁超时要谨慎 force-unlock。
# 查看锁状态
terraform force-unlock --help
# 只有确认没有其他 apply 在跑时才解锁
terraform force-unlock LOCK_ID
6.2 环境级权限隔离
理想状态下 dev 团队能改 dev,只有平台组能改 prod。用 Terraform Cloud/Enterprise 的团队权限,或 CI 流水线按分支绑定环境。
# 伪代码:流水线按分支映射环境
# main → prod
# staging → staging
# feature → dev
一句话:锁防「写坏 state」,权限防「改错环境」,两者都要,缺一不可。
7. 常见多环境坑
一句话总结: 多环境高发坑集中在 backend 变量不合法、workspace 误删、环境泄漏(引用错变量)、敏感值入库,前三个会造成状态损坏或环境污染。
| 坑 | 现象 | 正确做法 |
|---|---|---|
| backend 用变量 | init 报错「variables not allowed」 | 用 -backend-config 传参 |
| 误删 workspace | state 随之丢失 | 删除前备份 state,或用目录布局 |
| 环境变量泄漏 | staging 引用了 prod 配置 | 变量统一走 tfvars,按环境加载 |
| 敏感值入库 | 密码进 git 历史 | 用 TF_VAR + 密钥系统 |
| 共用 local backend | 多人写同一 state | 团队必须切 remote backend |
7.1 workspace 删除前的保护
# 列出并确认
terraform workspace list
# 切走再删除,删除不可恢复
terraform workspace select default
terraform workspace delete staging
# 想要保险,先导出 state
terraform state pull > staging.state.bak
8. 总结
多环境管理是 IaC 工程化的分水岭,隔离与晋升两条线贯穿始终:
| 环节 | 要点 |
|---|---|
| 隔离思路 | 状态隔离 + 配置差异 + 权限隔离三层 |
| 选型 | 小差异用 workspace,大差异用目录布局 |
| backend | key 按环境区分,用 -backend-config 传参 |
| 变量 | tfvars 声明差异,TF_VAR 注入敏感值 |
| promotion | 同代码重放或 plan 产物晋升 |
| 并发 | backend 锁 + 环境级权限 |
| 坑 | 禁用 backend 变量、删除前备份 state |
一句话收尾:把每个环境当成一个独立的「状态容器 + 配置切片」,用 backend、tfvars 与晋升流程把它们组织起来,多环境就从「三个易错副本」变成「一套可复用体系」。下一篇「Kubernetes Provider」将切换到云原生侧,讲解如何用 Terraform 编排 K8s 资源。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。