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 支持从多种来源调用模块,每种来源对应不同的版本管理策略。以下是四种主要的调用方式:
- 本地路径调用:适用于同一项目内的模块复用,路径相对于根模块的目录。
- Git 仓库调用:支持指定分支、标签或提交哈希,适合团队内部共享。
- Terraform Registry 调用:官方公共注册中心,支持语义化版本约束。
- 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 模块调用与版本约束的关系
下图展示了模块调用时版本约束的解析流程,以及不同来源的版本锁定机制:
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(排除特定版本)。在模块版本管理中,~> 是最常用的约束符号,它在灵活性和稳定性之间取得了平衡。