4. Terraform 模块结构:标准模块目录结构、根模块与子模块、模块注册表

Terraform 模块化设计的核心在于 可复用 与 可组合。一个设计良好的模块,必须遵循标准的目录结构,明确根模块与子模块的职责边界,并通过模块注册表实现团队间的共享与版本管理。本章将系统拆解这三个关键维度。

4.1 标准模块目录结构

HashiCorp 社区与官方最佳实践共同定义了 Terraform 模块的推荐目录布局。遵循此结构可确保模块的 可读性、可测试性 与 可发布性。

目录 / 文件 是否必须 用途说明
main.tf 是 模块的主逻辑入口,定义核心资源与数据源。
variables.tf 是 声明模块所有输入变量,包含类型、描述与默认值。
outputs.tf 是 定义模块对外暴露的输出值,供根模块或其他子模块引用。
versions.tf 推荐 声明 Terraform 与 Provider 的版本约束,确保兼容性。
README.md 推荐 模块的文档说明,包含用途、用法示例、输入输出清单。
modules/ 可选 存放嵌套子模块,用于拆分复杂逻辑或提供变体实现。
examples/ 推荐 提供可直接运行的示例配置,展示模块的典型用法。
test/ 推荐 存放 Terratest 或 OPA 等测试框架的测试用例。
关键原则: 根目录下的 main.tf、variables.tf、outputs.tf 是模块的“三件套”,缺一不可。缺少 outputs.tf 的模块将无法被外部有效消费。

以下是一个标准模块目录的完整示例:

terraform-aws-vpc/
├── main.tf
├── variables.tf
├── outputs.tf
├── versions.tf
├── README.md
├── modules/
│   ├── public_subnet/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── private_subnet/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
├── examples/
│   ├── complete_vpc/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   └── minimal_vpc/
│       ├── main.tf
│       └── terraform.tfvars
└── test/
    ├── vpc_test.go
    └── fixtures/
        └── test_fixture.tf

4.2 根模块与子模块

在 Terraform 工作空间中,根模块 是执行 terraform apply 的入口目录,它负责组合与编排子模块。而 子模块 是被根模块或其他子模块通过 module 块引用的独立单元。

最佳实践: 根模块应保持“薄”而“声明式”,只负责传递变量、调用子模块、定义远程状态配置。所有具体的资源创建逻辑都应下沉到子模块中。

根模块与子模块的职责对比如下:

维度 根模块 子模块
执行入口 是,包含 terraform.tfstate 否,不直接执行
职责范围 编排、组合、传递上下文 封装单一资源或一组紧密耦合的资源
复用性 低,通常为项目定制 高,可在多个根模块间共享
版本管理 通常不独立版本化 通过 Git Tag 或注册表版本化
测试粒度 集成测试 单元测试 + 集成测试

以下是一个根模块调用子模块的典型代码片段:

# 根模块 main.tf
module "vpc" {
  source = "./modules/vpc"
  version = "1.2.0"

  vpc_name        = var.vpc_name
  cidr_block      = var.cidr_block
  enable_nat_gateway = true
}

module "ecs_cluster" {
  source = "terraform-aws-modules/ecs/aws"
  version = "5.0.0"

  cluster_name = "${var.environment}-ecs-cluster"
  vpc_id       = module.vpc.vpc_id
  subnet_ids   = module.vpc.private_subnet_ids
}
避坑提示: 子模块内部不应直接引用根模块的变量或数据源。所有外部输入必须通过 variables.tf 显式声明,所有对外输出必须通过 outputs.tf 暴露。这保证了子模块的完全独立性与可移植性。

4.3 模块注册表

Terraform 模块注册表(Registry)是模块的 中央分发与版本管理平台。它允许团队像使用包管理器一样搜索、安装和版本化基础设施模块。

模块注册表分为两类:

  • 公有注册表(Public Registry): 由 HashiCorp 官方维护,包含大量经过验证的社区模块(如 terraform-aws-modules/vpc/aws)。
  • 私有注册表(Private Registry): 企业版 Terraform Cloud / Enterprise 提供的功能,或通过自建 Git 仓库 + 版本标签实现类似效果。

模块在注册表中的命名规范遵循 terraform-<PROVIDER>-<NAME> 格式:

命名示例 Provider 模块用途
terraform-aws-vpc AWS 创建 VPC 及其子网、路由表
terraform-google-kubernetes Google Cloud 创建 GKE 集群
terraform-azurerm-network Azure 创建虚拟网络与子网

从注册表引用模块时,只需指定 source 与 version:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.0"

  name = "my-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["us-east-1a", "us-east-1b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]

  enable_nat_gateway = true
  enable_vpn_gateway = true

  tags = {
    Terraform   = "true"
    Environment = "dev"
  }
}
版本约束最佳实践: 在 versions.tf 中为所有引用的注册表模块设置 ~> x.y.z 的版本范围,避免意外升级引入破坏性变更。例如 version = "~> 5.1.0" 表示允许补丁版本升级,但不允许次要或主要版本升级。

下图展示了从模块开发到注册表发布,再到根模块消费的完整生命周期:

Terraform 模块生命周期:开发 → 注册表 → 消费 ① 模块开发 • 标准目录结构 • main.tf / variables.tf / outputs.tf • examples/ 示例配置 • test/ 测试用例 • README.md 文档 • versions.tf 版本约束 git tag ② 模块注册表 • 公有 Registry • 私有 Registry • 版本化管理 • 语义化版本 • 文档自动生成 • 搜索与发现 source + version ③ 根模块消费 • module 块引用 • 传递输入变量 • 引用输出值 • terraform init 下载 • terraform apply 部署 • 状态管理 反馈:缺陷修复 / 功能增强 → 新版本发布 模块注册表的核心价值 1. 版本锁定: 通过 version 约束确保基础设施的可复现性。 2. 文档即代码: 注册表自动解析 variables.tf 和 outputs.tf 生成文档。 3. 依赖解析: 自动处理模块间的嵌套依赖关系。 4. 团队协作: 私有注册表实现组织级模块共享与治理。

4.4 模块结构决策树

在实际项目中,如何决定一个模块的拆分粒度?以下决策树可帮助架构师快速判断:

  1. 该资源组是否会被多个环境(dev/staging/prod)复用? → 是,则封装为子模块。
  2. 该资源组是否包含超过 5 个关联资源? → 是,考虑拆分为嵌套子模块。
  3. 该资源组是否有独立的生命周期(如独立版本迭代)? → 是,发布到注册表。
  4. 该资源组是否需要独立的测试覆盖? → 是,在 test/ 目录添加测试。
  5. 该资源组是否依赖外部模块的输出? → 是,通过 module 块显式声明依赖。
经验法则: 一个模块的 main.tf 文件行数建议控制在 100-300 行之间。超过 300 行意味着模块职责过重,应考虑拆分为更细粒度的子模块。

通过严格遵循标准目录结构、清晰划分根模块与子模块的边界、并善用模块注册表进行版本管理,团队可以构建出高度可复用、可测试、可治理的 Terraform 基础设施代码库。这是实现 CI/CD 服务目录环境的基础设施即代码基石。