3. 服务目录设计原则:模块化、可复用、版本化、安全合规

服务目录是 Terraform 冷启动的核心资产,其设计质量直接决定资源交付的效率与风险。本章围绕四个核心原则展开:模块化(Modularity)、可复用(Reusability)、版本化(Versioning)、安全合规(Security & Compliance)。这四个原则相互支撑,共同构成生产级服务目录的骨架。

核心观点: 服务目录不是代码仓库的简单堆砌,而是经过抽象、封装、治理后的“基础设施积木盒”。每个原则解决一个关键问题:模块化解决复杂度,可复用解决效率,版本化解决变更风险,安全合规解决底线。

3.1 模块化:解耦与职责单一

模块化要求每个 Terraform 模块只负责一个明确的资源边界。例如,一个“VPC 模块”不应同时管理安全组和子网路由表——这些应拆分为独立子模块,通过变量和输出进行组合。

设计维度 反模式(耦合) 正模式(模块化)
资源范围 一个模块创建 VPC + 子网 + 安全组 + EC2 每个资源类型独立模块,通过根模块组合
变量暴露 暴露 30+ 扁平变量,无分组 使用 variable 块 + type = object 结构化输入
输出粒度 只输出资源 ID,下游需二次查询 输出完整属性集(ID、ARN、标签、依赖关系)
依赖管理 隐式依赖,模块间硬编码引用 通过 depends_on 和 data 源显式声明
实践建议: 每个模块的 main.tf 文件行数建议控制在 200 行以内。超过此阈值,应考虑拆分子模块或重构资源分组。
模块化资源边界示意 模块化资源边界示意 根模块(Root Module) 网络层 VPC 模块 子网模块 安全组模块 计算层 EC2 模块 ALB 模块 Auto Scaling 模块 存储层 S3 模块 RDS 模块 ElastiCache 模块 每个子模块职责单一,通过根模块编排组合,形成完整服务目录

3.2 可复用:输入抽象与输出标准化

可复用性要求模块能够被不同环境(开发、测试、生产)和不同团队调用,而无需修改模块内部代码。关键手段包括:

  • 变量抽象: 使用 variable 块定义环境差异点(如实例规格、标签、CIDR 范围),避免硬编码。
  • 输出契约: 每个模块输出标准化的属性集,供下游模块或根模块消费。
  • 条件逻辑: 通过 count 或 for_each 控制资源是否创建,适应不同场景。
# 可复用模块示例:通用 EC2 模块
variable "instance_config" {
  type = object({
    name          = string
    instance_type = string
    ami_id        = string
    subnet_id     = string
    volume_size   = optional(number, 30)
    tags          = optional(map(string), {})
  })
}

resource "aws_instance" "this" {
  ami           = var.instance_config.ami_id
  instance_type = var.instance_config.instance_type
  subnet_id     = var.instance_config.subnet_id

  root_block_device {
    volume_size = var.instance_config.volume_size
  }

  tags = merge(var.instance_config.tags, {
    Name = var.instance_config.name
  })
}

output "instance_attributes" {
  value = {
    id         = aws_instance.this.id
    private_ip = aws_instance.this.private_ip
    public_ip  = aws_instance.this.public_ip
    arn        = aws_instance.this.arn
  }
}
常见陷阱: 不要为了“复用”而过度抽象。如果一个模块需要 10+ 个条件判断或 5 层嵌套变量,说明模块边界划分不合理,应拆分为更细粒度的子模块。

3.3 版本化:语义化版本与发布流程

版本化是服务目录的变更管理基石。Terraform 模块应遵循 语义化版本(SemVer):MAJOR.MINOR.PATCH。

版本号 变更类型 示例 消费者影响
MAJOR 不兼容的 API 变更 删除变量、修改输出结构、资源类型替换 必须修改调用代码
MINOR 向下兼容的功能新增 新增可选变量、新增输出属性、新增资源 无需修改,可选择性使用新功能
PATCH 向下兼容的问题修复 修复 Bug、更新文档、优化性能 无影响,建议自动升级

在服务目录中,版本化通过 Git Tag 或 Terraform Registry 的版本约束实现:

# 在根模块中引用特定版本
module "vpc" {
  source = "git::https://git.example.com/service-catalog/network/vpc.git?ref=v1.2.0"
  # 或使用 Terraform Registry 语法
  # source  = "terraform-aws-modules/vpc/aws"
  # version = "~> 5.0"
}
版本策略: 建议服务目录采用“主干开发 + 发布分支”模式。所有模块在 main 分支开发,发布时打 Tag 并创建 release/x.y.z 分支。消费者始终引用 Tag 或版本约束,不直接引用 main 分支。

3.4 安全合规:内置策略与审计痕迹

安全合规是服务目录的底线。设计阶段应将安全策略内嵌到模块中,而非依赖事后扫描。核心手段包括:

  • 默认安全: 模块变量设置安全默认值(如 S3 桶默认私有、安全组默认拒绝所有入站)。
  • 策略即代码: 使用 precondition 和 postcondition 在 Plan 阶段校验合规要求。
  • 加密与审计: 强制启用加密(KMS)、日志(CloudTrail)、访问分析(IAM Access Analyzer)。
# 安全合规内嵌示例:S3 模块强制加密和版本控制
resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name

  # 使用 precondition 在 Plan 阶段校验标签
  lifecycle {
    precondition {
      condition     = contains(keys(var.tags), "Environment")
      error_message = "所有 S3 桶必须包含 Environment 标签"
    }
  }
}

resource "aws_s3_bucket_versioning" "this" {
  bucket = aws_s3_bucket.this.id
  versioning_configuration {
    status = "Enabled"  # 强制启用版本控制
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
  bucket = aws_s3_bucket.this.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "aws:kms"  # 强制使用 KMS 加密
    }
  }
}
安全合规策略嵌入位置 安全合规策略在模块中的嵌入位置 变量定义 默认安全值 Precondition Plan 阶段校验 资源定义 强制加密/版本控制 Postcondition Apply 后验证 安全合规策略嵌入点说明 1. 变量定义阶段:设置安全默认值(如 S3 桶私有、安全组拒绝所有入站) 2. Precondition 阶段:在 terraform plan 时校验标签、区域、IAM 条件 3. 资源定义阶段:强制启用加密、版本控制、日志记录 4. Postcondition 阶段:Apply 后验证资源状态是否符合合规基线

3.5 四原则协同矩阵

四个原则并非孤立存在,而是相互增强。下表展示它们在实际服务目录中的协同关系:

原则 与模块化协同 与可复用协同 与版本化协同 与安全合规协同
模块化 — 细粒度模块更易被不同场景复用 模块边界清晰,版本变更影响范围可控 单一职责模块便于审计和策略注入
可复用 模块化是可复用的前提 — 版本化保证复用时的确定性 复用模块时安全策略自动继承
版本化 模块化降低版本冲突概率 版本约束让复用者明确依赖 — 安全修复通过 PATCH 版本快速下发
安全合规 安全策略内嵌到每个模块 复用模块时安全基线自动生效 版本化记录安全策略变更历史 —
设计检查清单: 在创建或评审服务目录模块时,逐项确认:
✅ 模块是否只管理一种资源类型或一个资源边界?
✅ 变量是否使用 object 类型进行结构化抽象?
✅ 模块是否发布了语义化版本 Tag?
✅ 是否在模块内嵌了至少一项安全合规策略(precondition/强制加密/标签校验)?