「多环境管理与 Workspace」

讲解 Terraform 多环境管理:workspace 与目录布局两种隔离路线、backend 的状态隔离与锁、环境变量与 tfvars 注入、环境 promotion 策略,以及并发锁定与常见多环境坑。

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 + DynamoDBDynamoDB 表锁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 传参
误删 workspacestate 随之丢失删除前备份 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,大差异用目录布局
backendkey 按环境区分,用 -backend-config 传参
变量tfvars 声明差异,TF_VAR 注入敏感值
promotion同代码重放或 plan 产物晋升
并发backend 锁 + 环境级权限
坑禁用 backend 变量、删除前备份 state

一句话收尾:把每个环境当成一个独立的「状态容器 + 配置切片」,用 backend、tfvars 与晋升流程把它们组织起来,多环境就从「三个易错副本」变成「一套可复用体系」。下一篇「Kubernetes Provider」将切换到云原生侧,讲解如何用 Terraform 编排 K8s 资源。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「terraform」更多文章

  1. 「漂移检测与收敛」
  2. 「资源重构与迁移」
  3. 「数据源与远程数据读取」