3. 服务目录架构设计:模块化设计原则

服务目录(Service Catalog)是 Terraform 工作流优化的核心资产。它将基础设施从“一次性脚本”转变为“可复用的产品”。本章聚焦于如何通过模块化设计原则,将基础设施抽象为标准化、可组合的服务目录项,从而提升执行效率与团队协作水平。

3.1 模块化设计的三大核心原则

在构建服务目录时,必须遵循以下原则,确保每个模块独立、可测试且易于组合:

原则 描述 关键实践
单一职责 每个模块只负责一种基础设施资源或一个逻辑单元(如:一个 VPC、一个 RDS 实例)。 避免创建“万能模块”;模块名称应直接反映其职责,如 aws-vpc、aws-ecs-service。
显式接口 通过输入变量(Input Variables)和输出值(Output Values)定义清晰的契约。 所有依赖必须通过变量传递;禁止在模块内部硬编码环境差异。
可组合性 模块应像乐高积木一样,能够通过根模块(Root Module)或编排层自由组合。 模块之间不产生隐式依赖;通过 Terraform 的 depends_on 或数据源显式声明关系。
实践提示: 在模块的 README 中明确标注“输入变量默认值仅用于开发环境”,避免生产环境误用默认配置。

3.2 服务目录的抽象层次模型

将基础设施抽象为服务目录项,通常分为三个层次。下图展示了从底层资源到上层业务服务的映射关系:

服务目录抽象层次模型 业务服务层 (Business Service) 示例:电商平台、支付网关、数据分析平台 由多个服务目录项组合而成,包含应用配置与基础设施 服务目录项 (Catalog Item) 示例:标准 Web 应用栈、托管 Kafka 集群、无服务器 API 封装了特定场景下的资源组合与配置策略 基础模块层 (Foundation Module) 示例:aws-vpc、aws-ec2-instance、aws-rds 原子化资源封装,遵循单一职责原则 提供标准输入/输出接口,支持版本管理
架构要点: 基础模块层是“砖块”,服务目录项是“房间”,业务服务层是“整栋楼”。每一层只依赖下一层提供的接口,不跨层调用。

3.3 模块结构标准化:服务目录项模板

每个服务目录项应遵循统一的文件结构,降低认知成本。推荐的标准布局如下:

service-catalog/
├── terraform-aws-standard-webapp/    # 服务目录项名称
│   ├── README.md                     # 使用说明、输入输出、示例
│   ├── main.tf                       # 主逻辑,调用基础模块
│   ├── variables.tf                  # 输入变量定义
│   ├── outputs.tf                    # 输出值定义
│   ├── versions.tf                   # Terraform 与 Provider 版本约束
│   ├── examples/
│   │   └── basic-usage/              # 可运行的示例
│   │       ├── main.tf
│   │       └── terraform.tfvars
│   └── tests/                        # 自动化测试(Terratest 等)
│       └── webapp_test.go
常见反模式: 将 terraform.tfvars 或 backend.tf 硬编码在模块内部。这些文件应属于调用方,而非模块本身。

3.4 变量与输出设计规范

良好的接口设计是模块复用的基石。下表对比了推荐与不推荐的变量设计方式:

设计维度 推荐做法 不推荐做法
变量命名 使用清晰、自解释的名称,如 vpc_cidr_block 使用缩写或模糊名称,如 cidr、cfg
默认值 提供安全的开发环境默认值,并注释说明 不提供默认值,或提供生产级高风险默认值
类型约束 使用 string、number、list(string) 等具体类型 滥用 any 类型,导致接口不明确
验证规则 使用 validation 块检查 CIDR 格式、端口范围等 依赖文档或口头约定,无代码级校验
输出值 输出模块内创建的关键资源属性(如 ID、ARN、端点) 输出所有资源属性,造成信息过载

3.5 模块组合与依赖管理

服务目录项通常由多个基础模块组合而成。下图展示了一个“标准 Web 应用栈”服务目录项的内部组合关系:

服务目录项组合示例:标准 Web 应用栈 standard-webapp (服务目录项) aws-vpc 网络隔离层 aws-ecs-service 容器编排层 aws-rds 持久化存储层 输出:Web 端点、数据库地址 依赖关系:ECS 服务依赖 VPC 的子网 ID;RDS 部署在 VPC 的数据库子网中 这些依赖通过变量传递,而非硬编码

3.6 版本管理与发布策略

服务目录项应使用语义化版本控制(SemVer),并与 Git Tag 绑定。推荐的工作流如下:

  1. 开发阶段: 在模块仓库的 main 分支进行迭代,版本号为 0.x.y。
  2. 预发布: 创建 Release Candidate 分支,运行集成测试套件。
  3. 正式发布: 合并至 main,打 Tag 如 v1.2.0,并更新服务目录索引文件。
  4. 消费端锁定: 在根模块中通过 source = "git::https://...//terraform-aws-standard-webapp?ref=v1.2.0" 锁定版本。
效率提升关键: 使用 terraform init -upgrade 仅在有明确升级意图时更新模块版本,避免“隐式升级”导致生产环境意外变更。

3.7 服务目录索引与发现

当服务目录项数量超过 10 个时,建议维护一个中央索引文件(如 catalog.yaml),供团队快速检索和消费:

# catalog.yaml
catalog_items:
  - name: standard-webapp
    description: 标准 Web 应用栈,包含 VPC、ECS Fargate、RDS
    version: "1.2.0"
    source: "git::https://github.com/org/terraform-aws-standard-webapp?ref=v1.2.0"
    tags: ["web", "ecs", "rds", "production-ready"]

  - name: serverless-api
    description: 无服务器 API,基于 API Gateway + Lambda + DynamoDB
    version: "0.4.1"
    source: "git::https://github.com/org/terraform-aws-serverless-api?ref=v0.4.1"
    tags: ["serverless", "api", "low-cost"]

通过自动化工具(如 terragrunt 或自定义脚本)解析该索引,可快速生成调用代码骨架,将新服务的部署时间从小时级缩短到分钟级。