1. Terraform 模块基础:模块定义、模块结构、模块调用方式

Terraform 模块是基础设施即代码(IaC)的核心抽象单元,它允许你将一组相关的资源定义封装为可复用、可组合的组件。理解模块的基础概念是掌握版本管理与依赖更新的前提。

1.1 模块定义与核心价值

模块本质上是一个包含多个 .tf 或 .tf.json 文件的目录,这些文件共同定义了一个或多个基础设施资源。模块的价值体现在三个维度:

维度 说明 典型场景
封装性 隐藏内部实现细节,对外暴露清晰的输入输出接口 将 VPC、子网、路由表等网络资源打包为一个网络模块
复用性 同一模块可在不同环境(开发/测试/生产)或不同项目中重复使用 EC2 实例模块在多个微服务项目中共享
可组合性 模块可以嵌套调用,构建出更复杂的基础设施架构 应用模块调用网络模块和数据库模块
最佳实践:每个模块应遵循单一职责原则,只负责管理一类或一组紧密相关的资源。避免创建"万能模块"。

1.2 标准模块结构

一个规范的 Terraform 模块通常包含以下文件结构。虽然 Terraform 不强制要求所有文件都存在,但遵循社区标准能显著提升模块的可维护性。

modules/
└── ec2-instance/
    ├── main.tf          # 核心资源定义(必选)
    ├── variables.tf     # 输入变量声明(推荐)
    ├── outputs.tf       # 输出值定义(推荐)
    ├── versions.tf      # Terraform 与 Provider 版本约束(推荐)
    ├── README.md        # 模块文档(强烈推荐)
    └── examples/
        └── basic/
            ├── main.tf
            └── terraform.tfvars
文件 作用 是否必须
main.tf 定义模块管理的核心资源,如 aws_instance、aws_security_group 等 是
variables.tf 声明模块的输入参数,包括类型、默认值、描述和校验规则 推荐
outputs.tf 定义模块对外暴露的输出值,供调用方或其他模块引用 推荐
versions.tf 指定 Terraform 版本和 Provider 版本的约束条件 推荐
README.md 提供模块用途、使用示例、注意事项等文档 强烈推荐
关键点:versions.tf 是模块版本管理的基石。通过在模块内声明 required_version 和 required_providers,可以防止因 Terraform 或 Provider 版本不兼容导致的部署失败。

1.3 模块调用方式

Terraform 支持从多种来源调用模块,每种来源对应不同的版本管理策略。以下是四种主要的调用方式:

  1. 本地路径调用:适用于同一项目内的模块复用,路径相对于根模块的目录。
  2. Git 仓库调用:支持指定分支、标签或提交哈希,适合团队内部共享。
  3. Terraform Registry 调用:官方公共注册中心,支持语义化版本约束。
  4. HTTP URL 调用:从任意 HTTP 服务器下载模块归档文件。

以下代码展示了四种调用方式的语法:

# 方式一:本地路径调用
module "local_network" {
  source = "./modules/network"
  vpc_cidr = "10.0.0.0/16"
}

# 方式二:Git 仓库调用(指定标签)
module "git_ec2" {
  source = "git::https://github.com/example/terraform-aws-ec2.git?ref=v1.2.0"
  instance_type = "t3.micro"
}

# 方式三:Terraform Registry 调用(带版本约束)
module "registry_rds" {
  source  = "terraform-aws-modules/rds/aws"
  version = "~> 5.0"
  db_name = "appdb"
}

# 方式四:HTTP URL 调用
module "http_bucket" {
  source = "https://example.com/modules/s3-bucket.zip"
  bucket_name = "my-app-data"
}
版本管理警示:使用 ref 参数指向 Git 分支(如 ref=main)会导致每次 terraform init 拉取最新代码,破坏部署的可重复性。生产环境应始终使用语义化标签(如 ref=v1.2.0)。

1.4 模块调用与版本约束的关系

下图展示了模块调用时版本约束的解析流程,以及不同来源的版本锁定机制:

模块调用与版本约束解析流程 根模块 (Root Module) 解析 version / ref 约束 Terraform Registry 语义化版本 ~> 5.0 Git 仓库 标签 / 分支 / 提交 本地文件系统 相对路径 ./modules/ 生成 .terraform.lock.hcl Registry 使用语义化版本范围;Git 推荐使用标签锁定版本;本地路径无版本概念,依赖文件系统状态。 所有来源最终都会在 .terraform.lock.hcl 中记录实际下载的模块版本哈希。

1.5 模块输入与输出设计原则

良好的接口设计是模块可复用的关键。以下表格总结了输入变量和输出值的核心设计要点:

要素 设计原则 示例
输入变量 提供合理的默认值;使用 type 约束类型;添加 description;对敏感值使用 sensitive = true variable "instance_type" { type = string; default = "t3.micro"; description = "EC2 实例类型" }
输入校验 使用 validation 块对变量值进行运行时校验 condition = contains(["t3.micro","t3.small"], var.instance_type)
输出值 只输出调用方真正需要的信息;避免暴露内部敏感数据;使用 description 说明用途 output "instance_id" { value = aws_instance.this.id; description = "EC2 实例 ID" }
依赖传递 通过输出值显式传递依赖关系,避免隐式依赖 网络模块输出 vpc_id,应用模块通过 module.network.vpc_id 引用
实战建议:在模块的 variables.tf 中为每个变量添加 description 和 validation,这不仅能提升模块的可用性,还能在 CI/CD 流水线中提前捕获配置错误,避免部署到一半才发现参数不合法。

1.6 模块版本管理的起点

模块版本管理的第一步是在模块内部声明版本信息。以下是一个标准的 versions.tf 示例:

# modules/ec2-instance/versions.tf
terraform {
  required_version = ">= 1.3.0, < 2.0.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 4.0.0, < 6.0.0"
    }
  }
}

在调用方一侧,通过 version 参数约束模块版本:

# root/main.tf
module "ec2" {
  source  = "git::https://github.com/team/terraform-aws-ec2.git"
  version = "~> 2.0"  # 允许 >= 2.0.0 且 < 3.0.0

  instance_type = "t3.large"
}
版本约束符号速查:>= 1.0(大于等于)、~> 2.0(允许补丁版本升级)、!= 1.1.0(排除特定版本)。在模块版本管理中,~> 是最常用的约束符号,它在灵活性和稳定性之间取得了平衡。