Terraform基础设施即代码多环境部署与状态管理实践

Terraform作为HashiCorp推出的基础设施即代码(IaC)工具,通过声明式配置语言HCL描述云资源拓扑,实现基础设施的版本化管理与自动化部署。DevOps实践中Terraform的核心挑战在于多环境(dev/staging/prod)配置隔离、State状态文件安全管理、Module模块复用三个方向。合理的状态管理策略能避免团队协作中的状态冲突与资源漂移。

Terraform多环境架构设计与工作区隔离

多环境管理有三种主流模式:目录隔离、工作区隔离、变量文件隔离。目录隔离为每个环境创建独立目录和独立State,最安全但代码重复较多。工作区隔离使用terraform workspace在同一配置下切换不同State,适合配置差异小的场景。变量文件隔离通过不同的terraform.tfvars文件区分环境参数。

生产环境推荐目录隔离模式,配合Module复用消除重复代码。目录结构设计:

infra/
├── modules/
│   ├── vpc/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── eks/
│   └── rds/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── backend.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   ├── staging/
│   │   ├── main.tf
│   │   ├── backend.tf
│   │   └── terraform.tfvars
│   └── prod/
│       ├── main.tf
│       ├── backend.tf
│       └── terraform.tfvars
└── README.md

每个环境目录引用相同的Module,通过传入不同变量实现差异化配置。dev环境可降低副本数和实例规格,prod环境配置多可用区和高可用参数。

State状态文件远程存储与锁定机制配置

Terraform State文件记录了基础设施的实际状态,是plan和apply的基础。本地State文件存在三大风险:无法团队协作、无历史版本、含敏感信息明文。生产环境必须使用远程Backend存储。

S3 + DynamoDB是AWS环境的标准方案:S3存储State文件并开启版本控制,DynamoDB提供状态锁防止并发执行冲突。锁定机制确保同一时刻只有一个Terraform进程能修改State,避免覆盖式更新导致的资源丢失。

# environments/prod/backend.tf
terraform {
  backend "s3" {
    bucket         = "mycompany-terraform-state-prod"
    key            = "prod/terraform.tfstate"
    region         = "ap-northeast-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}
# 初始化远程后端的S3和DynamoDB(一次性引导资源)
# bootstrap.tf - 用本地State创建远程存储基础设施
resource "aws_s3_bucket" "terraform_state" {
  bucket = "mycompany-terraform-state-prod"
  
  lifecycle {
    prevent_destroy = true  # 防止误删State存储桶
  }
}

resource "aws_s3_bucket_versioning" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  
  attribute {
    name = "LockID"
    type = "S"
  }
}

Module模块化设计与版本化复用

Module封装了一组相关资源的配置,通过input variables接收参数,通过outputs暴露结果。良好的Module设计应保持单一职责、提供合理默认值、不硬编码环境相关信息。

# modules/vpc/main.tf
variable "cidr_block" {
  type    = string
  default = "10.0.0.0/16"
}

variable "environment" {
  type    = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "environment must be dev, staging, or prod."
  }
}

variable "enable_nat_gateway" {
  type    = bool
  default = true
}

variable "single_nat_gateway" {
  type    = bool
  default = false  # prod环境设为false(每AZ一个NAT),dev设为true(共享NAT)
}

resource "aws_vpc" "main" {
  cidr_block           = var.cidr_block
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = {
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

output "vpc_id" {
  value = aws_vpc.main.id
}

output "private_subnet_ids" {
  value = aws_subnet.private[*].id
}

环境目录引用Module时通过source指定路径或Git版本:

# environments/prod/main.tf
module "vpc" {
  source = "../../modules/vpc"
  
  cidr_block          = "10.1.0.0/16"
  environment         = "prod"
  enable_nat_gateway  = true
  single_nat_gateway  = false  # 生产环境每AZ独立NAT
}

module "eks" {
  source = "../../modules/eks"
  
  vpc_id              = module.vpc.vpc_id
  private_subnet_ids  = module.vpc.private_subnet_ids
  cluster_version     = "1.30"
  node_group_size     = 3
  environment         = "prod"
}

module "rds" {
  source = "../../modules/rds"
  
  vpc_id     = module.vpc.vpc_id
  db_size    = "db.r6g.xlarge"
  multi_az   = true
  environment = "prod"
}

CI/CD流水线集成与Plan/Apply自动化

Terraform应纳入CI/CD流水线实现自动化管理。典型流程为:PR提交触发terraform fmt + validate + plan,人工Review plan输出,合并后触发apply。GitHub Actions配置示例:

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

on:
  pull_request:
    paths: ["environments/**"]
  push:
    branches: [main]
    paths: ["environments/**"]

jobs:
  plan:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: environments/prod
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1
      - uses: hashicorp/setup-terraform@v3
      - run: terraform fmt -check -recursive
      - run: terraform init
      - run: terraform validate
      - run: terraform plan -no-color
        continue-on-error: true
      - uses: actions/github-script@v7
        if: github.event_name == 'pull_request'
        with:
          script: |
            const output = `Terraform Plan Output:\n${{ steps.plan.outputs.stdout }}`
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output
            })

  apply:
    needs: plan
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE }}
          aws-region: ap-northeast-1
      - uses: hashicorp/setup-terraform@v3
      - run: cd environments/prod && terraform init
      - run: cd environments/prod && terraform apply -auto-approve

State漂移检测与资源导入

基础设施因手动修改或外部操作偏离Terraform配置时产生State漂移。定期执行terraform plan检查是否存在非预期的变更输出。CI/CD中可设置定时任务运行plan并告警。

手动创建或通过Console创建的资源需要通过terraform import导入State管理。导入操作不会生成配置文件,需手动编写对应的resource配置:

# 导入已存在的S3桶到State
terraform import aws_s3_bucket.my_bucket my-existing-bucket-name

# 导入后需在配置文件中补充对应的resource块
# terraform show可查看导入资源属性,据此补全配置

import命令只导入State不修改资源。对于复杂资源(如RDS实例及其子资源参数组、安全组),需要逐一导入或使用import block(Terraform 1.5+)批量导入。

# Terraform 1.5+ 的import block(可在plan阶段预览导入)
import {
  to = aws_s3_bucket.my_bucket
  id = "my-existing-bucket-name"
}

import {
  to = aws_db_instance.main
  id = "mydb-instance-identifier"
}

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

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

相关推荐