跳到主要内容

工作区与多环境管理

在实际项目中,通常需要管理多个环境(开发、测试、生产)的基础设施。Terraform 提供了工作区和文件结构两种方式来管理多环境配置。本章将详细介绍这两种方法及其适用场景。

工作区(Workspace)

Terraform 工作区允许在同一配置下维护多个独立的状态文件,每个工作区有自己的状态。

工作区命令

# 列出所有工作区
terraform workspace list
* default

# 创建新工作区
terraform workspace new dev
Created and switched to workspace "dev"!

# 切换工作区
terraform workspace select default
Switched to workspace "default".

# 查看当前工作区
terraform workspace show
dev

# 删除工作区
terraform workspace delete dev
Deleted workspace "dev".

工作区使用场景

工作区适合环境差异较小的情况:

# main.tf
locals {
# 根据工作区确定环境
environment = terraform.workspace == "default" ? "prod" : terraform.workspace

# 环境配置
config = {
dev = {
instance_type = "t2.micro"
instance_count = 1
enable_monitoring = false
}
staging = {
instance_type = "t2.small"
instance_count = 2
enable_monitoring = true
}
prod = {
instance_type = "t2.medium"
instance_count = 3
enable_monitoring = true
}
}

# 获取当前环境配置
env_config = local.config[local.environment]
}

# 使用环境配置
resource "aws_instance" "web" {
count = local.env_config.instance_count

ami = data.aws_ami.amazon_linux.id
instance_type = local.env_config.instance_type

monitoring = local.env_config.enable_monitoring

tags = {
Name = "web-${local.environment}-${count.index + 1}"
Environment = local.environment
}
}

工作区与远程状态

# backend.tf
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "terraform.tfstate" # 工作区名称会自动附加
region = "us-east-1"

# 实际存储路径:
# - dev: env:/dev/terraform.tfstate
# - prod: env:/prod/terraform.tfstate
# - default: terraform.tfstate
}
}

工作区命名空间

# 使用工作区命名资源
resource "aws_s3_bucket" "assets" {
bucket = "my-app-assets-${terraform.workspace}"

tags = {
Environment = terraform.workspace
}
}

文件结构方式

对于环境差异较大的项目,推荐使用文件结构方式管理多环境。

推荐的项目结构

terraform-project/
├── modules/ # 可重用模块
│ ├── networking/
│ ├── compute/
│ └── database/
├── environments/ # 环境配置
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ ├── backend.tf # 远程状态配置
│ │ └── terraform.tfvars # 环境变量值
│ ├── staging/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ ├── backend.tf
│ │ └── terraform.tfvars
│ └── prod/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ ├── backend.tf
│ └── terraform.tfvars
├── global/ # 全局资源
│ └── iam/
├── live/ # 实时基础设施(按服务划分)
│ ├── vpc/
│ ├── eks/
│ └── rds/
└── scripts/ # 辅助脚本
├── plan.sh
└── apply.sh

环境配置示例

开发环境

# environments/dev/backend.tf
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "dev/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}

# environments/dev/main.tf
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = var.aws_region

default_tags {
tags = {
Environment = "dev"
ManagedBy = "Terraform"
}
}
}

module "vpc" {
source = "../../modules/networking"

vpc_cidr = var.vpc_cidr
availability_zones = var.availability_zones
environment = "dev"
}

module "compute" {
source = "../../modules/compute"

vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
instance_type = var.instance_type
instance_count = var.instance_count
}

# environments/dev/variables.tf
variable "aws_region" {
type = string
default = "us-east-1"
}

variable "vpc_cidr" {
type = string
default = "10.0.0.0/16"
}

variable "availability_zones" {
type = list(string)
default = ["us-east-1a"]
}

variable "instance_type" {
type = string
default = "t2.micro"
}

variable "instance_count" {
type = number
default = 1
}

# environments/dev/terraform.tfvars
aws_region = "us-east-1"
vpc_cidr = "10.1.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b"]
instance_type = "t2.micro"
instance_count = 2

生产环境

# environments/prod/backend.tf
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}

# environments/prod/main.tf
# 结构与 dev 类似,但使用不同的变量值

# environments/prod/terraform.tfvars
aws_region = "us-east-1"
vpc_cidr = "10.3.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
instance_type = "t2.large"
instance_count = 5

环境管理策略

策略一:工作区方式

适用场景

  • 环境之间差异较小
  • 团队规模较小
  • 需要快速切换环境

优点

  • 配置复用,减少重复代码
  • 切换环境简单

缺点

  • 权限控制困难
  • 容易误操作生产环境

策略二:文件结构方式

适用场景

  • 环境之间差异较大
  • 需要严格的权限控制
  • 大型团队

