Terraform 云部署:远程状态与模块化

FreeGuideOnline 最新 2026-07-01

bash aws s3api create-bucket
--bucket terraform-remote-state-2024
--region us-east-1
--create-bucket-configuration LocationConstraint=us-east-1


 **启用版本控制**(用于状态回滚):
```bash
aws s3api put-bucket-versioning \
    --bucket terraform-remote-state-2024 \
    --versioning-configuration Status=Enabled

启用服务端加密(保护静态数据):

aws s3api put-bucket-encryption \
    --bucket terraform-remote-state-2024 \
    --server-side-encryption-configuration '{
      "Rules": [
        {
          "ApplyServerSideEncryptionByDefault": {
            "SSEAlgorithm": "AES256"
          }
        }
      ]
    }'

创建 DynamoDB 表(状态锁表):

aws dynamodb create-table \
    --table-name terraform-state-locks \
    --attribute-definitions AttributeName=LockID,AttributeType=S \
    --key-schema AttributeName=LockID,KeyType=HASH \
    --billing-mode PAY_PER_REQUEST \
    --region us-east-1

表结构说明LockID 为分区键,Terraform 会在执行 apply 前尝试向该表写入一条记录,通过条件写入检查实现分布式锁。

配置后端

在 Terraform 配置文件的顶层(或专门的 backend.tf 文件)中定义远程后端。

terraform {
  backend "s3" {
    bucket         = "terraform-remote-state-2024"
    key            = "production/network/terraform.tfstate"  # 路径分隔,支持多项目
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-locks"
  }
}
  • key:状态文件在桶中的路径,通常按 环境/项目/terraform.tfstate 组织,实现多环境隔离。
  • encrypt:开启服务端加密。
  • dynamodb_table:指定用于状态锁的 DynamoDB 表名。

首次配置后端时,运行 terraform init 会提示迁移现有本地状态到远程,按提示操作即可。

最佳实践:将后端配置中的 bucket、region 等参数用变量或者单独的后端配置文件管理(部分参数不支持变量),避免硬编码。

第二步:使用远程状态进行协作

状态锁定演示

当开发者 A 正在执行 terraform apply 时,开发者 B 尝试同样操作会立即收到锁定错误:

Error acquiring the state lock: ConditionalCheckFailedException

这有效防止了并行修改。操作完成后,Terraform 会自动释放锁(正常或异常退出时强制清理)。

共享状态引用:terraform_remote_state

远程状态不仅用于主工作目录,还能被其他 Terraform 项目读取,实现跨项目数据共享。例如,网络团队输出的 VPC ID 可以被应用团队直接引用。

data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "terraform-remote-state-2024"
    key    = "production/network/terraform.tfstate"
    region = "us-east-1"
  }
}

# 在其他资源中使用
resource "aws_instance" "web" {
  subnet_id = data.terraform_remote_state.network.outputs.public_subnet_id
  #...
}

注意:此方法需要上游项目通过 output 明确暴露所需值。另一种更解耦的方式是使用数据源直接查询(如 aws_vpc data source),但远程状态在跨团队交付时更直接。

第三步:构建可复用的模块

模块是 Terraform 代码组织和复用的基本单元,可以是本地目录、Git 仓库或 Terraform Registry。

模块基本结构

一个典型的 VPC 模块目录:

modules/vpc/
├── main.tf
├── variables.tf
├── outputs.tf
└── README.md

variables.tf —— 暴露输入参数

variable "name" {
  description = "VPC name tag"
  type        = string
}
variable "cidr" {
  description = "VPC CIDR block"
  type        = string
}
variable "azs" {
  description = "Availability zones"
  type        = list(string)
}

main.tf —— 定义资源

resource "aws_vpc" "main" {
  cidr_block           = var.cidr
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = var.name
  }
}

resource "aws_subnet" "public" {
  count                   = length(var.azs)
  vpc_id                  = aws_vpc.main.id
  cidr_block              = cidrsubnet(var.cidr, 8, count.index)
  availability_zone       = var.azs[count.index]
  map_public_ip_on_launch = true
  tags = {
    Name = "${var.name}-public-${count.index}"
  }
}

outputs.tf —— 输出关键属性

output "vpc_id" {
  value = aws_vpc.main.id
}
output "public_subnet_ids" {
  value = aws_subnet.public[*].id
}

调用模块

在根配置中引用模块,通过传递不同变量值创建多个环境或项目。

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

  name = "prod-vpc"
  cidr = "10.0.0.0/16"
  azs  = ["us-east-1a", "us-east-1b"]
}

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

  name = "stg-vpc"
  cidr = "10.1.0.0/16"
  azs  = ["us-east-1a"]
}

模块来源管理

  • 本地source = "./modules/vpc"
  • Git 仓库source = "git::https://github.com/your-org/terraform-modules.git//vpc?ref=v1.2.0"(标签固定版本)
  • Terraform Registrysource = "terraform-aws-modules/vpc/aws"(官方社区模块)

版本固定非常重要,避免上游变更破坏你的基础设施。

第四步:将远程状态与模块结合用于云部署

目录结构最佳实践

一个多环境项目的推荐布局:

.
├── backend.tf          # 后端配置(所有环境可能指向同一桶,不同 key)
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   ├── stg/
│   └── prod/
├── modules/
│   ├── vpc/
│   ├── ecs-service/
│   └── rds/
└── global/            # 全局资源(如 IAM、Route53)

每个环境都有自己的状态文件 key,例如:

  • dev/network/terraform.tfstate
  • prod/network/terraform.tfstate

创建环境入口(environments/prod/main.tf)

provider "aws" {
  region = "us-east-1"
  assume_role {
    role_arn = "arn:aws:iam::123456789012:role/terraform-deploy-role"
  }
}

module "vpc" {
  source = "../../modules/vpc"
  name   = "prod-vpc"
  cidr   = "10.0.0.0/16"
  azs    = ["us-east-1a", "us-east-1b"]
}

module "service" {
  source = "../../modules/ecs-service"
  vpc_id = module.vpc.vpc_id
  subnets = module.vpc.public_subnet_ids
  # ...其他参数
}