论 Terraform 模块:实现基础设施规模化

  • infisical
  • 发布于 2025-04-18 16:55
  • 阅读 10

文章讨论了 Terraform/OpenTofu 模块在基础设施规模化中的作用:从本地模块、Git 托管模块到私有 Registry 模块,随着团队和系统复杂度提升,模块能帮助复用配置、统一安全与合规策略、减少漂移并便于版本管理。文章还结合 Infisical 介绍了如何用 Terraform 的 ephemeral resources 以不落盘的方式获取密钥,避免 secrets 进入 state 文件,同时保持模块接口简洁可复用。

Terraform Modules 的必要性:扩展你的基础设施组织

发布于 2025 年 4 月 16 日星期三

Blog image

使用 Terraform 或 OpenTofu 的基础设施团队,随着部署规模的增长,必然会遇到扩展性挑战。最初简单直接的资源定义,会演变为跨环境、应用和团队重复出现的复杂配置。最初的解决方案通常是为每个新服务或环境复制并修改 Terraform 代码,而随着新服务和应用不断加入,这种做法会逐渐累积技术债务。

为什么使用 Terraform Modules?

Terraform modules 通过将基础设施模式转化为可版本控制、可复用的组件,来应对这一挑战。工程团队不再复制配置块,而是可以在 modules 中封装通用模式,并通过不同的输入变量重复使用。这种方法将基础设施开发从手工复制转变为系统化的组件复用。

当团队需要修改负载均衡器配置或更新安全策略时,他们只需修改一次相关 module。使用该 module 的服务随后便可以通过受控的版本更新采用这些改进。

模块化基础设施的影响不仅限于代码组织。有效使用 modules 的团队会发现,他们的开发模式会从被动复制转向主动设计。安全策略、合规要求和基础设施优化会变成标准化组件,而不是孤立的配置。这种标准化降低了配置漂移,同时有助于在规模化时保持基础设施的一致性。

本地还是外部 Modules:哪种方式更合适?

从本地 modules 到外部 modules 的路径,通常反映了组织基础设施成熟度和规模的演进。团队在早期开发阶段往往从本地 modules 开始,把可复用配置放在基础设施仓库的 modules 目录中。当一两名工程师快速迭代基础设施时,这种方式非常合理;变更是即时的,测试也很直接,而且版本控制会自然地与主配置一起进行。

本地 modules 位于基础设施仓库的结构之内,通常在一个 modules 目录中,每个 module 都维护自己的资源、变量和输出文件。对这些 modules 的更改会和根配置一起记录在同一个提交历史中,因此很容易将 module 更新与基础设施变更对应起来:

module "app_networking" {
  source = "./modules/app_networking"

  vpc_cidr    = "10.0.0.0/16"
  environment = "production"
}

随着组织的 Infrastructure as Code(IaC)实践日趋成熟,通常会把 modules 迁移到专门的 Git 仓库中。当团队需要在多个项目之间共享基础设施模式,或者需要通过适当的版本管理来控制变更时,就会发生这种转变。托管在 Git 上的 modules 支持通过 commit 或 tag 固定特定版本,从而能够精确控制每个配置使用的 module 版本:

module "web_platform" {
  source = "git::https://example.com/infrastructure.git//modules/web-platform?ref=v1.2.0"
}

对于包含多个相关 modules 的大型仓库,可以采用子目录级别的组织方式。module source 中的双斜杠语法允许引用这些子目录,同时保持相关 modules 一起进行版本管理:

locals {
  infra_repo = "git::https://example.com/infrastructure.git//modules"
  version    = "?ref=v1.2.0"
}

module "networking" {
  source = "${local.infra_repo}/networking${local.version}"
}

当基础设施的复杂度和规模达到某个阈值时,组织通常会投资于像 Terrateam 这样的 TACOS(Terraform/OpenTofu Automation and Collaboration Software)平台,它提供多种自动化功能,以及一个私有 module registry 的托管实例。

由 registry 托管的 modules 引入了一种更结构化的版本管理和分发方式:

module "web_platform" {
  source  = "private-registry.example.com/infrastructure/web-platform"
  version = "~> 1.2.0"
}

在整个开发过程中,工程师不可避免地会面临一个决定:是构建自定义 modules,还是利用来自公共社区 registry 的现有 modules,例如 TerraformOpenTofu。这些 registry 提供了数千个由社区维护的常见基础设施模式模块,从 VPC 配置到数据库部署。那么,你是否应该重新发明轮子?

