Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
有关Terraform代码结构的问题是社区中迄今为止最常见的问题。 每个人都在某个时候考虑过项目的最佳代码结构。
这是一个有很多解决方案的问题之一,很难给出通用的建议,所以让我们从理解我们正在处理什么开始。
你的项目有多复杂?
相关resources(资源)数量
Terraform 提供商的数量(参见下面关于“逻辑提供者”的注释)
你的基础架构多久更改一次?
从每月/每周/每天一次
到连续(每次有新提交时)
代码更改发起者? 当一个新的artifact(构件)生成时,是否让 CI 服务器更新存储库?
只有开发人员可以推送到基础架构存储库
每个人都可以通过打开 PR(包括在 CI 服务器上运行的自动化任务)来提出对任何事物的更改
你使用哪种部署平台或部署服务?
AWS CodeDeploy、Kubernetes 或 OpenShift 需要稍微不同的方法
环境如何分组?
按环境、地区、项目
当开始或编写示例代码时,将所有代码放在 main.tf
中是一个好主意。 在所有其他情况下,最好将多个文件按逻辑拆分,如下所示:
main.tf
- 调用modules(模块)、locals(本地)和data sources(数据源)来创建所有资源
variables.tf
- 包含在main.tf
中使用的变量声明
outputs.tf
- 包含在 main.tf
中创建的资源的输出
versions.tf
- 包含 Terraform 和提供商的版本要求
terraform.tfvars
不应在除composition(组合)之外的任何地方使用。
请确保您了解核心概念 - resource module(资源模块),infrastructure module(基础设施模块),和 composition(组合), 因为它们在以下示例中使用。
使用更少的资源可以更轻松、更快速地进行工作
terraform plan
和 terraform apply
都调用 cloud API 来验证资源状态
如果你的整个基础设施都在一个组合中,这可能需要一些时间
资源越少,爆炸半径(A blast radius)就越小
通过将不相关的资源放置在单独的组合中以进行隔离,可以降低如果出现问题时的风险
使用远程状态启动您的项目,因为:
你的笔记本电脑不是基础设施代码真实来源的合适位置
在 git 中管理tfstate
文件是一场噩梦
当基础设施层开始朝多个方向(依赖项或资源数量)增长时,将更容易控制事物
实践一致的结构和 naming convention(命名约定):
与procedural code(过程式代码)一样,Terraform 代码应该首先编写供人们阅读。当六个月后发生变化时,一致性将有所帮助
可以在 Terraform 状态文件中移动资源,但如果结构和命名不一致,则可能更难做到
保持资源模块尽可能简单
不要将可以作为变量传递或使用数据源(data sources)发现的值硬编码
专门使用data sources(数据源)和terraform_remote_state
作为composition(组合)中基础设施模块之间的粘合剂
在本书中,示例项目按复杂性分组 —— 从小型到超大型基础设施。这种分离并不严格,因此还要检查其他结构。
拥有小型基础设施意味着存在少量依赖项和资源。 随着项目的增长,链接 Terraform 配置的执行、连接不同的基础设施模块以及在组合中传递值的需求变得显而易见。
开发人员使用的编排解决方案至少有5个不同的群体:
仅限Terraform。 非常简单,开发人员只需了解 Terraform 即可完成工作。
Terragrunt。 纯编排工具,可用于编排整个基础架构以及处理依赖项。 Terragrunt 原生地操作基础设施模块和组合,因此它减少了代码重复。
In-house scripts(内部脚本)。 通常,这发生在编排的起点和发现 Terragrunt 之前。
Ansible 或类似的通用自动化工具。通常在 Ansible 之后采用 Terraform 时使用,或者在积极使用 Ansible UI 时使用。
Crossplane 和其他受 Kubernetes 启发的解决方案。有时,利用 Kubernetes 生态系统并采用reconciliation loop(协调循环)功能来实现 Terraform 配置的所需状态是有意义的。 观看视频Crossplane vs Terraform了解更多信息。
考虑到这一点,本书回顾了这些项目结构中的前两个,即仅使用Terraform和Terragrunt。
请参阅下一章中 Terraform 或 Terragrunt 的代码结构示例。
至少应该没有理由不遵循这些约定 :)
请注意,实际的云资源通常对允许的名称有限制。 例如,有些资源不能包含破折号,有些资源必须采用驼峰式大小写。 本书中的约定指的是 Terraform 名称本身。
在任何地方(资源名称、数据源名称、变量名称、输出等)使用 _
(下划线) 而不是 -
(破折号)。
非常适合使用小写字母和数字(即使支持 UTF-8)。
不要在资源名称中重复资源类型(既不要部分,更不要完全):
resource "aws_route_table" "public" {}
resource "aws_route_table" "public_route_table" {}
resource "aws_route_table" "public_aws_route_table" {}
如果没有更多的描述性和通用名称可用,或者如果资源模块创建这种类型的单个资源,资源名称应该被命名为this
(例如,在AWS VPC module 中有一个类型为 aws_nat_gateway
的资源和多个类型为aws_route_table
的资源,所以 aws_nat_gateway
应该命名为this
,并且aws_route_table
应该有更具描述性的名称——比如private
, public
, database)
。
始终使用单数名词作为名称。
在参数值内部和值将显示给人类的地方,使用-
(例如,在 RDS 实例的 DNS 名称内部)。
将参数count
/ for_each
包含在资源或数据源块中作为顶部的第一个参数,并在其后用换行符分隔。
包括参数tags,
如果资源支持,作为最后一个真正的参数,如果需要,后面是 depends_on
和lifecycle
。所有这些都应该用一个空行分隔。
在参数count
/ for_each
中使用条件时,首选布尔值而不是使用 length
或其他表达式。
resource
(资源)代码示例count
/ for_each
的使用tags
(标签)的放置count
(计数)条件不要在资源模块中重新造轮子:使用你正在使用的资源的“参数参考”部分中定义的变量 name,description
和 default
。
对变量验证的支持相当有限(例如,无法访问其他变量或进行查找)。 相应地计划,因为在许多情况下此功能是无用的。
当type是list(...)
或 map(...)
,在变量名中使用复数形式。
在变量块中排序键,比如:description
,type
,default
,validation
。
始终包括对所有变量的description
,即使你认为它很明显(将来你会需要它)。
优先使用简单类型(number
,string
,list(...)
,map(...)
, any
) 而不是像object()
这样的特定类型,除非您需要对每个键都有严格的约束。
如果map的所有元素都具有相同的类型(例如string
)或可以转换为它(例如number
类型可以转换为string
),则使用特定类型,如map(map(string))
。
使用类型any
来禁用从某个深度开始或者在支持多个类型时的类型验证。
值{}
有时是一个map,有时是一个对象。使用tomap(...)
制作map,因为没有办法制作对象。
使输出在其范围之外保持一致且易于理解(当用户使用模块时,它返回的值的类型和属性应该很明显)。
输出的名称应该描述它包含的属性,并且不像你通常想要的那样自由。
输出名称的良好结构类似于{name}_{type}_{attribute}
,其中:
{name}
是没有提供者前缀的资源或数据源名称。aws_subnet
的{name}是subnet
,aws_vpc
的{name}
是vpc
{type}
是一种资源来源
{attribute}
是输出返回的属性
参见示例 See examples.
如果输出返回一个带有插值函数和多个资源的值,{name}
和 {type}
应该尽可能通用(this
作为前缀应该省略), 参见示例 See example。
如果返回值是一个列表,它应该有一个复数名称。 参见示例 See example。
始终包括所有输出的description
,即使你认为它很明显。
避免设置sensitive
参数, 除非您完全控制此输出在所有模块的所有位置的使用。
更喜欢try()
(自 Terraform 0.13 起可用)而不是 element(concat(...))
(0.13 之前版本的旧方法)。
output
(输出)代码示例最多返回一个安全组ID:
当有多个相同类型的资源时,this
在输出名称中应省略:
来源: https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/large-terraform
此示例包含的代码作为为大型基础设施构建 Terraform 配置的示例,该基础设施使用:
2个AWS账户
2个地区
2 个独立的环境(prod
和 stage
什么都不共享)。 每个环境都位于一个单独的 AWS 账户中,并跨越 2 个区域之间的资源
每个环境都使用来自 Terraform Registry 的不同版本的现成基础设施模块 (alb
)
每个环境都使用相同版本的内部模块modules/network
,因为它来自本地目录
在此处描述的大型项目中,使用 Terragrunt 的好处变得非常明显。请参阅Code Structures examples with Terragrunt。
非常适合基础设施在逻辑上分离的项目(单独的 AWS 账户)
适合不需要修改 AWS 账户之间共享的资源(一个环境 = 一个 AWS 账户 = 一个状态文件)
适合不需要编排环境之间的变化
适合基础设施资源因环境而异且无法一概而论时(例如,在一个环境或某些地区缺少某些资源)
随着项目的增长,让这些环境彼此保持最新状态将变得更加困难。 考虑使用基础设施模块(现成的或内部的)来完成可重复的任务。
示例和 Terraform 模块应包含说明功能及其使用方法的文档。
README.md 文件中的所有链接都应该是绝对链接,以使 Terraform Registry 网站正确显示它们。
文档可能包括使用mermaid创建的图表和使用cloudcraft.co创建的蓝图。
使用 Terraform pre-commit hooks 确保代码有效、格式正确并自动记录,然后再推送到 git 并由人工审查。
pre-commit 是一个用于管理和维护多语言预提交挂钩的框架。它是用 Python 编写的,是一个强大的工具,可以在代码提交到 git 存储库之前在开发人员的机器上自动执行某些操作。通常,它用于运行 linters 和格式化代码(参见supported hooks)。
通过 Terraform 配置,pre-commit
可用于格式化和验证代码,以及更新文档。
查看 pre-commit-terraform repository 以熟悉它,以及已经使用它的现有存储库(例如, terraform-aws-vpc)。
terraform-docs 是一种工具,可以从 Terraform 模块以各种输出格式完成文档的生成。你可以手动运行它(没有预提交挂钩),或使用 pre-commit-terraform hooks 来自动更新文档。
@todo:记录模块版本、发布、GH actions
预提交框架主页:pre-commit framework homepage
用于预提交框架的Terraform 的 git 钩子集合:Collection of git hooks for Terraform to be used with pre-commit framework
来源: https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/medium-terraform
此示例包含的代码作为为中型基础设施构建 Terraform 配置的示例,该基础设施使用:
2个AWS账户
2 个独立的环境 (prod
和 stage
什么都不共享),每个环境都位于一个单独的 AWS 账户中
每个环境都使用来自 Terraform Registry 的不同版本的现成基础设施模块 (alb
)
每个环境都使用相同版本的内部模块modules/network
,因为它来自本地目录
非常适合基础设施在逻辑上分离的项目(单独的 AWS 账户)
适合不需要修改 AWS 账户之间共享的资源(一个环境 = 一个 AWS 账户 = 一个状态文件)
适合不需要编排环境之间的变化
适合基础设施资源因环境而异且无法一概而论时(例如,在一个环境或某些地区缺少某些资源)
随着项目的增长,让这些环境彼此保持最新状态将变得更加困难。考虑使用基础设施模块(现成的或内部的)来完成可重复的任务。
有很多人创建了很棒的内容并管理了与 Terraform 社区相关的开源项目,但我想不出最好的结构来在不复制像 awesome-terraform 这样的列表的情况下列出这些链接。
https://twitter.com/antonbabenko/lists/terraform-experts - 非常积极地使用 Terraform 并且可以告诉你很多(如果你问他们的话)的人的名单。
https://github.com/shuaibiyy/awesome-terraform - HashiCorp 的 Terraform 上精选的资源列表。
http://bit.ly/terraform-youtube - Anton Babenko 的YouTube 频道“Your Weekly Dose of Terraform”,包含评论、采访、问答、实时编码和一些使用 Terraform 的黑客技术的直播。
https://weekly.tf - Terraform 每周时事通讯。 Terraform 世界中的各种新闻(项目、公告、讨论),由 Anton Babenko 撰写。
来源: https://github.com/antonbabenko/terraform-best-practices/tree/master/examples/small-terraform
此示例包含的代码作为为小型基础设施构建 Terraform 配置的示例,其中不使用外部依赖项。
非常适合入门和按需重构
非常适合小型资源模块
适用于小型和线性基础设施模块(例如,terraform-aws-atlantis)
适用于少量资源(少于 20-30)
如果资源数量不断增加,所有资源的单一状态文件会使使用 Terraform 的过程变慢(考虑使用参数 -target
来限制资源数量)
locals
指定资源之间的显式依赖关系这是一种有用的方法,可以向Terraform提供提示,即使在Terraform配置中没有直接依赖关系,某些资源也应该在其它资源之前被删除。
https://raw.githubusercontent.com/antonbabenko/terraform-best-practices/master/snippets/locals.tf
如果var.website
不是一个空的map,则必须设置必需参数 index_document
。
可选参数error_document
可以省略。
还有一个研讨会,供想要练习本指南中描述的某些内容的人使用。
内容在这里 - https://github.com/antonbabenko/terraform-best-practices-workshop
Terraform 官方文档详细阐述了 配置的方方面面。请仔细阅读以便理解本节的其余部分。
本节内容将介绍本书中所使用的核心概念。
类似aws_vpc
, aws_db_instance
这样的都统称为资源。单个的资源一定属于某个提供商(Provider),它接受参数,输出属性,并且有一个生命周期。资源可以被创建、检索、更新和删除。
资源模块是一组连接的资源,它们共同执行通用操作(例如,AWS VPC Terraform module 创建VPC、子网、NAT网关等)。它依赖于提供商配置,该配置可以在其中定义,或在更高级别的结构(如在基础设施模块)中定义。
基础设施模块是一组资源模块的集合,这些资源模块在逻辑上可能没有连接,但在当前的情况/项目/设置中具有相同的目的。它定义了提供商的配置,这些配置传递给下游资源模块和资源。通常,它的范围限定在每个逻辑分隔符(例如,AWS区域、Google项目)的一个实体中。
例如,terraform-aws-atlantis 模块使用像 terraform-aws-vpc 和 terraform-aws-security-group 这样的资源模块来管理在 AWS Fargate 上运行 Atlantis 所需的基础设施。
另一个例子是 terraform-aws-cloudquery 模块,其中使用 terraform-aws-modules 的多个模块一起管理基础设施,并使用Docker资源来构建、推送和部署Docker镜像。所有这些都在一个集合中。
组合是由跨越多个逻辑分隔区域(例如,AWS区域、多个AWS账户)的基础设施模块组成的集合。组合用于描述整个组织或项目所需的完整基础设施。
组合由基础设施模块组成,这些基础设施模块又由资源模块组成,这些资源模块实现各个资源。
数据源执行只读操作并依赖于提供商的配置,它用于资源模块和基础设施模块。
数据源terraform_remote_state
作为高级模块和组合的粘合剂。
外部数据源允许外部程序充当数据源,并公开任意数据以供 Terraform 配置中的其他地方使用。这里有一个来自terraform-aws-lambda 模块的示例,其中文件名通过调用外部Python脚本计算。
http 数据源向给定的URL发出HTTP GET请求,并导出有关响应的信息,这对于从不存在本机 Terraform 提供商的端点获取信息通常很有用。
基础设施模块和组合应该将它们的 Terraform state 保存在远程位置,以便可以以可控的方式被他人检索(例如指定ACL、版本控制和日志记录)。
提供商、配置器等术语在官方文档中已经有很好的描述,因此在这里重复没有意义。我认为,它们与编写良好的Terraform模块关系不大。
虽然单个资源就像基础设施中的原子一样,但资源模块是分子。模块是最小的可版本化和可共享的单位。它具有精确的参数列表,为这样一个单元实现基本逻辑来完成所需的功能。例如,terraform-aws-security-group 模块可以根据输入创建aws_security_group
和aws_security_group_rule
资源。这个资源模块本身可以与其他模块一起使用来创建基础设施模块。
使用模块的输出和数据源执行跨分子(资源模块和基础设施模块)的数据访问。
组合之间的访问通常使用远程状态数据源来执行。 有多种方法可以在配置之间共享数据。
当把上面描述的概念放在伪关系中时,它可能看起来像这样:
本文档旨在系统性描述使用 Terraform 的最佳实践,并为 Terraform 用户遇到的最常见问题提供建议。
Terraform 是一款非常强大(如果不是现在最强大的)的工具,也是最常用的基础设施即代码管理工具之一。它使开发人员可以做很多事情,并且帮助开发人员轻松应对各种复杂实现或集成。
本书中描述的一些信息可能并不像是最佳实践。为了帮助读者区分哪些是已经确立的最佳实践,哪些是个人偏好的最佳实践,我有时使用提示来提供一些上下文,并使用图标来指定与最佳实践相关的各个小节的成熟度水平。
本书于2018年在阳光明媚的马德里开始编写,可在此处免费获取 - https://www.terraform-best-practices.com/ 。 经过若干年不断迭代更新,它包含了更多与Terraform 1.0相关的实际最佳实践。最终,本书应该包含大多数无可争议的最佳实践和对 Terraform 用户的建议。
Please contact me if you want to become a sponsor.
若您有意参与本书翻译,请与我联系。
随着社区的日益成熟和新思路的不断实践和验证,我一直希望得到反馈并更新本书。 如果你对某些话题感兴趣,请开启新问题,或者点赞您感兴趣的问题。如果您有补充内容并且想要贡献,请提交pull request(不必担心文字是否华丽)。
本书在不同贡献者和翻译人员的协助下由 Anton Babenko 维护。
本书使用Apache 2 License许可协议,具体请查看协议本身。
本书作者和贡献者无法保证本书内容的有效性。 请确保您了解本书提供的信息是免费提供的,并且您与此内容或项目相关的任何人之间没有任何协议或合同。 作者和贡献者不承担任何一方因本内容中包含的、关联的或链接的信息的错误或遗漏而造成的任何损失、损害或中断的任何责任,无论此类错误或遗漏是由于疏忽、事故或任何其他原因造成的。
版权所有 © 2018-2023 Anton Babenko。
FTP (Frequent Terraform Problems)
- 编排工具(Orchestration tool)
- 代码检查器(Code linter)
- 版本管理器(Version manager)
- 拉取请求自动化(Pull Request automation)
- 用于预提交框架 Terraform 的 git 钩子集合
- 拉取请求中 Terraform 的云成本估算。 也适用于 Terragrunt、Atlantis 和 pre-commit-terraform。
应指定资源和基础设施模块的版本。 提供商应该在模块之外配置,但只能在组合中配置。 提供商和 Terraform 的版本也可以被锁定。
没有主要的依赖管理工具,但有一些技巧可以减少依赖地狱的问题。 例如, 可用于自动化依赖更新。 Dependabot 创建拉取请求以确保您的依赖关系安全和最新。 Dependabot 支持 Terraform 配置。
很少资源,没有外部依赖。 单个 AWS 账户。 单一区域。 单一环境。
Yes
多个 AWS 账户和环境,使用 Terraform 的现成基础设施模块。
Yes
许多 AWS 账户,许多地区,迫切需要减少复制粘贴、自定义基础设施模块、大量使用组合。 使用Terraform。
WIP 生产中
very-large
多个提供商(AWS、GCP、Azure)。 多云部署。 使用Terraform。
No
medium
多个 AWS 账户和环境,现成的基础设施模块,使用 Terragrunt 的组合模式。
No
large
许多 AWS 账户,许多地区,迫切需要减少复制粘贴、自定义基础设施模块、大量使用组合。 使用 Terragrunt。
No
very-large
多个提供商(AWS、GCP、Azure)。 多云部署。 使用 Terragrunt。
No
CAST AI — Cut your Kubernetes costs by 60%+ on average. First cluster optimization FREE!
Speakeasy — Terraform Providers, SDKs and docs for your API. Make your API enterprise-ready!