1. 模块(Modules)
1.1. module简介
Terraform 模块是一组位于单个目录中的 Terraform 配置文件,即使是一个包含了一个或多个 .tf 文件的单一目录的简单配置也被视为一个模块。模块是 Terraform 中代码复用的主要方法,它们通过指定可以检索代码的源来重复使用。
模块可以多层嵌套,类似代码中的函数。
Module 就是一组 .tf 文件的集合,用来创建一组相关的基础设施资源。
比如:
- 一个
vpc 模块:创建 VPC、子网、路由表
- 一个
rds 模块:创建数据库实例、安全组
- 一个
eks 模块:创建 Kubernetes 集群
1.2. Module 的基本结构
1 2 3 4 5
| modules/ └── vpc/ ├── main.tf ├── variables.tf └── outputs.tf
|
modules/vpc/main.tf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| resource "aws_vpc" "main" { cidr_block = var.cidr_block
tags = { Name = var.name } }
resource "aws_subnet" "private" { count = length(var.private_subnets)
vpc_id = aws_vpc.main.id cidr_block = var.private_subnets[count.index] availability_zone = element(var.azs, count.index)
tags = { Name = "private-${element(var.azs, count.index)}" } }
|
variables.tf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| variable "cidr_block" { type = string }
variable "name" { type = string }
variable "private_subnets" { type = list(string) }
variable "azs" { type = list(string) }
|
outputs.tf
1 2 3 4 5 6 7
| output "vpc_id" { value = aws_vpc.main.id }
output "private_subnet_ids" { value = aws_subnet.private[*].id }
|
1.3. 在根模块中使用 Module
方法 1:本地模块
1 2 3 4 5 6 7 8 9
| module "prod_vpc" { source = "./modules/vpc"
cidr_block = "10.0.0.0/16" name = "prod-vpc"
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] azs = ["us-west-2a", "us-west-2b", "us-west-2c"] }
|
方法 2:远程模块
1 2 3 4 5 6 7
| module "vpc" { source = "git::https://github.com/your-org/terraform-modules.git//vpc?ref=v1.0.0"
cidr_block = "10.10.0.0/16" name = "dev-vpc" # ... 其他参数 }
|
支持来源类型:
./path/to/module — 本地
github.com/org/repo//dir
git::https://...
terraform-registry-mirror/internal/modules/vpc
1.4. Module 的输入与输出
1. 输入(Input Variables)
通过 variables.tf 定义参数,在调用时传入。
1 2 3 4 5
| module "xxx" { source = "..." param1 = "value1" param2 = true }
|
2. 输出(Outputs)
其他模块或根配置可以通过 module.<name>.<output> 引用输出值。
1 2 3 4 5
| # 假设 module.vpc 输出了 vpc_id 和 subnet_ids resource "aws_instance" "app" { subnet_id = module.prod_vpc.private_subnet_ids[0] # ... }
|
1.5. 实际使用场景示例
VPC 模块创建网络,RDS 模块使用它的输出来部署数据库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| # main.tf module "network" { source = "./modules/vpc"
cidr_block = "10.0.0.0/16" name = "my-app-network" private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] azs = ["us-west-2a", "us-west-2b"] }
module "database" { source = "./modules/rds"
vpc_id = module.network.vpc_id subnet_ids = module.network.private_subnet_ids instance_class = "db.t3.micro" }
|
2. 资源依赖
Terraform 有两种依赖关系:隐式依赖(Implicit Dependence)和`显式依赖``(Explicit Dependence)。隐式依赖是 Terraform 已知的,而显式依赖是未知的。
2.1. 隐式依赖(Implicit Dependence)
隐式依赖简单来讲就是对于变量引用的依赖。当一个资源的创建依赖于另一个资源创建后的信息时,需要用到隐式依赖来让 Terraform 知晓依赖关系。
针对隐式依赖关系,Terraform 通过属性值引用赋值的方式来知晓。
2.2. 显式依赖(Explicit Dependence)
显式依赖就是Terraform无法通过变量之间的引用关系推断出来,需要用户明确指出的依赖。可以使用 depends_on 来显式声明依赖关系。无论资源类型是什么,depends_on 可以在模块内使用,该值可以是指向资源的表达式。
2.3. 平级module的变量引用
Terraform 中:Module 之间不能直接访问对方的内部资源或输出,除非通过根模块中显式传递。
但你可以通过 根模块作为“中介”,将一个 module 的输出传给另一个 module 作为输入。
示例:两个平级模块通信
假设我们有两个模块:
vpc:创建 VPC 和子网
eks:创建 EKS 集群,需要使用 VPC 创建的子网 ID
1 2 3 4 5 6 7 8 9 10 11
| . ├── main.tf ├── variables.tf ├── outputs.tf └── modules/ ├── vpc/ │ ├── main.tf │ └── outputs.tf └── eks/ ├── main.tf └── variables.tf
|
步骤 1:为 vpc 模块定义输出(modules/vpc/outputs.tf)
1 2 3 4 5 6 7
| output "private_subnet_ids" { value = aws_subnet.private[*].id }
output "vpc_id" { value = aws_vpc.main.id }
|
步骤 2:为 eks 模块定义输入变量(modules/eks/variables.tf)
1 2 3 4 5 6 7
| variable "vpc_id" { type = string }
variable "subnet_ids" { type = list(string) }
|
步骤 3:在根模块中调用并连接两个模块(main.tf)
1 2 3 4 5 6 7 8 9 10 11 12 13
| # 创建 VPC 模块 module "network" { source = "./modules/vpc" # 输入变量... }
# 创建 EKS 模块,使用 network 模块的输出作为输入 module "cluster" { source = "./modules/eks"
vpc_id = module.network.vpc_id subnet_ids = module.network.private_subnet_ids }
|
这样就实现了:eks 模块使用了平级 vpc 模块的输出值。
2.4. 父级模块的变量引用
核心原则:Terraform 模块的引用方向是 单向向下的。
🔹 Terraform 只能引用当前模块直接调用的“子模块”的 output 字段。
🔹 不能向上引用父级、也不能横向引用兄弟模块。
🔹 所有跨模块通信都必须通过“调用者”显式传递(通常是根模块)。
依赖必须由“上层模块”显式传递,通过 根模块中转
1 2 3 4 5 6 7 8 9 10
| module "A" { source = "./modules/a" }
module "B" { source = "./modules/b"
# 正确!根模块把 A 的输出传给 B 的输入 vpc_id = module.A.vpc_id }
|
2.5. 推荐架构设计原则
-
扁平化优于深层嵌套
- 尽量让多个模块由根模块统一调用
- 减少层级,便于管理依赖
-
接口清晰化
- 每个模块通过
outputs.tf 明确暴露哪些值
- 通过
variables.tf 明确需要哪些输入
-
避免循环依赖
- A → B → C → A 是非法的
- 如果出现,说明模块职责不清,需重构
-
用 locals 或 variables 统一配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| locals { common_tags = { Environment = "prod" } }
module "vpc" { source = "./modules/vpc" tags = local.common_tags }
module "rds" { source = "./modules/rds" tags = local.common_tags }
|
参考: