模块组合与高级模式
掌握模块组合技巧和高级设计模式。
一、模块组合策略
1.1 单一职责模块
原则: 每个模块只做一件事,做好一件事。
# ✅ 好的设计:单一职责
module "vpc" {
source = "./modules/vpc"
# 只负责网络
}
module "database" {
source = "./modules/database"
# 只负责数据库
}
module "application" {
source = "./modules/application"
# 只负责应用
}
# ❌ 不好的设计:一个模块做所有事
module "everything" {
source = "./modules/all-in-one"
# VPC + 数据库 + 应用 + 监控...
}
1.2 模块分层
三层架构:
┌─────────────────────────────────────┐
│ Application Layer (L3) │ 业务逻辑
│ ┌──────────┐ ┌──────────┐ │
│ │ Web App │ │ API │ │
│ └──────────┘ └──────────┘ │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Infrastructure Layer (L2) │ 基础服务
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ EKS │ │ RDS │ │ S3 │ │
│ └──────┘ └──────┘ └──────┘ │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Foundation Layer (L1) │ 网络基础
│ ┌──────────────────────┐ │
│ │ VPC │ │
│ └──────────────────────┘ │
└─────────────────────────────────────┘
实现:
# L1: 基础层
module "foundation" {
source = "./modules/foundation"
vpc_cidr = "10.0.0.0/16"
availability_zones = ["us-west-2a", "us-west-2b"]
}
# L2: 基础设施层
module "infrastructure" {
source = "./modules/infrastructure"
vpc_id = module.foundation.vpc_id
subnet_ids = module.foundation.private_subnet_ids
}
# L3: 应用层
module "application" {
source = "./modules/application"
vpc_id = module.foundation.vpc_id
subnet_ids = module.foundation.private_subnet_ids
database_endpoint = module.infrastructure.database_endpoint
}
1.3 模块组合模式
垂直组合(Vertical Composition):
# 高层模块调用低层模块
module "complete_stack" {
source = "./modules/complete-stack"
# 内部调用多个子模块
# - vpc
# - database
# - application
}
水平组合(Horizontal Composition):
# 根模块组合多个独立模块
module "vpc" {
source = "./modules/vpc"
}
module "database" {
source = "./modules/database"
vpc_id = module.vpc.vpc_id
}
module "application" {
source = "./modules/application"
vpc_id = module.vpc.vpc_id
db_endpoint = module.database.endpoint
}
二、高级模块模式
2.1 Wrapper 模块
目的: 为第三方模块提供定制化封装。
# modules/vpc-wrapper/main.tf
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
# 标准化配置
name = "${var.project_name}-${var.environment}-vpc"
cidr = var.vpc_cidr
# 企业标准
enable_dns_hostnames = true
enable_dns_support = true
enable_nat_gateway = var.environment == "prod"
# 统一标签
tags = merge(
var.tags,
{
ManagedBy = "Terraform"
Module = "vpc-wrapper"
Environment = var.environment
}
)
# 其他参数传递
azs = var.availability_zones
private_subnets = var.private_subnet_cidrs
public_subnets = var.public_subnet_cidrs
}
# 输出代理
output "vpc_id" {
value = module.vpc.vpc_id
}
output "private_subnet_ids" {
value = module.vpc.private_subnets
}
2.2 Factory 模块
目的: 根据配置创建不同类型的资源。
# modules/environment-factory/main.tf
locals {
environments = {
dev = {
instance_type = "t3.micro"
instance_count = 1
enable_backup = false
enable_monitoring = false
}
staging = {
instance_type = "t3.small"
instance_count = 2
enable_backup = true
enable_monitoring = true
}
prod = {
instance_type = "t3.large"
instance_count = 3
enable_backup = true
enable_monitoring = true
}
}
env_config = local.environments[var.environment]
}
module "vpc" {
source = "./modules/vpc"
vpc_name = "${var.project_name}-${var.environment}"
# ... 其他配置
}
module "application" {
source = "./modules/application"
instance_type = local.env_config.instance_type
instance_count = local.env_config.instance_count
# ... 其他配置
}
module "monitoring" {
count = local.env_config.enable_monitoring ? 1 : 0
source = "./modules/monitoring"
# ... 配置
}
使用:
module "dev_env" {
source = "./modules/environment-factory"
project_name = "myapp"
environment = "dev"
}
module "prod_env" {
source = "./modules/environment-factory"
project_name = "myapp"
environment = "prod"
}
2.3 Composite 模块
目的: 组合多个相关模块形成完整解决方案。
# modules/web-application/main.tf
# 网络层
module "network" {
source = "../vpc"
vpc_name = var.app_name
vpc_cidr = var.vpc_cidr
availability_zones = var.availability_zones
public_subnet_cidrs = var.public_subnet_cidrs
private_subnet_cidrs = var.private_subnet_cidrs
}
# 安全层
module "security" {
source = "../security-groups"
vpc_id = module.network.vpc_id
app_name = var.app_name
}
# 数据库层
module "database" {
source = "../rds"
db_name = var.app_name
vpc_id = module.network.vpc_id
subnet_ids = module.network.private_subnet_ids
security_group_ids = [module.security.database_sg_id]
instance_class = var.db_instance_class
}
# 应用层
module "application" {
source = "../ecs-service"
app_name = var.app_name
vpc_id = module.network.vpc_id
subnet_ids = module.network.private_subnet_ids
security_group_ids = [module.security.app_sg_id]
database_url = module.database.connection_string
}
# 负载均衡
module "load_balancer" {
source = "../alb"
app_name = var.app_name
vpc_id = module.network.vpc_id
subnet_ids = module.network.public_subnet_ids
security_group_ids = [module.security.alb_sg_id]
target_group_arn = module.application.target_group_arn
}
# 监控
module "monitoring" {
source = "../cloudwatch"
app_name = var.app_name
resources = {
alb_arn = module.load_balancer.alb_arn
db_id = module.database.db_instance_id
ecs_cluster = module.application.cluster_name
}
}
使用:
module "my_web_app" {
source = "./modules/web-application"
app_name = "myapp"
vpc_cidr = "10.0.0.0/16"
availability_zones = ["us-west-2a", "us-west-2b"]
db_instance_class = "db.t3.micro"
}
output "application_url" {
value = module.my_web_app.load_balancer_dns
}
2.4 Multi-Provider 模块
目的: 同时管理多个云平台资源。
# modules/multi-cloud-dns/main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
# AWS Route53
resource "aws_route53_zone" "main" {
count = var.use_aws ? 1 : 0
name = var.domain_name
}
resource "aws_route53_record" "app" {
count = var.use_aws ? 1 : 0
zone_id = aws_route53_zone.main[0].zone_id
name = var.subdomain
type = "CNAME"
ttl = 300
records = [var.target_dns]
}
# Cloudflare DNS
resource "cloudflare_zone" "main" {
count = var.use_cloudflare ? 1 : 0
zone = var.domain_name
}
resource "cloudflare_record" "app" {
count = var.use_cloudflare ? 1 : 0
zone_id = cloudflare_zone.main[0].id
name = var.subdomain
value = var.target_dns
type = "CNAME"
proxied = true
}
三、模块间通信
3.1 显式依赖
module "database" {
source = "./modules/database"
# ... 配置
}
module "application" {
source = "./modules/application"
# 显式依赖:通过输出传递
db_endpoint = module.database.endpoint
# 确保数据库先创建
depends_on = [module.database]
}
3.2 隐式依赖
module "vpc" {
source = "./modules/vpc"
}
module "application" {
source = "./modules/application"
# 隐式依赖:通过引用 VPC 输出
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
}
3.3 数据传递模式
向下传递(Pass-down):
variable "common_tags" {
type = map(string)
}
module "vpc" {
source = "./modules/vpc"
tags = var.common_tags
}
module "database" {
source = "./modules/database"
tags = var.common_tags
}
向上传递(Bubble-up):
module "application" {
source = "./modules/application"
}
output "app_url" {
value = module.application.load_balancer_dns
}
横向传递(Sibling):
module "database" {
source = "./modules/database"
}
module "application" {
source = "./modules/application"
db_endpoint = module.database.endpoint # 从兄弟模块获取
}
四、模块配置模式
4.1 配置对象模式
# modules/eks-cluster/variables.tf
variable "cluster_config" {
description = "EKS 集群配置"
type = object({
version = string
endpoint_private = bool
endpoint_public = bool
log_types = list(string)
addon_versions = map(string)
})
default = {
version = "1.28"
endpoint_private = true
endpoint_public = false
log_types = ["api", "audit"]
addon_versions = {
coredns = "v1.10.1-eksbuild.2"
kube-proxy = "v1.28.1-eksbuild.1"
vpc-cni = "v1.14.1-eksbuild.1"
}
}
}
# 使用
module "eks" {
source = "./modules/eks-cluster"
cluster_config = {
version = "1.29"
endpoint_private = true
endpoint_public = true
log_types = ["api", "audit", "authenticator"]
addon_versions = {
coredns = "v1.10.1-eksbuild.2"
kube-proxy = "v1.28.1-eksbuild.1"
vpc-cni = "v1.15.0-eksbuild.1"
}
}
}
4.2 Feature Flags 模式
# modules/application/variables.tf
variable "features" {
description = "功能开关"
type = object({
enable_monitoring = bool
enable_backup = bool
enable_autoscaling = bool
enable_cdn = bool
})
default = {
enable_monitoring = false
enable_backup = false
enable_autoscaling = false
enable_cdn = false
}
}
# modules/application/main.tf
module "monitoring" {
count = var.features.enable_monitoring ? 1 : 0
source = "./modules/monitoring"
# ... 配置
}
module "backup" {
count = var.features.enable_backup ? 1 : 0
source = "./modules/backup"
# ... 配置
}
resource "aws_autoscaling_group" "app" {
count = var.features.enable_autoscaling ? 1 : 0
# ... 配置
}
4.3 环境配置模式
# environments/dev.tfvars
environment = "dev"
cluster_config = {
node_count = 2
instance_type = "t3.small"
disk_size = 20
}
features = {
enable_monitoring = false
enable_backup = false
enable_autoscaling = false
}
# environments/prod.tfvars
environment = "prod"
cluster_config = {
node_count = 5
instance_type = "t3.large"
disk_size = 100
}
features = {
enable_monitoring = true
enable_backup = true
enable_autoscaling = true
}
五、模块测试
5.1 单元测试
使用 Terratest(Go):
// test/vpc_test.go
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestVPCModule(t *testing.T) {
t.Parallel()
terraformOptions := &terraform.Options{
TerraformDir: "../modules/vpc",
Vars: map[string]interface{}{
"vpc_name": "test-vpc",
"vpc_cidr": "10.0.0.0/16",
"availability_zones": []string{"us-west-2a", "us-west-2b"},
},
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
// 验证输出
vpcId := terraform.Output(t, terraformOptions, "vpc_id")
assert.NotEmpty(t, vpcId)
subnetIds := terraform.OutputList(t, terraformOptions, "public_subnet_ids")
assert.Equal(t, 2, len(subnetIds))
}
5.2 集成测试
func TestCompleteStack(t *testing.T) {
t.Parallel()
terraformOptions := &terraform.Options{
TerraformDir: "../examples/complete",
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
// 测试应用是否可访问
appUrl := terraform.Output(t, terraformOptions, "application_url")
http_helper.HttpGetWithRetry(
t,
fmt.Sprintf("http://%s/health", appUrl),
nil,
200,
"OK",
30,
10*time.Second,
)
}
5.3 合规性测试
使用 Open Policy Agent (OPA):
# policy/vpc.rego
package terraform.vpc
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_vpc"
not resource.change.after.enable_dns_hostnames
msg := sprintf(
"VPC %s must have DNS hostnames enabled",
[resource.address]
)
}
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_subnet"
resource.change.after.map_public_ip_on_launch == true
msg := sprintf(
"Subnet %s should not auto-assign public IPs",
[resource.address]
)
}
测试:
terraform plan -out=tfplan
terraform show -json tfplan > plan.json
opa eval -d policy/ -i plan.json "data.terraform.vpc.deny"
六、性能优化
6.1 减少 API 调用
# ❌ 每次都查询 AMI
data "aws_ami" "ubuntu" {
most_recent = true
# ... 过滤条件
}
resource "aws_instance" "app" {
count = 10
ami = data.aws_ami.ubuntu.id # 查询一次,使用多次
}
# ✅ 使用变量传递
variable "ami_id" {
type = string
}
resource "aws_instance" "app" {
count = 10
ami = var.ami_id
}
6.2 并行化执行
# Terraform 自动并行化独立资源
resource "aws_subnet" "public" {
count = 3 # 3 个子网并行创建
# ...
}
# 使用 -parallelism 控制并发数
# terraform apply -parallelism=20
6.3 缓存和复用
# 使用 locals 缓存计算结果
locals {
subnet_cidrs = cidrsubnets(var.vpc_cidr, 8, 8, 8, 8)
common_tags = merge(
var.tags,
{
ManagedBy = "Terraform"
Timestamp = timestamp()
}
)
}
resource "aws_subnet" "public" {
count = 4
cidr_block = local.subnet_cidrs[count.index]
tags = local.common_tags
}
小结
模块组合和高级模式的关键要点:
- 组合策略:单一职责、分层设计、垂直/水平组合
- 设计模式:Wrapper、Factory、Composite、Multi-Provider
- 通信模式:显式/隐式依赖、数据传递
- 配置模式:配置对象、Feature Flags、环境配置
- 测试策略:单元测试、集成测试、合规性测试
- 性能优化:减少 API 调用、并行化、缓存
掌握这些模式可以构建更灵活、可维护的基础设施代码!