根模块的最佳实践

本文档提供了使用根模块时需要考虑的准则和建议。

根配置或根模块是您在其中运行 Terraform CLI 的工作目录。确保根配置遵循以下标准(以及适用的上述 Terraform 准则)。针对根模块的明确建议取代了一般准则。

本指南未介绍 Terraform。如需了解如何将 Terraform 与 Google Cloud 搭配使用,请参阅 Terraform 使用入门

最大限度地减少每个根模块中的资源数量

请务必防止单个根配置过大,同时还要防止同一目录和状态下存储的资源过多。每次运行 Terraform 时,特定根配置中的所有资源都会刷新。如果单个状态中包含的资源过多,则可能会导致执行缓慢。一般规则:在单个状态下,不要添加超过 100 个资源(最好不要超过十几个)。

为每个应用使用单独的目录

如需相互独立地管理应用和项目,请将每个应用和项目的资源放在其各自的 Terraform 目录中。服务可能表示特定应用或通用服务,例如共享网络。将特定服务的所有 Terraform 代码嵌套在一个目录(包括子目录)下。

将应用拆分为特定于环境的子目录

在 Google Cloud 中部署服务时,将服务的 Terraform 配置拆分为两个顶级目录:包含服务的实际配置的 modules 目录和包含每个环境的根配置的 environments 目录。

-- SERVICE-DIRECTORY/
   -- OWNERS
   -- modules/
      -- <service-name>/
         -- main.tf
         -- variables.tf
         -- outputs.tf
         -- provider.tf
         -- README
      -- ...other…
   -- environments/
      -- dev/
         -- backend.tf
         -- main.tf

      -- qa/
         -- backend.tf
         -- main.tf

      -- prod/
         -- backend.tf
         -- main.tf

使用环境目录

如需跨环境共享代码,请引用模块。通常,这可能是一个服务模块,其中包含服务的基本共享 Terraform 配置。在服务模块中,对常见输入进行硬编码,并且只需要特定于环境的输入作为变量。

每个环境目录必须包含以下文件:

  • backend.tf 文件,用于声明 Terraform backend状态位置(通常为 Cloud Storage
  • main.tf 文件,用于实例化服务模块

每个环境目录(devqaprod)对应于默认的 Terraform 工作区,并会将服务版本部署到该环境。这些工作区将特定于环境的资源隔离到各自的上下文中。仅使用默认工作区

不建议在一个环境中使用多个 CLI 工作区,原因如下:

  • 检查每个工作区中的配置并非易事。
  • 不建议为多个工作区使用单个共享后端,因为如果将共享后端用于环境分隔,它会成为单点故障。
  • 虽然代码可以重复使用,但必须根据当前工作区变量进行切换(例如 terraform.workspace == "foo" ? this : that),这增加了读取代码的难度。

详情请参阅以下内容:

通过远程状态公开输出

确保您从根模块公开模块实例的有用输出。

例如,以下代码段传递项目工厂模块实例中的项目 ID 输出,将其作为根模块的输出。

# Project root module
terraform {
  backend "gcs" {
    bucket  = "BUCKET"
  }
}

module "project" {
  source  = "terraform-google-modules/project-factory/google"
  ...
}

output "project_id" {
  value       = module.project.project_id
  description = "The ID of the created project"
}

其他 Terraform 环境和应用只能引用根模块级输出。

通过使用远程状态,您可以引用根模块输出。若要允许其他依赖应用使用配置,请确保将与服务的端点相关的信息导出到远程状态。

# Networks root module
data "terraform_remote_state" "network_project" {
  backend = "gcs"

  config = {
    bucket = "BUCKET"
  }
}

module "vpc" {
  source  = "terraform-google-modules/network/google"
  version = "~> 9.0"

  project_id   = data.terraform_remote_state.network_project.outputs.project_id
  network_name = "vpc-1"
  ...
}

有时,例如,从环境目录调用共享服务模块时,重新导出整个子模块是最适当的操作,如下所示:

output "service" {
  value       = module.service
  description = "The service module outputs"
}

固定到次要提供商版本

在根模块中,声明每个提供商并固定到次要版本。这样可以自动升级升级到新的补丁版本,同时仍保持可靠的目标。 为了保持一致性,请将版本文件命名为 versions.tf

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 4.0.0"
    }
  }
}

将变量存储在 tfvars 文件中

对于根模块,请使用 .tfvars 变量文件提供变量。为了保持一致性,请将变量文件命名为 terraform.tfvars

请勿使用替代 var-filesvar='key=val' 命令行选项指定变量。命令行选项是临时性的,容易忘记。使用默认变量文件更容易预测。

签入 .terraform.lock.hcl 文件

对于根模块,.terraform.lock.hcl 依赖项锁文件应签入源代码控制系统中。这有助于跟踪和审核指定配置的提供商选择变化。

后续步骤