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 支持多种变量赋值方式,优先级从高到低如下:
- 命令行
-var或-var-file(最高优先级) - 环境变量
TF_VAR_<变量名> terraform.tfvars或*.auto.tfvars文件- 变量定义文件中的
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 的基础架构:
4.5 本章小结与避坑指南
- 模块化:避免“上帝模块”,每个模块应小而专。使用
source锁定版本。 - 变量:充分利用
validation块进行输入校验;敏感变量使用sensitive = true。 - 远程状态:务必启用状态锁(如 DynamoDB),防止多人同时执行
apply导致状态损坏。 - 状态文件安全:远程 Backend 应启用加密(如 S3 SSE-S3 或 KMS),并限制 IAM 权限。
生产建议: 在 Runbook 的
terraform plan 阶段,使用 -detailed-exitcode 参数,结合 CI/CD 判断是否有变更,从而决定是否触发 apply。这可以避免无变更时的无效执行。