Terraform多环境基础设施即代码管理实战指南

Terraform基础设施即代码与多环境挑战

当团队从单环境扩展到dev/staging/prod多环境时,基础设施管理复杂度呈指数增长。不同环境的VPC网络、实例规格、安全组策略各不相同,手动配置既容易出错又难以审计。Terraform作为主流基础设施即代码工具,通过声明式配置和状态管理解决了这一问题,但多环境场景下的目录结构、状态隔离和变量管理仍然有不少工程细节需要处理。

多环境管理的核心目标有三个:配置复用最大化(减少重复代码)、环境隔离彻底化(避免状态交叉污染)、变更流程规范化(prod变更必须经过审批)。

目录结构设计

多环境Terraform项目的目录结构有两种主流流派:目录隔离法和Workspaces隔离法。目录隔离法为每个环境创建独立的目录结构,状态文件天然隔离;Workspaces法使用Terraform Workspaces在同一目录下管理多环境,通过变量映射区分。

project-root/
├── modules/              # 共享模块
│   ├── vpc/
│   ├── ec2/
│   └── rds/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── terraform.tfvars
│   ├── staging/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── terraform.tfvars
│   └── prod/
│       ├── main.tf
│       ├── variables.tf
│       ├── outputs.tf
│       └── terraform.tfvars
└── shared/
    └── backend.tf

目录隔离法的优势在于每个环境完全独立,状态文件不会互相干扰,且可以使用不同的Terraform版本和Provider版本。缺点是模块引用路径需要显式声明。

状态后端隔离与远程状态配置

每个环境必须使用独立的状态文件,这是多环境管理的铁律。状态交叉污染的后果极其严重——staging的apply操作可能覆盖prod的资源状态。

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

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

状态锁定(State Locking)是防止并发写入的关键机制。DynamoDB作为锁表确保同一时刻只有一个operator可以操作某个环境的状态。对于阿里云用户,可以使用OSS后端配合TableStore实现同样的效果。

模块化与变量分层策略

共享模块是配置复用的核心。一个设计良好的模块应该接受环境差异作为输入变量,内部逻辑保持一致。变量分层策略如下:

第一层:模块默认值。模块内部variables.tf中定义的default值,作为最基础配置。

第二层:环境公共变量。environments/xxx/terraform.tfvars中定义该环境的所有变量值。

第三层:环境特定变量。使用locals块根据环境名称做条件计算,处理无法通过简单变量覆盖的差异逻辑。

# environments/prod/main.tf
locals {
  env_config = {
    dev = {
      instance_type  = "t3.small"
      min_size       = 1
      max_size       = 2
      multi_az       = false
    }
    staging = {
      instance_type  = "t3.medium"
      min_size       = 2
      max_size       = 4
      multi_az       = true
    }
    prod = {
      instance_type  = "c5.2xlarge"
      min_size       = 4
      max_size       = 20
      multi_az       = true
    }
  }
  config = local.env_config[var.environment]
}

module "compute" {
  source = "../../modules/ec2"
  instance_type = local.config.instance_type
  min_size      = local.config.min_size
  max_size      = local.config.max_size
  multi_az      = local.config.multi_az
}

CI/CD流水线集成与审批门控

多环境Terraform的变更必须通过CI/CD流水线执行,禁止本地直连prod状态。典型的流水线分为plan和apply两个阶段:

# GitLab CI示例
stages:
  - plan
  - apply

terraform_plan_dev:
  stage: plan
  script:
    - cd environments/dev
    - terraform init
    - terraform plan -out=tfplan
  artifacts:
    paths:
      - environments/dev/tfplan

terraform_apply_prod:
  stage: apply
  needs: [terraform_plan_prod]
  script:
    - cd environments/prod
    - terraform init
    - terraform apply tfplan
  when: manual
  only:
    - main
  environment:
    name: production
    action: start

prod环境的apply阶段设置when: manual,要求运维人员手动确认后才执行。结合GitLab的Protected Environments或GitHub的Environment Protection Rules,可以进一步限制只有特定角色的用户才能触发prod部署。

Drift检测与状态一致性维护

配置漂移(Configuration Drift)是指实际基础设施状态与Terraform状态不一致的情况,通常由手动操作控制台、自动化脚本直接修改资源导致。漂移检测是SRE稳定性工程的重要环节。

#!/bin/bash
# drift_detect.sh - 定期检测各环境漂移
ENVIRONMENTS="dev staging prod"
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx"

for env in $ENVIRONMENTS; do
  cd environments/$env || continue
  terraform init -backend-config=backend.tf > /dev/null 2>&1
  CHANGES=$(terraform plan -detailed-exitcode 2>&1)
  EXIT_CODE=$?
  if [ $EXIT_CODE -eq 2 ]; then
    PAYLOAD="{\"text\":\"[$env] 检测到基础设施漂移,请检查\n$CHANGES\"}"
    curl -s -X POST -H 'Content-type: application/json' \
      --data "$PAYLOAD" $SLACK_WEBHOOK
  fi
done

建议将漂移检测脚本加入定时任务,每天凌晨执行一次。当检测到漂移时,优先通过terraform plan确认差异,再决定是执行apply恢复声明式状态,还是更新Terraform配置以匹配实际变更。口袋网在实践中有条经验:任何非Terraform发起的资源变更必须同步更新Terraform代码,否则下一次apply会覆盖手动变更。

数据资源与跨环境依赖

跨环境依赖是多环境管理中的常见场景。Terraform通过data块和remote_state数据源解决这个问题:

# environments/staging/main.tf
data "terraform_remote_state" "prod" {
  backend = "s3"
  config = {
    bucket = "tf-state-prod"
    key    = "infra/prod/terraform.tfstate"
    region = "us-east-1"
  }
}

resource "aws_vpc_peering_connection" "staging_to_prod" {
  vpc_id      = module.vpc.vpc_id
  peer_vpc_id = data.terraform_remote_state.prod.outputs.vpc_id
  peer_region = "us-east-1"
  auto_accept = false
}

跨环境引用增加了耦合度,应控制使用范围。推荐的实践是只通过remote_state读取输出值,不直接依赖其他环境的内部资源结构,保持各环境的自治性。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/terraform-duo-huan-jing-ji-chu-she-shi-ji-dai-ma-guan-li/

(0)
小编小编
上一篇 18小时前
下一篇 18小时前

相关推荐