从安全角度来看,内部创建的 modules 通常是更安全的选择,尤其是对于关键基础设施。外部 modules 需要仔细审查其实现细节:一个看似无害的计算模块,可能在配置中硬编码了恶意的 AMI 镜像。除了关键安全问题之外,外部来源的 modules 不可避免地会带有一定程度的“大而全”配置;它们必须被设计为支持尽可能多的 use case。引入一个外部 module 可能会提供所需的基础设施,但代价是陈旧或未使用的配置,以及额外的技术债务。

版本管理在内部 modules 和外部 modules 之间也有显著差异。对于内部 modules,团队掌控整个更新周期。外部 modules 可能会引入破坏性变更,或带来与现有基础设施冲突的依赖更新。有些团队通过 fork 并在内部维护外部 modules 来解决这个问题,但这也会带来额外的维护负担。

从本地 modules 过渡到外部 modules,也意味着必须实现认证和授权逻辑。托管在 Git 上的 modules 通常依赖 SSH keys 或 access tokens,因此需要进行凭证和 secret 管理。私有 registry 会增加另一层认证,通常会与组织的 SSO 集成,同时还要求 CI/CD 系统使用服务凭证。

对于大多数企业来说,modules 的使用会随着基础设施成熟而经历以下阶段:

  1. 本地 modules 适用于早期开发阶段,此时团队规模较小,且正在积极迭代基础设施模式。
  2. 托管在 Git 上的 modules 会在组织的 Infrastructure as Code 实践成熟、并需要在团队之间共享 modules 时出现。
  3. 通过 TACOS 平台提供的私有 registry modules 则会在基础设施复杂度和规模要求更好的自动化、治理和分发机制时成为必要。

在 Modules 中处理 Secrets:Infisical 的方法

随着团队扩展 Terraform 部署,管理 secrets 和凭证会迅速成为一个痛点。直接在 Terraform 代码中硬编码凭证,从安全角度看显然有问题,但即使使用环境变量或 .tfvars 文件,随着基础设施变得越来越复杂,也会带来运维挑战。基础设施团队最终会面临一个棘手的问题:我们的可复用 modules 如何安全地访问它们所需的凭证?

Infisical 通过与 Terraform 集成来应对这一挑战,提供与模块化基础设施无缝协作的安全 secrets 管理。使用 Terraform v1.10+ 时,你可以使用 ephemeral resources,确保 secrets 永远不会持久化到 state 文件中:

terraform {
  required_providers {
    infisical = {
      source  = "infisical/infisical"
      version = ">= 0.4.0"
    }
  }
  required_version = ">= 1.10.0"
}

module "database" {
  source = "./modules/database"

  name          = "customer-db"
  engine        = "postgres"
  instance_type = "db.t3.medium"

  # 从 Infisical 引用 ephemeral secrets
  credentials = {
    username = local.db_credentials.username
    password = local.db_credentials.password
  }
}

## 以 ephemeral 方式获取 secret(永不持久化到 state 中)
ephemeral "infisical_secret" "db_creds" {
  name         = "DB_CREDENTIALS"
  env_slug     = var.environment
  workspace_id = var.infisical_workspace_id
  folder_path  = "/database"
}

## 将 JSON secret 解码为可用值
locals {
  db_credentials = jsondecode(ephemeral.infisical_secret.db_creds.value)
}

这种方法同时解决了几个问题:

  1. 敏感值永远不会进入你的 Terraform 代码或 state 文件。
  2. 凭证轮换发生在基础设施代码之外,从而减少了在安全策略要求更改密码时重新部署的需要。
  3. 你的 modules 仍然保持灵活且可复用,因为它们只是使用被提供的凭证,而不需要知道这些凭证来自哪里。

另请阅读:如何使用 Terraform 从 Infisical 获取 secrets,使用传统 data sources 和 ephemeral resources 两种方式

这种模式在数据库 modules 中尤其有效,因为你通常既要处理预配期间的管理员凭证,也要处理服务连接所需的应用凭证。与其通过变量和输出管理两套凭证,不如都通过 Infisical 来处理,从而降低 module 接口的复杂性。

结论

到了一定阶段,本地 modules 将不再足够。随着基础设施规模扩大,团队需要一种方式来管理 module 版本、安全地发布更新,并在不同环境之间保持一致性。这正是结构化 module 管理和自动化发挥作用的地方。下一篇文章将介绍如何组织 Terraform modules、处理版本管理,以及避免基础设施代码在扩展过程中常见的陷阱。

Malcolm Matalka

CTO,Terrateam

  • 原文链接: infisical.com/blog/terra...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
infisical
infisical
江湖只有他的大名,没有他的介绍。