模块
模块是 Terraform 中用于封装和复用配置的容器。通过模块,你可以将基础设施组件打包成可重用的单元,在多个项目或团队中共享。
什么是模块
模块是一组一起使用的 Terraform 配置文件集合。每个模块可以定义输入变量和输出值,使用者只需关心接口而不需要了解内部实现。
模块类型
- 根模块:运行
terraform apply的当前工作目录 - 子模块:被其他模块调用的模块
- 发布模块:发布到 Terraform Registry 的模块
模块结构
一个标准的模块包含以下文件:
my-module/
├── main.tf # 主要资源配置
├── variables.tf # 输入变量定义
├── outputs.tf # 输出值定义
├── README.md # 模块文档
└── examples/ # 使用示例
└── complete/
├── main.tf
└── variables.tf
创建模块
基本模块示例
创建一个 VPC 模块:
# modules/vpc/variables.tf
variable "vpc_cidr" {
description = "VPC 的 CIDR 块"
type = string
default = "10.0.0.0/16"
}
variable "availability_zones" {
description = "可用区列表"
type = list(string)
}
variable "environment" {
description = "环境名称"
type = string
}
# modules/vpc/main.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.environment}-vpc"
Environment = var.environment
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.environment}-igw"
Environment = var.environment
}
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.environment}-public-${count.index + 1}"
Environment = var.environment
Type = "public"
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.environment}-public-rt"
Environment = var.environment
}
}
resource "aws_route_table_association" "public" {
count = length(var.availability_zones)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
# modules/vpc/outputs.tf
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
}
output "vpc_cidr" {
description = "VPC CIDR 块"
value = aws_vpc.main.cidr_block
}
output "public_subnet_ids" {
description = "公共子网 ID 列表"
value = aws_subnet.public[*].id
}
output "internet_gateway_id" {
description = "互联网网关 ID"
value = aws_internet_gateway.main.id
}
模块文档
创建 README.md 说明模块用法:
# VPC 模块
创建包含公共子网的 VPC 网络基础设施。
## 用法
```hcl
module "vpc" {
source = "./modules/vpc"
vpc_cidr = "10.0.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b"]
environment = "production"
}
输入
| 名称 | 描述 | 类型 | 默认值 | 必需 |
|---|---|---|---|---|
| vpc_cidr | VPC CIDR 块 | string | "10.0.0.0/16" | 否 |
| availability_zones | 可用区列表 | list(string) | - | 是 |
| environment | 环境名称 | string | - | 是 |
输出
| 名称 | 描述 |
|---|---|
| vpc_id | VPC ID |
| public_subnet_ids | 公共子网 ID 列表 |
## 使用模块
### 本地模块
引用本地文件系统中的模块:
```hcl
module "vpc" {
source = "./modules/vpc"
vpc_cidr = "10.0.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b"]
environment = "dev"
}
module "security_group" {
source = "./modules/security-group"
name_prefix = "web"
vpc_id = module.vpc.vpc_id
}
Terraform Registry 模块
使用官方或社区模块:
# AWS VPC 官方模块
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
enable_vpn_gateway = false
tags = {
Terraform = "true"
Environment = "dev"
}
}
# 使用模块输出
resource "aws_instance" "web" {
subnet_id = module.vpc.public_subnets[0]
}
Git 仓库模块
从 Git 仓库引用模块:
# HTTPS
module "vpc" {
source = "github.com/example/terraform-modules//vpc?ref=v1.0.0"
vpc_cidr = "10.0.0.0/16"
}
# SSH
module "vpc" {
source = "[email protected]:example/terraform-modules.git//vpc?ref=v1.0.0"
vpc_cidr = "10.0.0.0/16"
}
# 特定分支
module "vpc" {
source = "github.com/example/terraform-modules//vpc?ref=develop"
}
S3 存储模块
从 S3 存储桶引用模块:
module "vpc" {
source = "s3::https://s3.amazonaws.com/my-terraform-modules/vpc.zip"
vpc_cidr = "10.0.0.0/16"
}
模块版本控制
版本约束
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0" # 兼容 5.x 版本
# 其他参数
}
版本约束语法:
| 约束 | 含义 |
|---|---|
= 1.2.3 | 精确匹配 |
!= 1.2.3 | 排除版本 |
> 1.2.3 | 大于 |
>= 1.2.3 | 大于或等于 |
< 1.2.3 | 小于 |
<= 1.2.3 | 小于或等于 |
~> 1.2.3 | 兼容版本 |
模块更新
# 检查模块更新
terraform init -upgrade
# 查看模块依赖
terraform providers
模块组合
多层架构示例
# main.tf
# 网络层
module "networking" {
source = "./modules/networking"
vpc_cidr = "10.0.0.0/16"
environment = var.environment
}
# 安全层
module "security" {
source = "./modules/security"
vpc_id = module.networking.vpc_id
}
# 计算层
module "compute" {
source = "./modules/compute"
subnet_ids = module.networking.private_subnet_ids
security_group_ids = [module.security.web_sg_id]
instance_count = var.instance_count
}
# 数据库层
module "database" {
source = "./modules/database"
subnet_ids = module.networking.database_subnet_ids
security_group_ids = [module.security.database_sg_id]
instance_class = var.db_instance_class
}
模块最佳实践
1. 单一职责原则
每个模块应该只做一件事,并做好:
# 好的做法:VPC 模块只管理网络
module "vpc" {
source = "./modules/vpc"
}
# 不好的做法:在一个模块中混合网络和计算资源
2. 清晰的接口设计
定义明确的输入和输出:
# variables.tf - 提供合理的默认值
variable "instance_type" {
description = "EC2 实例类型"
type = string
default = "t2.micro"
}
variable "tags" {
description = "资源标签"
type = map(string)
default = {}
}
# outputs.tf - 输出有用的信息
output "instance_id" {
description = "实例 ID"
value = aws_instance.main.id
}
output "private_ip" {
description = "私有 IP 地址"
value = aws_instance.main.private_ip
}
3. 输入验证
使用变量验证确保输入正确:
variable "environment" {
description = "环境名称"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "环境必须是 dev、staging 或 prod。"
}
}
variable "instance_count" {
description = "实例数量"
type = number
default = 1
validation {
condition = var.instance_count > 0 && var.instance_count <= 10
error_message = "实例数量必须在 1-10 之间。"
}
}
4. 使用局部值简化配置
locals {
common_tags = {
Environment = var.environment
ManagedBy = "Terraform"
Module = "vpc"
}
name_prefix = "${var.project_name}-${var.environment}"
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-vpc"
})
}
5. 模块文档
始终包含完整的文档:
## Requirements
| Name | Version |
|------|---------|
| terraform | >= 1.0 |
| aws | ~> 5.0 |
## Providers
| Name | Version |
|------|---------|
| aws | ~> 5.0 |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|----------|
| vpc_cidr | VPC CIDR block | string | "10.0.0.0/16" | no |
## Outputs
| Name | Description |
|------|-------------|
| vpc_id | The ID of the VPC |
条件模块
可选模块
variable "create_vpc" {
description = "是否创建 VPC"
type = bool
default = true
}
module "vpc" {
source = "./modules/vpc"
count = var.create_vpc ? 1 : 0
vpc_cidr = var.vpc_cidr
}
# 使用条件模块的输出
locals {
vpc_id = var.create_vpc ? module.vpc[0].vpc_id : var.existing_vpc_id
}
for_each 与模块
variable "environments" {
type = map(object({
cidr = string
azs = list(string)
instance = string
}))
default = {
dev = {
cidr = "10.1.0.0/16"
azs = ["us-east-1a"]
instance = "t2.micro"
}
prod = {
cidr = "10.2.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
instance = "t2.medium"
}
}
}
module "environment" {
source = "./modules/environment"
for_each = var.environments
name = each.key
vpc_cidr = each.value.cidr
availability_zones = each.value.azs
instance_type = each.value.instance
}
模块测试
使用 Terraform 测试框架
Terraform 1.6+ 引入了原生测试支持:
# tests/vpc.tftest.hcl
run "create_vpc" {
command = apply
variables {
vpc_cidr = "10.0.0.0/16"
availability_zones = ["us-east-1a"]
environment = "test"
}
assert {
condition = aws_vpc.main.cidr_block == "10.0.0.0/16"
error_message = "VPC CIDR 不匹配"
}
assert {
condition = length(aws_subnet.public) == 1
error_message = "应该创建 1 个公共子网"
}
}
run "validate_outputs" {
command = plan
assert {
condition = output.vpc_id != ""
error_message = "VPC ID 不应为空"
}
}
运行测试:
terraform test
发布模块
Terraform Registry
- 创建 GitHub 仓库,命名格式:
terraform-<PROVIDER>-<NAME> - 添加语义化版本标签(如
v1.0.0) - 模块会自动发布到 Terraform Registry
私有 Registry
使用 Terraform Cloud 或 Terraform Enterprise:
module "vpc" {
source = "app.terraform.io/my-org/vpc/aws"
version = "1.0.0"
# 配置
}
下一步
掌握了模块开发后,我们将学习 工作区与多环境管理,了解如何有效管理不同环境的基础设施配置。