← 返回首页
🔧

Terraform模块:代码复用与组织

📂 devops ⏱ 4 min 661 words

Terraform模块:代码复用与组织

什么是Terraform模块

模块是Terraform中实现代码复用的基本单元。它是一组资源和配置的集合,可以被多个项目重复使用。模块化设计能提高代码的可维护性、可测试性和可复用性。

创建模块

模块目录结构

modules/
├── vpc/
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   └── versions.tf
├── ec2/
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   └── versions.tf
└── rds/
    ├── main.tf
    ├── variables.tf
    ├── outputs.tf
    └── versions.tf

VPC模块示例

# modules/vpc/main.tf

resource "aws_vpc" "this" {
  cidr_block           = var.cidr_block
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(var.tags, {
    Name = "${var.project_name}-vpc"
  })
}

resource "aws_subnet" "public" {
  count = length(var.public_subnets)
  
  vpc_id                  = aws_vpc.this.id
  cidr_block              = var.public_subnets[count.index].cidr
  availability_zone       = var.public_subnets[count.index].az
  map_public_ip_on_launch = true

  tags = merge(var.tags, {
    Name = "${var.project_name}-public-${count.index + 1}"
  })
}

resource "aws_subnet" "private" {
  count = length(var.private_subnets)
  
  vpc_id            = aws_vpc.this.id
  cidr_block        = var.private_subnets[count.index].cidr
  availability_zone = var.private_subnets[count.index].az

  tags = merge(var.tags, {
    Name = "${var.project_name}-private-${count.index + 1}"
  })
}

resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.this.id

  tags = merge(var.tags, {
    Name = "${var.project_name}-igw"
  })
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.this.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.this.id
  }

  tags = merge(var.tags, {
    Name = "${var.project_name}-public-rt"
  })
}

resource "aws_route_table_association" "public" {
  count          = length(var.public_subnets)
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}
# modules/vpc/variables.tf

variable "project_name" {
  description = "项目名称"
  type        = string
}

variable "cidr_block" {
  description = "VPC CIDR块"
  type        = string
  default     = "10.0.0.0/16"
  
  validation {
    condition     = can(cidrhost(var.cidr_block, 0))
    error_message = "必须是有效的CIDR块"
  }
}

variable "public_subnets" {
  description = "公有子网配置"
  type = list(object({
    cidr = string
    az   = string
  }))
}

variable "private_subnets" {
  description = "私有子网配置"
  type = list(object({
    cidr = string
    az   = string
  }))
}

variable "tags" {
  description = "资源标签"
  type        = map(string)
  default     = {}
}
# modules/vpc/outputs.tf

output "vpc_id" {
  description = "VPC ID"
  value       = aws_vpc.this.id
}

output "vpc_cidr" {
  description = "VPC CIDR块"
  value       = aws_vpc.this.cidr_block
}

output "public_subnet_ids" {
  description = "公有子网ID列表"
  value       = aws_subnet.public[*].id
}

output "private_subnet_ids" {
  description = "私有子网ID列表"
  value       = aws_subnet.private[*].id
}

output "internet_gateway_id" {
  description = "互联网网关ID"
  value       = aws_internet_gateway.this.id
}

使用模块

本地模块

# main.tf

module "vpc" {
  source = "./modules/vpc"
  
  project_name = "my-app"
  cidr_block   = "10.0.0.0/16"
  
  public_subnets = [
    { cidr = "10.0.1.0/24", az = "us-west-2a" },
    { cidr = "10.0.2.0/24", az = "us-west-2b" },
  ]
  
  private_subnets = [
    { cidr = "10.0.10.0/24", az = "us-west-2a" },
    { cidr = "10.0.11.0/24", az = "us-west-2b" },
  ]
  
  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

module "ec2" {
  source = "./modules/ec2"
  
  instance_count = 2
  instance_type  = "t3.micro"
  subnet_ids     = module.vpc.private_subnet_ids
  vpc_id         = module.vpc.vpc_id
}

远程模块

# 使用Git仓库
module "vpc" {
  source = "git::https://github.com/company/terraform-modules.git//vpc?ref=v1.2.0"
  
  # 模块参数...
}

# 使用Terraform Registry
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"
  
  name = "my-vpc"
  cidr = "10.0.0.0/16"
  
  azs             = ["us-west-2a", "us-west-2b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]
}

S3模块存储

# 使用S3存储模块
module "vpc" {
  source = "s3::https://bucket-name.s3.amazonaws.com/modules/vpc.zip"
  
  # 模块参数...
}

模块版本管理

版本约束

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"  # 兼容5.x版本
  
  # 或精确版本
  # version = "= 5.0.0"
  
  # 或版本范围
  # version = ">= 5.0, < 6.0"
}

版本锁定

# 更新模块到最新版本
terraform get -update

# 锁定模块版本
terraform providers lock -platform=linux_amd64 -platform=darwin_amd64

模块测试

测试文件结构

modules/vpc/
├── main.tf
├── variables.tf
├── outputs.tf
├── versions.tf
└── tests/
    ├── main_test.go
    └── terraform_test.go

使用Terratest

package test

import (
  "testing"
  "github.com/gruntwork-io/terratest/modules/terraform"
  "github.com/stretchr/testify/assert"
)

func TestVpcModule(t *testing.T) {
  terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
    TerraformDir: "../",
    Vars: map[string]interface{}{
      "project_name": "test-vpc",
      "cidr_block":   "10.0.0.0/16",
      "public_subnets": []map[string]string{
        {"cidr": "10.0.1.0/24", "az": "us-west-2a"},
      },
      "private_subnets": []map[string]string{
        {"cidr": "10.0.10.0/24", "az": "us-west-2a"},
      },
    },
  })

  defer terraform.Destroy(t, terraformOptions)
  terraform.InitAndApply(t, terraformOptions)

  vpcId := terraform.Output(t, terraformOptions, "vpc_id")
  assert.Regexp(t, "^vpc-", vpcId)
  
  publicSubnetIds := terraform.OutputList(t, terraformOptions, "public_subnet_ids")
  assert.Equal(t, 1, len(publicSubnetIds))
}

模块发布

发布到Terraform Registry

# 1. 创建GitHub仓库
git init
git add .
git commit -m "Initial module"

# 2. 打版本标签
git tag v1.0.0
git push origin v1.0.0

# 3. 在Terraform Registry中连接仓库
# 访问 https://app.terraform.io/ 托管注册表
# 或使用公共注册表 https://registry.terraform.io/

模块文档

# VPC Module

## Description
创建标准VPC配置,包括公有/私有子网、互联网网关和路由表。

## Usage
\```hcl
module "vpc" {
  source = "company/vpc/aws"
  version = "1.0.0"
  
  project_name = "my-app"
  cidr_block   = "10.0.0.0/16"
}
\```

## Inputs
| Name | Description | Type | Default |
|------|-------------|------|---------|
| project_name | 项目名称 | string | - |
| cidr_block | VPC CIDR块 | string | "10.0.0.0/16" |

## Outputs
| Name | Description |
|------|-------------|
| vpc_id | VPC ID |
| public_subnet_ids | 公有子网ID列表 |

最佳实践

  1. 单一职责:每个模块专注于一个功能
  2. 清晰接口:定义明确的输入输出
  3. 版本管理:使用语义化版本控制
  4. 完整文档:提供使用示例和输入输出说明
  5. 自动化测试:编写单元测试和集成测试