优点

  • 环境隔离清晰
  • 易于权限管理
  • 可以独立版本控制

缺点

  • 配置有一定重复
  • 需要维护多个目录

策略三:混合方式

结合两种方式的优点:

terraform-project/
├── modules/
│ └── ...
├── environments/
│ ├── dev/ # 开发环境(文件结构)
│ ├── staging/ # 测试环境(文件结构)
│ └── prod/ # 生产环境(文件结构)
│ ├── us-east-1/ # 使用工作区管理多区域
│ └── eu-west-1/
└── ...

环境部署流程

使用脚本自动化

#!/bin/bash
# scripts/deploy.sh

ENV=$1
ACTION=$2

if [ -z "$ENV" ] || [ -z "$ACTION" ]; then
echo "Usage: $0 <environment> <plan|apply|destroy>"
exit 1
fi

cd "environments/$ENV" || exit 1

case $ACTION in
plan)
terraform init
terraform plan -out=tfplan
;;
apply)
terraform apply tfplan
;;
destroy)
terraform destroy
;;
*)
echo "Unknown action: $ACTION"
exit 1
;;
esac

CI/CD 集成

GitHub Actions 示例

# .github/workflows/terraform.yml
name: Terraform

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
terraform:
runs-on: ubuntu-latest
environment: ${{ matrix.environment }}

strategy:
matrix:
environment: [dev, staging]

steps:
- uses: actions/checkout@v3

- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: "1.6.0"

- name: Terraform Init
run: |
cd environments/${{ matrix.environment }}
terraform init

- name: Terraform Plan
run: |
cd environments/${{ matrix.environment }}
terraform plan

- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: |
cd environments/${{ matrix.environment }}
terraform apply -auto-approve
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

环境间依赖

使用远程状态数据源

# environments/prod/main.tf
# 引用开发环境的 VPC 信息

data "terraform_remote_state" "dev" {
backend = "s3"

config = {
bucket = "company-terraform-state"
key = "dev/terraform.tfstate"
region = "us-east-1"
}
}

# 使用开发环境的输出
resource "aws_security_group_rule" "allow_dev" {
type = "ingress"
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = [data.terraform_remote_state.dev.outputs.vpc_cidr]
security_group_id = aws_security_group.database.id
}

环境升级策略

蓝绿部署

# 使用工作区实现蓝绿部署
locals {
deployment_color = terraform.workspace == "blue" ? "blue" : "green"
}

resource "aws_instance" "app" {
count = var.instance_count

ami = var.ami_id
instance_type = var.instance_type

tags = {
Name = "app-${local.deployment_color}-${count.index + 1}"
Deployment = local.deployment_color
}
}

金丝雀发布

# 逐步切换流量
locals {
canary_percentage = terraform.workspace == "canary" ? 10 : 100
}

resource "aws_lb_target_group_attachment" "app" {
count = length(aws_instance.app)

target_group_arn = aws_lb_target_group.app.arn
target_id = aws_instance.app[count.index].id
port = 80
}

环境清理

自动清理开发环境

# 使用生命周期规则
resource "aws_instance" "dev" {
count = var.environment == "dev" ? 1 : 0

ami = data.aws_ami.amazon_linux.id
instance_type = "t2.micro"

tags = {
Name = "dev-instance"
AutoCleanup = "true"
CreatedAt = timestamp()
}

lifecycle {
prevent_destroy = false
}
}
# 清理脚本
#!/bin/bash
# scripts/cleanup.sh

# 查找并删除标记为 AutoCleanup 的资源
aws ec2 describe-instances \
--filters "Name=tag:AutoCleanup,Values=true" \
--query 'Reservations[*].Instances[*].InstanceId' \
--output text | xargs -I {} aws ec2 terminate-instances --instance-ids {}

最佳实践

1. 环境命名规范

# 推荐的环境命名
- dev / development
- staging / test / qa
- prod / production
- dr / disaster-recovery

# 避免使用
- temp / temporary
- test1 / test2
- my-env

2. 状态隔离

# 每个环境使用独立的 S3 路径
# dev: s3://bucket/env/dev/terraform.tfstate
# prod: s3://bucket/env/prod/terraform.tfstate

terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "env/${var.environment}/terraform.tfstate"
region = "us-east-1"
}
}

3. 权限控制

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::company-terraform-state/env/dev/*"
},
{
"Effect": "Deny",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::company-terraform-state/env/prod/*"
}
]
}

4. 变更审批流程

# 生产环境需要审批
production:
environment:
name: production
url: https://prod.example.com
deployment:
protection_rules:
- type: required_reviewers
reviewers:
- senior-devops

下一步

了解了多环境管理后,我们将学习 最佳实践,掌握生产环境中使用 Terraform 的高级技巧和注意事项。