正在加载,请稍候…

Infrastructure as Code with Terraform: Modules, State, and Best Practices

Manage cloud infrastructure with Terraform. Learn modules, state management, workspaces, remote state, Terraform Cloud, and best practices for team collaboration.

Infrastructure as Code with Terraform

Project Structure

terraform/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   └── prod/
│       ├── main.tf
│       └── terraform.tfvars
├── modules/
│   ├── vpc/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── ecs-service/
│   └── rds/
└── shared/
    └── backend.tf

Module Design

# modules/ecs-service/main.tf
variable "name" { type = string }
variable "image" { type = string }
variable "cpu" { type = number; default = 256 }
variable "memory" { type = number; default = 512 }
variable "environment_variables" {
  type = map(string)
  default = {}
}

resource "aws_ecs_task_definition" "this" {
  family                   = var.name
  requires_compatibilities = ["FARGATE"]
  network_mode             = "awsvpc"
  cpu                      = var.cpu
  memory                   = var.memory

  container_definitions = jsonencode([{
    name  = var.name
    image = var.image
    portMappings = [{ containerPort = 3000 }]
    environment = [
      for k, v in var.environment_variables : { name = k, value = v }
    ]
    logConfiguration = {
      logDriver = "awslogs"
      options = {
        "awslogs-group"  = "/ecs/${var.name}"
        "awslogs-region" = data.aws_region.current.name
        "awslogs-stream-prefix" = "ecs"
      }
    }
  }])
}

output "task_definition_arn" {
  value = aws_ecs_task_definition.this.arn
}

Using Modules

# environments/prod/main.tf
module "api_service" {
  source = "../../modules/ecs-service"

  name   = "api-prod"
  image  = "123456789.dkr.ecr.us-east-1.amazonaws.com/api:${var.image_tag}"
  cpu    = 512
  memory = 1024

  environment_variables = {
    NODE_ENV    = "production"
    DB_HOST     = module.rds.endpoint
    REDIS_URL   = "redis://${module.redis.endpoint}:6379"
  }
}

Remote State with S3

# backend.tf
terraform {
  backend "s3" {
    bucket         = "my-company-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"  # Prevent concurrent applies
  }
}

# Create the lock table
resource "aws_dynamodb_table" "terraform_lock" {
  name           = "terraform-state-lock"
  billing_mode   = "PAY_PER_REQUEST"
  hash_key       = "LockID"
  attribute { name = "LockID"; type = "S" }
}

Workspaces for Environments

# Create workspaces
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod

# Switch workspace
terraform workspace select prod

# Use in config
locals {
  env_config = {
    dev     = { instance_type = "t3.small"; min_count = 1; max_count = 3 }
    staging = { instance_type = "t3.medium"; min_count = 2; max_count = 5 }
    prod    = { instance_type = "t3.large"; min_count = 3; max_count = 20 }
  }
  config = local.env_config[terraform.workspace]
}

CI/CD Integration

# .github/workflows/terraform.yml
- name: Terraform Init
  run: terraform init -backend-config="key=${{ github.ref_name }}/terraform.tfstate"

- name: Terraform Plan
  run: terraform plan -out=tfplan -var="image_tag=${{ github.sha }}"

- name: Terraform Apply (main branch only)
  if: github.ref == 'refs/heads/main'
  run: terraform apply tfplan

Drift Detection

# Detect config drift (manual changes in console)
terraform plan -detailed-exitcode
# Exit code 0: no changes
# Exit code 1: error
# Exit code 2: diff found (drift!)

Terraform modules enable DRY infrastructure code that can be reused across environments and teams.