
Terraform Module 模块化实战从零封装可复用基础设施的6步指南 模块工作原理流程图一、核心概念模块的本质与分类1. 什么是模块2. 模块的三种类型二、六步创建你的第一个模块以VPC模块为例步骤1设计模块的输入与输出步骤2创建模块目录结构步骤3编写 variables.tf输入变量声明步骤4编写 main.tf核心资源定义步骤5编写 outputs.tf输出值声明步骤6在根模块中调用模块三、模块的进阶用法1. 从Terraform Registry引用公共模块2. 模块嵌套子模块调用子模块3. 使用 for_each 动态创建模块实例四、模块发布到私有Registry 模块优势总结 常见问题排查 总结The Begin点点关注收藏不迷路⬇ ⬇ 底部 ⬇ ⬇当你用Terraform管理了3个AWS服务后你会发现VPC的配置代码在dev目录写了一遍在prod目录又复制了一遍。当需要修改VPC的CIDR计算逻辑时你必须同时修改两处——这正是**代码重复**的典型困境。Module模块是Terraform解决代码复用的核心机制。它就像编程语言中的函数——把一组相关的资源封装起来暴露参数和返回值在需要时调用而非复制。 模块工作原理流程图先通过一张图理解Terraform模块的调用关系和输入输出机制 根模块 Root Modulemain.tf调用子模块 模块A: networking封装VPC子网NAT 模块B: compute封装EC2ASGELB 模块C: database封装RDS参数组 传入 Input Variables 输出 Outputsvpc_id, subnet_ids 输出 Outputsinstance_ids, elb_dns 输出 Outputsdb_endpoint, db_port根模块接收输出值根模块使用输出如配置安全组规则核心原理模块是一个黑盒——你传入参数Input Variables它创建资源然后暴露有用的信息Outputs。你不需要关心模块内部如何实现只需要知道传什么进去拿什么出来。一、核心概念模块的本质与分类1. 什么是模块模块是**一组Terraform配置文件的集合**通常封装了某个特定功能的基础设施。类比理解编程概念Terraform概念函数模块函数参数Input Variables函数返回值Outputs函数调用module xxx {}块函数库Module Registry2. 模块的三种类型类型说明示例根模块Root Module工作目录下的.tf文件你运行terraform apply的目录子模块Child Module被根模块调用的模块module vpc { source ./modules/vpc }发布模块Published Module上传到Registry的模块source terraform-aws-modules/vpc/aws核心理解每个Terraform工作目录本身就是一个根模块。当你在其中使用module块时你就在调用子模块。二、六步创建你的第一个模块以VPC模块为例步骤1设计模块的输入与输出在编写代码之前先明确模块的接口契约输入变量类型说明示例值vpc_namestringVPC名称production-vpcvpc_cidrstringVPC CIDR范围10.0.0.0/16public_subnet_cidrslist(string)公有子网CIDR列表[10.0.1.0/24, 10.0.2.0/24]enable_nat_gatewaybool是否创建NAT网关true输出值说明用途vpc_idVPC ID供其他资源引用public_subnet_ids公有子网ID列表EC2实例部署位置步骤2创建模块目录结构# 创建模块目录mkdir-pterraform-modules/vpccdterraform-modules/vpc# 模块标准文件结构touchmain.tf# 核心资源定义touchvariables.tf# 输入变量声明touchoutputs.tf# 输出值声明touchversions.tf# Terraform版本约束推荐标准模块目录结构terraform-modules/ └── vpc/ ├── main.tf # 资源定义 ├── variables.tf # 输入变量 ├── outputs.tf # 输出值 └── versions.tf # Provider版本约束步骤3编写 variables.tf输入变量声明variables.tf# 必填变量 variable vpc_name { description VPC名称用于资源命名和标签 type string # 没有default 调用时必须传入 } variable vpc_cidr { description VPC的CIDR地址范围 type string default 10.0.0.0/16 # 有默认值 可选传入 } # 可选变量 variable public_subnet_cidrs { description 公有子网的CIDR列表 type list(string) default [10.0.1.0/24, 10.0.2.0/24] } variable enable_nat_gateway { description 是否创建NAT网关生产环境建议开启 type bool default false # 默认关闭省钱 } variable tags { description 额外标签 type map(string) default {} }步骤4编写 main.tf核心资源定义main.tf# VPC resource aws_vpc this { cidr_block var.vpc_cidr enable_dns_hostnames true enable_dns_support true tags merge( { Name var.vpc_name Environment var.vpc_name # 可扩展为独立变量 ManagedBy Terraform }, var.tags ) } # 公有子网 resource aws_subnet public { count length(var.public_subnet_cidrs) vpc_id aws_vpc.this.id cidr_block var.public_subnet_cidrs[count.index] availability_zone data.aws_availability_zones.available.names[count.index] map_public_ip_on_launch true tags { Name ${var.vpc_name}-public-${count.index 1} Environment var.vpc_name ManagedBy Terraform } } # 互联网网关 resource aws_internet_gateway this { vpc_id aws_vpc.this.id tags { Name ${var.vpc_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 { Name ${var.vpc_name}-public-rt } } resource aws_route_table_association public { count length(var.public_subnet_cidrs) subnet_id aws_subnet.public[count.index].id route_table_id aws_route_table.public.id } # 数据源可用区 data aws_availability_zones available { state available }步骤5编写 outputs.tf输出值声明outputs.tfoutput vpc_id { description VPC的唯一标识符 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 # 使用splat表达式 } output internet_gateway_id { description 互联网网关ID value aws_internet_gateway.this.id }步骤6在根模块中调用模块根模块 main.tf调用VPC模块# 调用本地模块 module vpc { source ./terraform-modules/vpc # 本地路径 vpc_name production-vpc vpc_cidr 10.100.0.0/16 public_subnet_cidrs [10.100.1.0/24, 10.100.2.0/24, 10.100.3.0/24] enable_nat_gateway true } # 使用模块输出 resource aws_instance web { ami ami-0c55b159cbfafe1f0 instance_type t3.medium subnet_id module.vpc.public_subnet_ids[0] # 引用模块输出 tags { Name web-server } }执行命令# 初始化下载Provider和模块terraform init# 查看模块输出terraform output# 输出# vpc_id vpc-0abc123def456ghi# public_subnet_ids [# subnet-0aaa111bbb,# subnet-0ccc222ddd,# subnet-0eee333fff# ]三、模块的进阶用法1. 从Terraform Registry引用公共模块无需重复造轮子社区已经贡献了大量高质量模块。调用官方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 single_nat_gateway true # 生产环境建议false }2. 模块嵌套子模块调用子模块场景一个应用模块内部调用VPC模块 RDS模块 EC2模块。# modules/application/main.tf module vpc { source ../vpc # ... VPC配置 } module database { source ../database vpc_id module.vpc.vpc_id # ... RDS配置 } module compute { source ../compute vpc_id module.vpc.vpc_id db_endpoint module.database.endpoint # ... EC2配置 }3. 使用for_each动态创建模块实例# 为3个环境创建相同的VPC module vpc { for_each { dev { cidr 10.10.0.0/16, nat false } test { cidr 10.20.0.0/16, nat true } prod { cidr 10.30.0.0/16, nat true } } source ./modules/vpc vpc_name ${each.key}-vpc vpc_cidr each.value.cidr enable_nat_gateway each.value.nat }四、模块发布到私有Registry企业团队应建立内部的模块Registry统一管理和分发。发布步骤# 1. 创建Git仓库包含模块代码gitinitgitadd.gitcommit-mInitial VPC modulegittag v1.0.0# 语义化版本# 2. 推送到GitLab/GitHubgitremoteaddorigin gitgithub.com:company/terraform-aws-vpc.gitgitpush origin main--tags# 3. 在其他项目中引用modulevpc{sourcegit::https://github.com/company/terraform-aws-vpc.git?refv1.0.0# ...}Terraform Cloud/Enterprise私有Registrymodule vpc { source app.terraform.io/company/vpc/aws version 1.0.0 } 模块优势总结优势说明对比无模块代码复用一次编写多处调用需要复制粘贴多份代码降低复杂度隐藏内部实现细节所有资源平铺难以理解版本管理模块独立版本和升级路径修改任何资源都影响全局团队协作专人维护模块其他人使用所有人修改所有代码测试隔离模块可独立测试只能集成测试最佳实践封装安全组、命名规范内置依赖个人经验和习惯 常见问题排查问题原因解决方案Module not foundsource路径错误确认相对路径正确./开头模块输出为空模块未定义output在模块的outputs.tf中声明变量默认值冲突模块变量与调用方变量混淆模块变量完全独立不继承调用方修改模块后根模块不更新版本锁定更新版本号或引用commit hashfor_each报错模块不支持count或for_eachTerraform 0.13 才支持 总结通过本文你已掌握Terraform模块从创建到发布的完整链路模块本质可复用的资源封装类比编程语言的函数。创建六步法设计接口 → 建目录 → 写variables → 写main → 写outputs → 调用模块。三大要素Input Variables入参、Resources资源、Outputs出参。进阶能力嵌套模块、for_each动态调用、Registry发布。黄金法则当你发现自己在复制粘贴Terraform代码时就该把它封装成模块。模块化不是过度设计而是基础设施代码从能用走向可维护的必经之路。The End点点关注收藏不迷路⬆ ⬆ 顶部 ⬆ ⬆