Terraform多环境管理的核心挑战
当团队使用Terraform管理基础设施时,开发、测试、预发、生产等多套环境的配置管理是绕不开的难题。不同环境使用不同的VPC、子网、实例规格和参数值,但底层资源结构高度一致。如何在保证环境隔离的同时避免配置代码大量重复,是Terraform多环境管理的核心问题。Terraform提供了workspaces、目录隔离、模块化+变量文件三种主流方案,各有适用场景。
方案一Workspaces工作空间隔离环境状态
Terraform Workspaces通过在同一代码目录下维护多套state文件来实现环境隔离。每个workspace对应一个独立的state,切换workspace即可在不同环境中操作。这种方式代码量最少,但隐式依赖关系容易导致误操作。
# 创建和切换workspace
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
terraform workspace select dev
# 在代码中引用workspace名称
resource "aws_instance" "app" {
ami = var.ami_id
instance_type = terraform.workspace == "prod" ? "m5.xlarge" : "t3.medium"
tags = {
Environment = terraform.workspace
}
}
Workspaces方案的问题在于:同一个代码目录的变量文件只能有一份,环境差异需要通过terraform.workspace条件判断或variable默认值处理。当环境差异较大时,代码中充斥着三目运算符和条件分支,可维护性急剧下降。Workspaces适合环境差异小(仅规模不同)的场景,不适合环境间有架构差异的情况。
方案二目录隔离各环境独立代码目录
目录隔离方案为每个环境创建独立的Terraform代码目录,各环境完全独立。这种方式最直观,但会导致大量代码重复。标准做法是通过共享模块(shared modules)消除重复,各环境目录仅存放backend配置和变量文件。
# 项目目录结构
infra/
├── modules/
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── ecs/
│ └── rds/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ ├── staging/
│ └── prod/
# environments/dev/main.tf
module "vpc" {
source = "../../modules/vpc"
cidr_block = var.vpc_cidr
env = "dev"
}
module "ecs" {
source = "../../modules/ecs"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
instance_type = var.ecs_instance_type
desired_count = var.ecs_desired_count
env = "dev"
}
方案三模块化与变量文件组合
这是大型团队最推荐的方案。将所有基础设施组件抽象为可复用模块,环境差异全部收敛到变量文件中。核心代码只维护一份,通过不同变量值驱动不同环境的资源创建。每个环境的目录只包含一个main.tf(调用模块)、变量定义和backend配置。
# environments/dev/terraform.tfvars
vpc_cidr = "10.0.0.0/16"
ecs_instance_type = "t3.medium"
ecs_desired_count = 2
rds_instance_class = "db.t3.medium"
rds_multi_az = false
enable_nat_gateway = false
# environments/prod/terraform.tfvars
vpc_cidr = "10.1.0.0/16"
ecs_instance_type = "m5.xlarge"
ecs_desired_count = 10
rds_instance_class = "db.r6g.xlarge"
rds_multi_az = true
enable_nat_gateway = true
Remote Backend多环境状态隔离配置
生产环境必须使用远程Backend存储state文件,避免本地state丢失和团队协作冲突。S3+DynamoDB是AWS环境的标准方案,多环境的state通过workspace key前缀或独立bucket实现隔离。
# environments/dev/backend.tf
terraform {
backend "s3" {
bucket = "terraform-state-dev"
key = "infra/dev/terraform.tfstate"
region = "cn-north-1"
encrypt = true
dynamodb_table = "terraform-lock-dev"
}
}
# environments/prod/backend.tf
terraform {
backend "s3" {
bucket = "terraform-state-prod"
key = "infra/prod/terraform.tfstate"
region = "cn-north-1"
encrypt = true
dynamodb_table = "terraform-lock-prod"
}
}
state文件包含敏感信息(数据库密码、安全组规则),S3 bucket必须启用版本控制和加密,并通过bucket policy限制访问源IP和IAM角色。DynamoDB表用于state locking,防止并发操作导致state损坏。
模块版本管理与发布流程
共享模块需要有明确的版本管理策略。模块代码变更后,各环境不能自动拉取最新代码,而是通过版本标签显式引用。Git标签是最常见的模块版本管理方式。
# 通过Git标签引用模块版本
module "vpc" {
source = "git::https://github.com/org/terraform-modules.git//vpc?ref=v2.3.0"
}
# 或通过Terraform Registry
module "vpc" {
source = "registry.terraform.io/org/vpc/aws"
version = "~> 2.3"
}
模块版本升级流程建议:开发环境先升级到新版本验证,预发环境跟随,生产环境最后。任何模块版本变更都需要走代码评审流程。生产环境的模块版本应该锁定精确版本号,避免语义版本前缀导致意外升级。
CI/CD流水线集成Terraform自动化部署
Terraform操作应该通过CI/CD流水线执行,而非人工在本地执行。流水线的标准阶段包括:init -> plan -> 人工审批(仅prod环境) -> apply。plan的输出作为审批依据,apply执行实际变更。
# GitHub Actions示例
name: Terraform Deploy
on:
push:
paths:
- 'environments/prod/**'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init
working-directory: environments/prod
- name: Terraform Plan
run: terraform plan -out=tfplan
working-directory: environments/prod
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve tfplan
working-directory: environments/prod
生产环境的apply操作必须配置审批门禁,plan输出中会显示资源变更明细(+创建、-删除、~修改),审批人需要仔细审查是否有意外删除操作。drift detection(配置偏移检测)也应纳入流水线,定期执行terraform plan检测运行状态与声明配置是否一致。
敏感变量管理与安全实践
Terraform变量文件中可能包含数据库密码、API密钥等敏感信息,这些值不应该硬编码在tfvars文件中。标准做法是使用Vault或云厂商的密钥管理服务(AWS SSM Parameter Store、AWS Secrets Manager)动态注入敏感值。
# 从AWS SSM Parameter Store读取数据库密码
data "aws_ssm_parameter" "db_password" {
name = "/prod/database/password"
with_decryption = true
}
module "rds" {
source = "../../modules/rds"
db_password = data.aws_ssm_parameter.db_password.value
}
Terraform state文件中默认会明文存储所有变量值,包括敏感值。模块的output中标记sensitive = true可以阻止CLI输出显示敏感值,但state文件中仍然是明文。因此state文件的访问控制至少与密钥管理系统同等严格。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/terraform-ji-chu-she-shi-ji-dai-ma-duo-huan-jing-pei-zhi/