3. 模块仓库结构规划
模块平台建设到第三阶段,真正决定长期维护成本的往往不是模块本身写得多好,而是仓库结构是否合理。一个企业里通常有几十到几百个 Terraform 模块,如果一开始没有规划好仓库形态、目录层级和文档标准,半年后就会出现“找不到模块、版本对不上、README 形同虚设”的典型困境。本节聚焦三件事:monorepo 与 polyrepo 的取舍、目录与命名规范、README 与 CHANGELOG 标准。
一、monorepo 与 polyrepo 的取舍
monorepo 指所有模块放在同一个仓库中,通过子目录区分;polyrepo 指每个模块(或每组强相关模块)独立成仓。两者没有绝对优劣,关键看团队规模、发布节奏和权限模型。
monorepo 的优势在于:统一 CI 流水线、跨模块重构方便、依赖关系一目了然、新人只需 clone 一个仓库。代价是仓库体积增长快、CI 全量触发成本高、权限粒度粗(无法只让某团队维护某个模块)。polyrepo 的优势是:模块独立版本、独立发布、权限清晰、CI 只跑受影响模块。代价是跨模块变更需要多仓协同、版本对齐困难、平台侧要维护大量仓库配置。
一个可落地的判断依据是:模块数量少于 50、团队少于 5 个、发布节奏接近时优先 monorepo;模块数量超过 100、多团队独立发布、存在强权限隔离需求时优先 polyrepo。很多企业实际采用混合模式:核心基础模块(网络、IAM、标签规范)放 monorepo 统一维护,业务域模块按团队 polyrepo 独立演进,平台通过 registry 统一暴露。
二、目录层级规划
无论选哪种仓库形态,目录层级都应遵循“按资源类型分层、按业务域分组”的原则。推荐 monorepo 采用如下结构:
terraform-modules/
├── modules/ # 可复用模块
│ ├── network/
│ │ ├── vpc/
│ │ │ ├── main.tf
│ │ │ ├── variables.tf
│ │ │ ├── outputs.tf
│ │ │ ├── versions.tf
│ │ │ ├── README.md
│ │ │ └── CHANGELOG.md
│ │ └── subnet/
│ ├── iam/
│ │ └── role/
│ └── tags/
│ └── standard/
├── examples/ # 每个模块的可运行示例
│ ├── network-vpc-basic/
│ └── iam-role-cross-account/
├── test/ # 集成测试与合规测试
│ ├── terratest/
│ └── policy/
├── .github/workflows/ # CI/CD
└── docs/ # 平台使用文档
关键约定:modules/ 下第一层是资源域(network、iam、compute、data),第二层是具体模块名;examples/ 与 modules/ 一一对应,命名采用“域-模块-场景”格式;test/ 与 docs/ 平级,不嵌套进模块目录。这样做的目的是让模块本身保持纯净(只含 Terraform 代码和文档),示例和测试作为独立消费者存在,避免模块目录被测试文件污染。
三、命名规范
命名规范要覆盖仓库名、模块目录名、资源名、变量名和输出名五个层面。推荐规则如下:
- 仓库名:polyrepo 用
tf-<provider>-<resource>,如tf-aws-vpc;monorepo 用terraform-modules。 - 模块目录名:小写短横线,
<域>/<模块>,如network/vpc、iam/role。 - 资源名:
this用于单资源模块,多资源用语义名,如aws_vpc.this、aws_subnet.public。 - 变量名:小写蛇形,
vpc_cidr、enable_flow_log,布尔变量用enable_或is_前缀。 - 输出名:与变量风格一致,
vpc_id、subnet_ids,复数用_ids结尾。
命名一旦确定,应通过 tflint 或自定义 conftest 策略在 CI 中强制校验,避免人工评审遗漏。
四、README 与 CHANGELOG 标准
README 是模块的“使用说明书”,必须包含以下固定章节,缺一不可:
- 模块简介:一句话说明用途和适用场景。
- 使用示例:可直接复制的
module调用代码块。 - 输入变量表:名称、类型、是否必填、默认值、说明。
- 输出表:名称、类型、说明。
- 依赖与前置条件:Provider 版本、所需权限、外部依赖。
- 版本兼容性:支持的 Terraform 版本和 Provider 版本范围。
推荐使用 terraform-docs 自动生成变量表和输出表,通过 pre-commit hook 保证 README 与代码同步。CHANGELOG 遵循 Keep a Changelog 规范,按 Added / Changed / Deprecated / Removed / Fixed / Security 分类,每个版本标注日期和语义化版本号。破坏性变更必须单独用 BREAKING CHANGE 标注,并在升级指南中给出迁移步骤。
仓库结构规划的本质,是把“模块怎么被找到、怎么被使用、怎么被升级”这三件事在物理层面固化下来。monorepo 与 polyrepo 的选择决定了协作边界,目录层级决定了查找效率,命名规范决定了可读性,README 与 CHANGELOG 决定了模块能否被安全消费。四者共同构成模块平台的地基,后续的版本管理、CI/CD 和发布流程都建立在这个地基之上。