跳到主要内容

模块

模块是 Terraform 中用于封装和复用配置的容器。通过模块,你可以将基础设施组件打包成可重用的单元,在多个项目或团队中共享。

什么是模块

模块是一组一起使用的 Terraform 配置文件集合。每个模块可以定义输入变量和输出值,使用者只需关心接口而不需要了解内部实现。

模块类型

  1. 根模块:运行 terraform apply 的当前工作目录
  2. 子模块:被其他模块调用的模块
  3. 发布模块:发布到 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_cidrVPC CIDR 块string"10.0.0.0/16"
availability_zones可用区列表list(string)-
environment环境名称string-

输出

名称描述
vpc_idVPC 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

  1. 创建 GitHub 仓库,命名格式:terraform-<PROVIDER>-<NAME>
  2. 添加语义化版本标签(如 v1.0.0
  3. 模块会自动发布到 Terraform Registry

私有 Registry

使用 Terraform Cloud 或 Terraform Enterprise:

module "vpc" {
source = "app.terraform.io/my-org/vpc/aws"
version = "1.0.0"

# 配置
}

下一步

掌握了模块开发后,我们将学习 工作区与多环境管理,了解如何有效管理不同环境的基础设施配置。