4. Terraform 编排 Runbook 基础架构:模块化设计、变量与输出、远程状态管理

构建生产级容灾自愈 Runbook,首先需要夯实 Terraform 的基础架构。本章聚焦三大核心支柱:模块化设计(复用与抽象)、变量与输出(参数化与数据传递)、远程状态管理(Backend,团队协作与状态锁)。这三者共同构成了可维护、可扩展、可协作的 IaC 基座。

4.1 模块化设计:从单体到可复用组件

模块化是将复杂的 Runbook 拆解为独立、可组合的 Terraform 模块。每个模块封装一组相关的资源,暴露清晰的输入(变量)和输出(Outputs)。

核心原则: 模块应遵循单一职责原则,每个模块只负责一个逻辑单元(如:创建云数据库、配置负载均衡、部署监控探针)。

4.1.1 模块结构标准

一个标准的 Terraform 模块通常包含以下文件:

文件/目录 用途 是否必须
main.tf 核心资源定义(如 aws_instance, azurerm_virtual_network) 是
variables.tf 声明模块的输入变量及其类型、默认值、描述 是
outputs.tf 定义模块对外暴露的输出值(如 IP 地址、资源 ID) 推荐
versions.tf 指定 Terraform 版本和 Provider 版本约束 推荐
README.md 模块使用说明、示例、注意事项 推荐

4.1.2 模块调用示例

以下示例展示如何在根模块中调用一个名为 vpc 的子模块:

# 根模块 main.tf
module "vpc" {
  source = "./modules/vpc"  # 本地路径,也可使用 Git URL 或 Terraform Registry

  # 传递输入变量
  vpc_name        = "runbook-vpc"
  cidr_block      = "10.0.0.0/16"
  enable_dns_hostnames = true

  tags = {
    Environment = "production"
    ManagedBy   = "terraform-runbook"
  }
}

# 使用模块输出
resource "aws_security_group" "web_sg" {
  name   = "web-sg"
  vpc_id = module.vpc.vpc_id  # 引用模块输出
  # ... 规则定义
}
最佳实践: 使用 source 参数引用模块时,优先使用 Git 标签(?ref=v1.2.0)锁定版本,避免意外变更。

4.2 变量与输出:参数化与数据契约

变量(Variables)使 Runbook 能够适应不同环境(开发、预发、生产);输出(Outputs)则定义了模块间的数据契约,以及向 Runbook 上层(如 CI/CD、监控系统)暴露的关键信息。

4.2.1 变量定义与类型约束

在 variables.tf 中定义变量时,应明确类型、描述和验证规则:

# variables.tf
variable "instance_count" {
  description = "EC2 实例数量"
  type        = number
  default     = 2

  validation {
    condition     = var.instance_count > 0 && var.instance_count <= 10
    error_message = "实例数量必须在 1 到 10 之间。"
  }
}

variable "allowed_ips" {
  description = "允许访问的 IP 列表"
  type        = list(string)
  default     = ["10.0.0.0/8"]
}

variable "environment" {
  description = "部署环境"
  type        = string
  default     = "dev"

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "环境必须是 dev、staging 或 prod。"
  }
}

4.2.2 输出定义与敏感数据

输出用于向调用方传递信息。对于密码、密钥等敏感数据,应标记为 sensitive = true:

# outputs.tf
output "vpc_id" {
  description = "VPC ID"
  value       = aws_vpc.main.id
}

output "database_endpoint" {
  description = "数据库连接端点"
  value       = aws_db_instance.main.endpoint
  sensitive   = true  # 防止在日志或 UI 中明文显示
}

output "instance_public_ips" {
  description = "实例公网 IP 列表"
  value       = aws_instance.web[*].public_ip
}

4.2.3 变量赋值方式优先级

Terraform 支持多种变量赋值方式,优先级从高到低如下:

  1. 命令行 -var 或 -var-file(最高优先级)
  2. 环境变量 TF_VAR_<变量名>
  3. terraform.tfvars 或 *.auto.tfvars 文件
  4. 变量定义文件中的 default 值(最低优先级)
注意: 不要在版本控制中提交包含生产环境密码或密钥的 terraform.tfvars 文件。应使用 CI/CD 的 Secret 管理功能或 Vault 等工具注入敏感变量。

4.3 远程状态管理(Backend):团队协作与状态锁

默认情况下,Terraform 将状态文件(terraform.tfstate)存储在本地。在团队协作或自动化 Runbook 场景中,必须使用远程 Backend 来共享状态、防止冲突并支持状态锁。

4.3.1 常用 Backend 类型对比

Backend 类型 状态存储位置 状态锁支持 适用场景
S3 (AWS) S3 Bucket DynamoDB AWS 原生环境,最常用
AzureRM Storage Account (Blob) Azure Blob Lease Azure 原生环境
GCS (GCP) GCS Bucket Cloud Storage Object Lock GCP 原生环境
Consul Consul KV Store Consul Session 多云或已有 Consul 基础设施
Terraform Cloud/Enterprise 托管服务 内置 企业级协作,无需自建

4.3.2 S3 + DynamoDB Backend 配置示例

这是生产环境中最推荐的 AWS Backend 方案:

# backend.tf
terraform {
  backend "s3" {
    bucket         = "my-company-terraform-state"
    key            = "runbook/self-healing/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"  # 用于状态锁
  }
}
关键点:
  • Bucket:必须提前创建,并启用版本控制(用于状态文件回滚)。
  • DynamoDB Table:必须提前创建,主键为 LockID(String),用于防止并发执行 terraform apply。
  • Key:建议按项目/环境/模块组织,如 project/env/component.tfstate。

4.3.3 远程状态工作流

配置 Backend 后,terraform init 会提示是否迁移本地状态到远程。后续所有 plan 和 apply 操作都会自动从远程读取状态,并在执行时获取锁。

# 初始化并迁移状态
terraform init -migrate-state

# 执行计划(自动从远程读取状态)
terraform plan

# 执行部署(自动获取锁,防止冲突)
terraform apply -auto-approve

4.4 模块化 + 变量 + 远程状态:Runbook 基础架构总览

下图展示了这三者如何协同工作,构成 Runbook 的基础架构:

Terraform Runbook 基础架构:模块化 + 变量 + 远程状态 根模块 (Root Module) - 定义全局变量 - 调用子模块 - 组合资源 - 输出关键信息 子模块 (Child Module) - 接收输入变量 - 定义资源逻辑 - 暴露输出值 - 可复用、可测试 远程状态 (Backend) - S3 / AzureRM / GCS - 状态文件共享 - DynamoDB 状态锁 - 版本控制与回滚 数据流与协作流程 1. 根模块通过 module.xxx 调用子模块,并传递变量。 2. 子模块执行资源创建,并通过 output 返回结果给根模块。 3. 所有模块的最终状态统一写入远程 Backend,团队共享并自动加锁。 CI/CD Pipeline 触发 Runbook 执行

4.5 本章小结与避坑指南

  • 模块化:避免“上帝模块”,每个模块应小而专。使用 source 锁定版本。
  • 变量:充分利用 validation 块进行输入校验;敏感变量使用 sensitive = true。
  • 远程状态:务必启用状态锁(如 DynamoDB),防止多人同时执行 apply 导致状态损坏。
  • 状态文件安全:远程 Backend 应启用加密(如 S3 SSE-S3 或 KMS),并限制 IAM 权限。
生产建议: 在 Runbook 的 terraform plan 阶段,使用 -detailed-exitcode 参数,结合 CI/CD 判断是否有变更,从而决定是否触发 apply。这可以避免无变更时的无效执行。