本文档提供了适用于 Terraform 配置的基本样式和结构建议。这些建议适用于可重复使用的 Terraform 模块和根配置。
本指南未介绍 Terraform。如需了解如何将 Terraform 与 Google Cloud 搭配使用,请参阅 Terraform 使用入门。
遵循标准模块结构
- Terraform 模块必须遵循标准模块结构。
- 使用
main.tf
文件启动每个模块,默认情况下,资源位于此文件中。 - 在每个模块中,添加 Markdown 格式的
README.md
文件。在README.md
文件中,添加有关模块的基本文档。 - 将示例放在
examples/
文件夹中,并为每个示例提供单独的子目录。对于每个示例,请添加详细的README.md
文件。 - 使用资源各自的文件和描述性名称(例如
network.tf
、instances.tf
或loadbalancer.tf
)创建资源的逻辑分组。- 避免为每个资源提供自己的文件。按共享用途对资源进行分组。例如,在
dns.tf
中组合使用google_dns_managed_zone
和google_dns_record_set
。
- 避免为每个资源提供自己的文件。按共享用途对资源进行分组。例如,在
- 在模块的根目录中,仅添加 Terraform (
*.tf
) 和代码库元数据文件(例如README.md
和CHANGELOG.md
)。 - 将任何其他文档放在
docs/
子目录中。
采用命名惯例
使用下划线命名所有配置对象,以分隔多个字词。这种做法可确保与资源类型、数据源类型和其他预定义值的命名惯例保持一致。此惯例不适用于名称参数。
推荐:
resource "google_compute_instance" "web_server" { name = "web-server" }
不推荐:
resource "google_compute_instance" "web-server" { name = "web-server" }
如需简化对唯一属于其类型的资源(例如,整个模块的单个负载均衡器)的引用,请将该资源命名为
main
。- 记住
some_google_resource.my_special_resource.id
与some_google_resource.main.id
需要花费额外的精力。
- 记住
如需区分属于同一类型的资源(例如
primary
和secondary
),请提供有意义的资源名称。将资源名称设为单数形式。
在资源名称中,请不要重复资源类型。例如:
推荐:
resource "google_compute_global_address" "main" { ... }
不推荐:
resource "google_compute_global_address" "main_global_address" { … }
谨慎使用变量
- 在
variables.tf
中声明所有变量。 - 为变量提供与其用法或用途相关的描述性名称:
- 表示数值的输入、局部变量和输出(例如磁盘大小或 RAM 大小)必须使用单位命名(例如
ram_size_gb
)。Google Cloud API 没有标准单位,因此使用单位命名变量可让配置维护人员清楚地了解预期输入单位。 - 对于存储单位,请使用二进制单位前缀(1024 的幂)-
kibi
、mebi
、gibi
。对于所有其他计量单位,请使用十进制单位前缀(1000 的幂)-kilo
、mega
、giga
。此用法与 Google Cloud 中的用法一致。 - 如需简化条件逻辑,请为布尔值变量提供正名称,例如
enable_external_access
。
- 表示数值的输入、局部变量和输出(例如磁盘大小或 RAM 大小)必须使用单位命名(例如
- 变量必须有说明。说明会自动包含在已发布模块的自动生成的文档中。说明为新开发者添加了描述性名称无法提供的其他背景信息。
- 为变量提供定义的类型。
- 在适当情况下,请提供默认值:
- 对于具有与环境无关的值的变量(例如磁盘大小),请提供默认值。
- 对于具有特定于环境的值的变量(例如
project_id
),请勿提供默认值。这样,调用模块必须提供有意义的值。
- 仅当变量保留为空是底层 API 不拒绝的有效偏好设置时才对变量(例如空字符串或列表)使用空默认值。
- 请谨慎使用变量。参数化的值必须根据每个实例或环境而变化。在决定是否公开某个变量时,请确保您拥有更改该变量的具体用例。如果只有极少数可能需要变量,请不要公开该变量。
- 添加具有默认值的变量是向后兼容的。
- 移除变量是向后不兼容的。
- 如果在多个位置重复使用字面量,您可以使用局部值,而无需将其作为变量公开。
公开输出
- 在
outputs.tf
文件中整理所有输出。 - 为所有输出提供有意义的说明。
- 在
README.md
文件中记录输出说明。使用 terraform-docs 等工具在提交时自动生成说明。 - 输出根模块可能需要引用或共享的所有有用值。特别是对于开源模块或频繁使用的模块,请公开所有可能消耗的输出。
请勿直接通过输入变量传递输出,因为这样做会阻止输出正确地添加到依赖关系图中。为确保已创建隐式依赖项,请务必从资源中输出引用特性。传递特性,而不是直接引用实例的输入变量,如下所示:
推荐:
output "name" { description = "Name of instance" value = google_compute_instance.main.name }
不推荐:
output "name" { description = "Name of instance" value = var.name }
使用数据源
- 将数据源放在引用它们的资源旁边。例如,如果要提取要在启动实例中使用的映像,请将其与实例放在一起,而不是在各自的文件中收集数据资源。
- 如果数据源数量很大,请考虑将它们移动到专用
data.tf
文件中。 - 如需提取相对于当前环境的数据,请使用变量或资源插值类型。
限制自定义脚本的使用
- 仅在必要时使用脚本。Terraform 不会考虑或管理通过脚本创建的资源的状态。
- 尽可能避免使用自定义脚本。仅在 Terraform 资源不支持所需行为时使用它们。
- 使用的任何自定义脚本都必须有明确记录的存在原因,并且最好有弃用方案。
- Terraform 可以通过预配工具(包括 local-exec 预配工具)调用自定义脚本。
- 将 Terraform 调用的自定义脚本放在
scripts/
目录中。
在单独的目录中添加辅助脚本
- 在
helpers/
目录中整理非 Terraform 调用的辅助脚本。 - 在
README.md
文件中记录辅助脚本,其中包含说明和调用示例。 - 如果辅助脚本接受参数,请提供参数检查和
--help
输出。
将静态文件放在单独的目录中
- Terraform 引用但未执行(例如加载到 Compute Engine 实例上的启动脚本)的静态文件必须整理到
files/
目录中。 - 将冗长的 HereDoc 放在外部文件(独立于其 HCL)中。使用
file()
函数引用它们。 - 对于使用 Terraform
templatefile
函数读入的文件,请使用文件扩展名.tftpl
。- 模板必须放在
templates/
目录中。
- 模板必须放在
保护有状态资源
对于有状态资源(例如数据库),请确保启用删除保护。例如:
resource "google_sql_database_instance" "main" {
name = "primary-instance"
settings {
tier = "D0"
}
lifecycle {
prevent_destroy = true
}
}
使用内置格式设置
所有 Terraform 文件都必须符合 terraform fmt
的标准。
限制表达式的复杂性
- 限制任何单个插值表达式的复杂性。如果单个表达式中需要许多函数,请考虑使用局部值将其拆分为多个表达式。
- 一行中不能有多个三元运算。请改用多个本地值来构建逻辑。
为条件值使用 count
如需有条件地实例化资源,请使用 count
元参数。例如:
variable "readers" {
description = "..."
type = list
default = []
}
resource "resource_type" "reference_name" {
// Do not create this resource if the list of readers is empty.
count = length(var.readers) == 0 ? 0 : 1
...
}
使用用户指定的变量为资源设置 count
变量时,请务必小心。如果为此类变量(如 project_id
)提供了资源特性,但该资源尚不存在,则 Terraform 无法生成计划。相反,Terraform 会报告错误 value of count cannot be computed
。在这种情况下,请使用单独的 enable_x
变量来计算条件逻辑。
为迭代资源使用 for_each
如果要根据输入资源创建资源的多个副本,请使用 for_each
元参数。
将模块发布到注册表
可重复使用的模块:将可重复使用的模块发布到模块注册表。
开源模块:将开源模块发布到 Terraform 注册表。
私有模块:将私有模块发布到私有注册表。