本章内容包括:
- 基础设施即代码
- Terraform的基本组件
- 声明式语言与图形
- Terraform的部署工作流
- Terraform与OpenTofu的关系
第一次设置亚马逊网络服务(AWS)虚拟私有云(VPC,AWS用来隔离网络的抽象概念)时,我是手动完成的,花费了好几天时间。即使我有了一定的经验,手动创建和配置符合建议最佳实践的VPC仍然需要几个小时。
当我第一次接触Terraform时,我再次尝试建立一个VPC。Terraform是我使用的第一个基础设施即代码工具:Terraform承诺让我使用简单的编程语言定义基础设施(如VPC),然后让Terraform自己处理基础设施的创建。与手动创建不同,我通过编写代码来描述VPC及其所需的所有组件。
当我第一次运行代码时,我感到震撼。Terraform启动了创建该高可用VPC结构所需的70多个资源,并且只用了大约一分钟的时间。从那时起,每当我需要一个VPC时,只需重用那段代码。之前需要几个小时才能完成的任务,现在成了不值一提的小事。更重要的是,这个任务突然变得简单了。
这带来了巨大的即时收益。没有任何网络知识的开发人员也能够独立创建VPC。基础设施变成了一系列构建块,我们可以一次又一次地在完全不同的系统中复用它们。甚至这些系统的质量也得到了提升,因为一个项目对构建块的改进会辐射到使用它的其他项目。如果有人需要为VPC模块添加新功能,该功能将对公司里的所有项目都可用。
Terraform还促成了我所在公司另一项巨大的文化转变。一旦引入Terraform,系统就变得可重复构建。也就是说,如果你用Terraform构建了一个平台,那么非常容易再创建一个相同的副本。我们不再只有一个产品测试环境,每个开发人员都可以随时创建自己的环境。开发人员可以在拥有完整真实系统的环境中开发新特性。如果有人想实验某些变更,他们可以轻松地将这些变更应用到自己的环境,并随意进行迭代。
本书的目标是帮助你学习如何使用Terraform,将这些好处带到你的项目中。我们将从基础开始,让你立刻收获益处,然后扩展到更高级的话题。本书还将包含OpenTofu的支持与示例,OpenTofu是Terraform的开源分支,我们将在本章稍后解释这个分支的历史。
1.1 基础设施即代码
基础设施即代码(IaC)是一类允许开发人员通过编码实践来配置基础设施的技术。Terraform并不是唯一的IaC工具。Pulumi是另一个供应商无关的框架,而AWS CloudFormation和GCP Deployment Manager是特定供应商的解决方案。尽管如此,Terraform仍被认为是IaC的标准,因为它是最成熟的项目之一,覆盖面也最大。与CloudFormation等特定供应商系统不同,Terraform不仅仅局限于与单一供应商合作。Terraform还使用了一种自定义的声明式语言,这使得它比其他框架更易于使用。
IaC是系统管理方式的一种自然演变。曾经,每台机器都需要单独配置其工作。系统管理员会共享脚本或教程,但大多直接与机器进行交互。随着云计算的普及,这种方式显然无法扩展。为了配置大量机器,像Puppet和Chef这样的配置管理工具应运而生,而像Packer这样的工具则用于构建可重用的黄金镜像。IaC框架迈出了下一步,它允许整个平台被编码并反复部署。
1.1.1 使用IaC的软件开发实践
IaC的一大魅力在于,许多多年来为软件开发所开发的工具和实践可以直接应用于基础设施。IaC可以通过Git进行版本管理,使用代码检查工具进行质量控制和安全扫描,并通过专门的持续集成和持续部署(CI/CD)流水线自动进行测试。更重要的是,团队可以将为软件工程开发的原则和实践直接应用于基础设施。
这在整个技术栈中都带来了好处。使用Git等版本控制系统(VCS)与GitHub或GitLab等现代VCS主机相结合,提供了许多优势,例如审计追踪、版本管理、自动化工作流,并且可以让多人在不互相干扰的情况下共同参与同一个项目。可以创建分支来测试新功能,而拉取请求则可以要求来自不同团队的评审并通过测试。
这一切都加总为提高质量,尤其是对于更复杂的系统。当操作得当时,这也意味着即使系统复杂性增加,也能够更快速、更有信心地进行开发。
1.1.2 可重复性和可共享性
软件允许可重复性。如果你编写了一个程序,你可以反复运行这个程序。将这种可重复性带入基础设施领域,可以让开发人员创建可以在代码库甚至跨项目中反复使用的构建模块。Terraform通过模块来实现这一点(我们将在第三章讨论模块)。
通常,配置一项基础设施——无论是VPC(云供应商如GCP和AWS提供的常见组件)、自动扩展组,还是像Kubernetes这样的编排系统——都需要研究和工作,而且通常需要花费大量时间手动完成。将这些知识封装成可复用的软件块,允许其他人利用和在其基础上构建,而无需重新进行这些工作。可重用的模块也可以随着时间的推移进行改进,这使得这些改进可以在整个技术栈中得到推送。
例如,一家公司可以创建一个VPC模块,在多个团队之间共享。随着时间的推移,他们可能会添加更多功能,如使用VPC端点或增加安全监控。Terraform和其他IaC框架允许这些改进像软件工程师升级他们用于开发的库一样,在整个组织中传播。
1.1.3 持续集成与持续部署
软件的重要部分是集成和部署变更,使用IaC也不例外。
甚至有专门为运行Terraform构建的CI/CD平台。这些Terraform专用系统变得如此流行,以至于它们现在有了自己的名称:TACOS(Terraform自动化与协作软件)。Terraform的制造商HashiCorp提供了一个名为HCP Terraform的解决方案,它作为一种软件即服务(SaaS)产品运行,还有一个名为Terraform Enterprise的自托管版本。此外,还有像Spacelift和Scalr这样的第三方供应商,它们运行自己的系统。每个平台都与GitHub和GitLab集成。这使得它们能够自动发布来自Git标签的模块,运行预测性计划来查看如果拉取请求中的代码变更被运行会发生什么,甚至在变更合并时管理部署。
通常同时使用专门的系统,如Terraform Cloud,以及更传统的CI/CD系统(如GitHub Actions、CircleCI或Jenkins)。更通用的系统具有更高的灵活性,可以运行多种类型的任务。这使得开发人员可以添加测试、格式化、代码检查等任务,同时将结果与平台中其他项目的结果放在一起。
持续集成与持续部署是Terraform中最重要的话题之一;第七章专门讨论了这一主题。
1.2 Terraform概述
Terraform的存在是为了创建和管理基础设施。它之所以如此强大,主要原因之一是它提供了一个统一的接口来连接成千上万不同的供应商和软件系统。无论你是在本地系统上创建文件,还是在云中启动机器,编辑域名系统(DNS)记录,甚至是订购披萨,使用Terraform时,你只需要知道一种语言。
Terraform通过将所有系统特定的组件抽象成“提供者”来实现这一点。如图1.1所示,每个供应商都有自己的资源和API,但所有这些都被包装在提供者背后,开发人员不需要关心这些细节。相反,Terraform核心(存在于Terraform命令行界面CLI中)负责将开发人员编写的Terraform语言与底层提供者之间进行转换,而提供者则处理与供应商系统的通信。
组件的这种分解方式非常强大。提供者系统允许供应商和社区成员开发自己的Terraform接口,这些接口可以独立于Terraform进行更新和发布。目前,Terraform提供者注册表(registry.terraform.io/browse/prov…)中有超过3280个提供者,开发人员可以使用它们。
Terraform在其他领域也采用了这种方法。Terraform有一个“状态”概念,它是关于其管理的资源的元数据集合。每次部署都有自己的状态。为了管理这些状态,Terraform有后端,后端告诉Terraform在哪里存储状态。后端能够存储多个部署,而Terraform将其分解成工作空间。如果你将Terraform看作一个桌面程序,比如文字处理器,那么你的后端就像文件系统,而每个工作空间则像你在那里保存的不同文件。
1.2.1 Terraform语言
Terraform在IaC框架中独具特色的一个方面是它使用HashiCorp配置语言(HCL)。HCL的设计目标是易于阅读,即使是那些可能不太精通该语言的人也能轻松理解。HCL还是一种声明式语言,这意味着开发人员定义的是理想的最终状态,而不是实现该状态所需的步骤。
其他IaC框架通常使用现有的命令式语言,或者试图在像YAML这样的仅数据语言上进行构建。使用像YAML这样的仅数据语言有许多局限性,通常通过使用插值方案来解决这些问题,但这往往会影响可读性。
HCL是HashiCorp创建的,并被多个产品使用:Packer、Nomad和Consul都使用HCL。每个使用HCL的HashiCorp项目都会在HCL中公开自己的资源和功能,同时在结构资源的方式上有一些微妙的差异。本书重点介绍Terraform版本的HCL,但一旦你理解了语法,切换到其他HCL版本应该会很容易。除非另有说明,本书在所有示例中使用Terraform HCL。
1.2.2 Terraform CLI和核心
Terraform CLI是运行和管理Terraform项目的主要方式。它包含将开发人员编写的HCL与所需的供应商特定库和API连接起来的引擎,用以管理底层资源。
CLI用于创建新基础设施、更新它、销毁它以及应用手动更改。CLI具有便于与CI/CD平台一起使用的功能,许多此类平台都是围绕CLI构建的。CLI还有一些帮助开发人员的命令,例如内置的代码格式化系统和生成Terraform管理的基础设施可视化的系统。
CLI与Terraform本身紧密结合。Terraform核心内置于CLI中,只能通过CLI访问。运行Terraform的CI/CD系统(我们将在第八章讨论),如HCP Terraform或Spacelift,都是围绕Terraform或OpenTofu CLI构建的,并直接调用它。与Go或C等语言不同,后者可以编译代码,但它类似于Python或Node这样的解释型语言,在这些语言中,你需要一个工具来运行程序。
对于大多数开发人员来说,即使他们也有可用的CI/CD系统,CLI也将被广泛使用。本书将随着新话题的介绍,包含使用CLI的示例。
1.2.3 提供者
提供者是Terraform的插件,它提供了一组用于与特定供应商管理基础设施的数据源、资源(我们将在下一章详细讨论)和功能。这些通常是对API的封装。一些提供者由HashiCorp直接管理,而其他则由其特定供应商发布(见图1.2)。
提供者通常使用Go语言编写(Terraform本身也是用Go语言编写的),并通过gRPC(一个高性能远程过程调用框架)与Terraform进行通信。它们充当Terraform与开发人员尝试与之交互的供应商系统之间的接口。从理论上讲,只要提供者实现了适当的gRPC接口,提供者可以用其他语言编写,但在实践中,目前只有少数非常实验性的例子,而且没有任何是可以投入生产使用的。
不用担心,如果你从未使用过gRPC,实际上在使用Terraform时并不需要了解gRPC,除非你自己制作自定义提供者,哪怕那时你大部分时间也会使用现有的Terraform库与gRPC进行交互。由于大多数供应商创建并管理自己的提供者,绝大多数Terraform用户不需要自己创建提供者(虽然如果你需要,我们会在第12章讨论如何创建)。
提供者通常与供应商或平台之间具有一对一的关系。如果你想与AWS进行交互,那么你就使用AWS提供者。GCP也有提供者,而Azure则发布了多个提供者以覆盖其产品。就像在其他编程语言中使用第三方库一样,提供者会有自己的命名法、风格和文档。
1.2.4 供应商
Terraform的整个目的是管理基础设施。Terraform本身是供应商无关的。它不关心管理什么样的基础设施,只要有提供者暴露它。每个供应商都有自己的API、资源和工作方法,这些都必须通过提供者进行抽象。几乎任何你能想到的领域都有供应商:
- 云服务提供商
- DNS提供商
- 数据分析
- 虚拟机托管
- Git仓库
- 认证系统
甚至还有为披萨外卖提供的提供者(mng.bz/BXDq),以及一个提供只读访问权限的提供者,告诉你哪些麦当劳的冰淇淋机目前坏了(mng.bz/dXPz)。Terraform是一个被广泛采用的平台,因此,大多数供应商都维护着相当最新的提供者。
1.2.5 后端
后端用于自定义工作空间存储其状态文件的位置。默认情况下,工作空间使用本地后端,这意味着如果你在本地运行工作空间且没有其他更改,那么状态文件将直接存储在本地文件系统上。这对于测试非常有用,但这也意味着工作空间只能从你的计算机上进行操作,这对于团队来说并不具备可扩展性。
后端通过允许状态存储在其他位置来解决这个问题。有些后端适用于主要云供应商的对象存储系统(如AzureRM、GCS、S3等),也有适用于开源数据库的后端,甚至有一个HTTP后端,可以用于创建简单的自定义服务。一些后端(如远程和云后端)甚至提供API,Terraform可以使用这些API来执行特殊操作。
使用除本地后端之外的后端时,团队协作会更加容易。在第6章,我们将讨论不同类型的后端及其优势。
1.2.6 工作空间
Terraform工作空间表示具有特定后端和输入变量集的Terraform代码库的部署。将Terraform与其他软件开发项目进行比较时,可以将工作空间类比为程序的特定安装——就像其他软件一样,你可以有多个安装,并且每个安装都是完全独立的,具有自己的配置和保存的数据。
每个Terraform工作空间都包括代码库、特定工作空间的配置以及当前在工作空间中创建的所有资源的列表(该列表称为状态,我们将在第4章进一步讨论)。一旦编写了代码,就可以从这些代码初始化一个Terraform工作空间。配置并初始化后,可以创建、更新和销毁该代码所管理的基础设施。
一个Terraform代码库可以有无限数量的部署。如果这些部署都使用相同的后端,那么每个部署将在该后端中获得自己的位置。通常,生产环境、预发布环境和临时特性部署都会共享一个共同的后端。有些部署是长期存在的,而有些可能只在特性开发期间存在。开发人员在Terraform项目中工作时,通常会有一个本地工作空间,以便快速迭代,使用本地文件系统作为后端。
1.3 声明式语言
Terraform使用声明式语言来编码基础设施。声明式语言是一类语言,其中开发人员定义代码运行后应该是什么样子。使用声明式语言时,重点是定义最终状态,然后运行该语言的引擎创建一个行动计划,将系统带到所需的最终状态。
声明式语言通过计算机科学中的一个概念——有向无环图(DAG)——来创建它们的计划,在这个上下文中,DAG基本上是一个必须按特定顺序完成的操作列表。Terraform将这些称为计划。在第5章中,我们将更深入地讨论DAG,特别是如何调试Terraform计划中的问题。
声明式语言通常在配置管理和IaC框架中更受欢迎。Terraform HCL、Kubernetes的YAML语言、CloudFormation的YAML以及Puppet的DSL都是声明式语言的例子。
1.3.1 声明式语言与命令式语言
大多数开发人员熟悉命令式语言。在使用如Javascript和Bash等语言时,开发人员使用语句显式地改变系统的状态。命令式语言描述的是如何做某事,而声明式语言描述的是结果应该是什么。声明式语言专注于结果,而不是到达结果的步骤,这与命令式语言非常不同。
可以这样理解,声明式语言主要使用名词和形容词,而命令式语言主要使用动词。在Terraform中,你永远不会说“检查这个机器是否存在,如果不存在则创建机器,然后应用这个配置。”相反,你会描述你想要的机器,而Terraform会将其转换为创建该机器的操作。
在基础设施的上下文中,这非常强大。它消除了编写迁移的需要,使开发人员可以专注于他们希望达到的最终状态。当你将其与版本控制结合时,它允许在不同版本之间来回切换,而不需要在它们之间创建特定的代码路径。
1.3.2 依赖关系解析
现代软件系统包括大量的基础设施。即使是一个基本的Web应用程序,也通常包括应用程序、文件系统、数据库、缓存和DNS记录。这些组件之间存在相互关系。应用程序必须从数据库和缓存中读取,而DNS记录需要知道应用程序主机的位置。声明式语言使定义这些组件之间的关系变得更加容易,通常可以自动推断这些关系,而无需开发人员显式地定义它们,如图1.3所示。
这种关系存在于用于启动基础设施的工具之外。应用程序需要知道数据库的位置以及使用什么凭证,否则它将无法访问数据库,这意味着数据库必须首先启动。这些资源之间的关系是通过配置定义的。应用程序必须配置从数据库获取的凭证,而DNS记录必须根据来自主机的IP地址进行配置。
像Terraform这样的声明式语言就是围绕定义这些关系构建的。当开发人员定义资源并运行像Terraform这样的IaC工具时,该工具将其代码转换为按正确顺序创建基础设施所需的操作。它看到开发人员在一个资源中使用了另一个资源的值来配置另一个资源,并利用这些信息来确定资源之间的关系应该是什么样子。这使得Terraform能够按正确的顺序创建资源,而无需开发人员显式地定义该顺序。Web应用程序的开发人员无需说“先部署数据库再部署应用程序”——一旦开发人员说应用程序正在使用来自数据库的值,这种关系就已经被定义好了。
1.3.3 声明式语言的陷阱
尽管声明式语言有许多好处,但也存在一些陷阱。大多数开发人员会遇到的最大问题是,声明式语言无法轻松定义资源之间存在循环依赖的系统。
循环依赖是一种关系类型,其中一系列资源在图中形成一个循环,如图1.4所示:
- 资源2需要资源4才能启动。
- 资源4需要资源3才能启动。
- 资源3需要资源2才能启动。
这三种资源最终相互等待,因此它们都无法启动。像Terraform这样的系统无法创建存在循环依赖关系的资源,除非采用某种手动解决方法。
一般来说,分布式系统中不推荐使用循环依赖关系,但在某些情况下,它们可能会出现。在第5章中,我们将讨论Terraform的一些工具,帮助解决无法避免的循环依赖情况。
1.4 Terraform部署流程
使用Terraform进行部署有一个流程。如图1.5所示,首先提出变更,初始化工作空间,创建计划,审查变更,然后如果变更通过审查,就应用它。初始化、计划和应用阶段都是由Terraform CLI发起的,尽管通常是CI/CD系统调用CLI,而不是由人工手动执行。
1.4.1 期望变更
在某个时刻,某人将希望更改他们的系统。你可能有一个新项目,其中的变更只是创建初始基础设施,或者可能是需要为现有项目添加新特性、修复漏洞、更新依赖项。
无论变更是什么,都需要进行编程。根据变更的大小,可能需要对底层系统进行一些研究,并由开发人员进行实验。就像任何软件项目一样,这个阶段的实际工作流在不同公司和团队之间可能差异很大。
一个非常简单的变更可能是向现有环境添加一个新的计算实例。这意味着添加一个代表Elastic Cloud Computing(EC2)实例的单一Terraform资源(我们将在下一章中讨论)。其他变更则更复杂,可能需要删除和替换资源,而有些则较为简单,可能只需要改变几个属性,同时保留资源不变。Terraform之所以如此强大的一点是它能够区分这些不同的情况,从而做出正确的决策。
1.4.2 初始化
一旦变更被编写出来,就该运行Terraform了。运行的第一部分是初始化阶段。在初始化阶段,Terraform会下载作业中定义的任何模块或提供者,并为本地系统准备开发环境。这些模块和提供者会从公共Terraform注册表下载,或者如果进行了配置,则从私有注册表下载。
运行terraform init时,首先初始化后端(我们将在后面的章节中详细介绍不同的状态后端,但现在只需知道,我们正在遵循使用默认本地后端来存储状态的常见开发实践)。一旦后端初始化,init命令将安装所需的提供者。如果我们使用了任何模块,它们也会在此安装。
在添加新EC2实例的情况下,我们只会看到AWS提供者被添加。
列出 1.1 初始化Terraform项目
$ terraform init ①
Initializing the backend... ②
Initializing provider plugins... ③
- Finding HashiCorp/aws versions matching "~> 4.0"... ④
- Installing HashiCorp/aws v4.41.0... ⑤
- Installed HashiCorp/aws v4.41.0 (signed by HashiCorp) ⑥
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control
repository so that Terraform can guarantee to make the same selections by
default when you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to
see any changes that are required for your infrastructure. All Terraform
commands should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget,
other commands will detect it and remind you to do so if necessary.
① 在我们的终端中,我们运行带有init子命令的terraform命令。
② 在初始化时,Terraform确认它可以与您的状态后端通信。
③ 此命令还会下载项目使用的任何模块或提供者。
④ Terraform解析版本约束并选择特定版本。
⑤ 然后安装特定的提供者。
⑥ 提供者经过加密签名,以确保其来源可靠。
1.4.3 计划
Terraform计划既是一个操作也是一个输出——你运行terraform plan命令,它计算一个可以在应用阶段使用的计划。计划过程本身有三个子阶段:
- 刷新——Terraform直接从供应商读取基础设施的真实状态,以及代码中存在的任何数据源,以识别自上次运行Terraform以来可能发生的任何变化。
- 比较——Terraform将上一步的状态与它根据代码认为应该存在的状态进行比较。
- 计划——Terraform创建一个有向无环图(DAG)的操作序列,以使基础设施与代码期望的状态一致。
在我们添加新实例的例子中,我们可以看到代码从AWS查找一些信息,然后展示提议的变更。这与我们的预期一致——我们想要添加一个实例,计划显示Terraform已注意到这一变化,并已在其计划中安排了一项创建操作来创建hello_world aws_instance。
列出 1.2 使用Terraform规划变更
$ terraform plan -out tfplan ①
data.aws_vpc.default: Reading... ②
data.aws_ami.ubuntu: Reading... ②
data.aws_ami.ubuntu: Read complete after 0s [id=ami-0cb81cb394fc2e305] ②
data.aws_vpc.default: Read complete after 0s [id=vpc-cc5449a4] ②
data.aws_subnets.default: Reading... ②
data.aws_subnets.default: Read complete after 0s [id=us-east-2] ②
Terraform used the selected providers to generate the following execution ③
plan. Resource actions are indicated with the following symbols:
+ create ④
Terraform will perform the following actions:
# aws_instance.hello_world will be created
+ resource "aws_instance" "hello_world" { ⑤
+ ami = "ami-0cb81cb394fc2e305"
+ arn = (known after apply) ⑥
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t3.micro"
## "known after apply" attributes truncated ##
+ source_dest_check = true
+ subnet_id = "subnet-b96b6ed1"
## "known after apply" attributes truncated ##
+ tags = {
+ "CreatedBy" = "terraform"
}
+ tags_all = {
+ "CreatedBy" = "terraform"
}
}
Plan: 1 to add, 0 to change, 0 to destroy. ⑦
Changes to Outputs: ⑧
+ aws_instance_arn = (known after apply) ⑨
① 这里我们运行plan子命令并保存输出。
② Terraform首先刷新状态并查找数据源。
③ 输出的其余部分描述了Terraform将要做的变更。
④ 该计划只有创建操作,但更新、替换或删除也是选项。
⑤ 每个资源及其更改将显示供用户评估计划。
⑥ 某些字段将在资源创建后才可用。
⑦ 在计划的末尾有一个简短的总结。
⑧ 如果有任何输出被更改,它们将在这里列出。
⑨ 输出的新值将在应用后才会知道。
一旦完成,计划就会被创建:它将被保存到文件、丢弃或作为应用命令的一部分自动应用。
如前所述,计划是DAG,这只是一种Fancy方式表达计划有许多相互依赖的资源。使用Terraform图形命令,你可以输出图形文件,借助像GraphViz这样的工具将它们转换为这些资源的可视化表示。图1.6展示了从Terraform直接输出的项目计划,它描述了与我们之前运行terraform plan命令时相同的内容,但使用了图形而不是文本。这里显示的data.aws_subnet_ids图形与计划创建时显示的相同。
1.4.4 应用
在应用阶段,Terraform引擎会执行计划阶段创建的所有操作。这意味着按照计划定义的顺序创建、删除和更新资源。在应用阶段,我们提供在前一节中创建的计划文件——如果没有提供文件,Terraform将从应用开始时创建一个计划。
我们的期望变更只是单个资源,因此应用阶段相对较短。
列出 1.3 使用terraform apply变更基础设施
$ terraform apply tfplan ①
aws_instance.hello_world: Creating... ②
aws_instance.hello_world: Still creating... [10s elapsed] ③
aws_instance.hello_world: Creation complete after 12s
➥[id=i-01792587739c8e453] ④
Apply complete! Resources: 1 added, 0 changed, 0 destroyed. ⑤
Outputs:
aws_instance_arn = "arn:aws:ec2:us-east-2:966612157092:instance/i-
➥01792587739c8e453" ⑥
① 在这里我们运行apply子命令,并传递我们之前创建的计划文件。
② 当Terraform执行apply时,它会向你更新正在进行的操作。
③ 有些资源可能需要一些时间才能创建。
④ 一旦创建完成,资源将有一个唯一的ID与之关联。
⑤ apply命令也有一个总结,和plan命令类似。
⑥ 现在资源存在了,输出已经更新。
我们的应用已成功执行,新的实例已启动!你可以在创建完成的消息中看到实例的ID,并且由于这段代码将其定义为输出,因此你可以在输出部分看到它。创建实例的总时间是12秒。
这个过程可能会根据资源的不同而有所不同。Terraform尝试通过尽可能多地并行操作来加速这个过程。无论如何,从Terraform启动基础设施通常比手动操作更快,因为Terraform所面临的延迟通常是在供应商那边,尤其是在资源被配置时。
一旦完成,Terraform会显示状态消息,告诉你创建、更新或销毁了多少资源,以及发生的任何错误。在下一章中,我们将详细讨论如何使用Terraform语言来定义这些资源。
1.5 人们用它做什么?
Terraform是一个非常棒的工具,但人们到底是用它做什么的呢?这个问题之所以有些难回答,是因为它几乎可以用来做任何事情。在过去几年中,我看到了几个令人惊叹的使用案例。
1.5.1 机器学习训练
机器学习现在非常流行,但训练一个模型的成本极其高昂。公司常用的一种方式是从云提供商(如Google、Azure和AWS)租用设备。这让研究人员能够在需要时才使用设备,从而节省成本。
训练机器学习模型通常涉及构建一个集群。这意味着创建网络、定义机器的模板、提供高性能文件系统,并最终提供一种将模型保存在高可靠性存储系统中的方式。为了让节点访问文件系统和存储系统,需要创建并分配权限。为了利用自动扩展,运行的任务数量也需要进行管理,这就要求使用Redis集群和Lambda函数来触发自动扩展。
这一切听起来非常复杂,对吧?对于大多数团队来说,这是一个轻松就能耗费几周时间的项目。Terraform为这样的项目提供了两个明显的优势:
- Terraform代码本身可以用于协作并构建集群设计,允许从简单的组件开始,并随着时间的推移进行迭代改进。也许集群最初没有自动扩展功能,之后可以再添加。
- 通过使用Terraform,团队可以在几秒钟内启动和关闭多个集群,如图1.7所示。如果团队有定制需求,那么修改现有系统就足够了,而不需要从零开始创建。
1.5.2 API和Web服务
另一个非常常见的模式是托管Web应用程序或API,特别是当你希望它们能够扩展时。你的标准Web应用程序通常包含许多相同的部分:负载均衡器用于引导流量,一系列的“任务”或Web应用程序实例,用于安全的SSL证书,以及DNS记录以为网站提供易于使用的地址。它们通常还会有一个缓存层和数据库。
这些组件也建立在网络层之上。如图1.8所示,这意味着需要创建子网,确保私有子网具有互联网访问权限,以及其他各种较小的组件。
通过将所有这些内容放入一个Terraform模块中,它使得开发人员能够轻松启动他们的服务,而无需考虑这些组件。从他们的角度来看,他们只需要担心自己的代码,模块会处理其他一切。虽然构建这样一个模块需要一些努力,但一旦构建完成,它可以扩展以支持更多用例和高级功能,然后可以将这些功能推广到所有使用它的人。
1.5.3 单点登录认证结构
从完全不同的方向来看,我还看到Terraform被用于管理像Okta这样的大型单点登录系统。这些系统可能非常复杂,包括组、用户、应用程序、策略、角色等,如图1.9所示。
将这些系统纳入Terraform管理可以使得查看和管理权限变得更加容易,尤其是在大型组织中。强制使用Terraform可以提供更强的安全控制,因为它更容易强制执行诸如多个人签署变更的政策,同时通过源代码管理创建自然的审计跟踪。
1.5.4 快速原型设计
最后这一项不是关于特定系统,而是关于能力。我花了很多时间在创业公司工作,其中一个真正产生影响的因素是能够快速实验和创新想法的能力。使用Terraform允许团队通过抽象化基础设施的琐碎细节来实现这一点。
可以从黑客马拉松或其他快速项目的角度来考虑。如果开发人员需要花一天时间来搭建环境,再花半天时间理解启动队列系统的不同参数,然后还需要弄清楚数据存储,那么他们已经在一个项目的开销上花费了大量时间。相比之下,如果他们从专家编写和维护的可重用Terraform模块开始,那么基础设施组件就成了一个可以迅速利用的工具,而他们可以专注于自己的主要目标。
在本书的过程中,我的目标是为你提供使用Terraform的工具,使这一目标成为现实。在接下来的几章中,我们将讨论Terraform的基础组件。之后,我们将扩展到更高级的话题,例如创建可重用模块和构建CI/CD流水线。在本书结束时,你不仅应该牢固掌握Terraform本身,还应该了解如何利用它提高你项目和团队的质量和可靠性。
1.6 Terraform与OpenTofu
在2023年底,Terraform的制造商HashiCorp宣布,他们将改变Terraform及其之前作为开源发布的几个其他工具的许可证。这意味着,从Terraform版本1.6开始的版本不再是开源的,而是采用自定义专有许可证。可以说,这一决定在社区中引起了轩然大波,没过多久,一些公司联合起来,将Terraform分叉成一个新的开源项目,名为OpenTofu。
1.6.1 HashiCorp与开源的历史
Terraform最初作为一个开源项目发布,使用Mozilla公共许可证(MPL)。当公司和开发人员以开源许可证发布软件时,他们为项目的用户提供了多种权利,例如查看和修改源代码、发布自己的版本以及选择可以运行软件的系统。
开源软件有一些重大好处。由于开源项目通常是免费提供的,人们可以在没有前期成本的情况下亲自尝试它。其他开源开发人员和公司也更可能围绕开源项目构建工具,而不是围绕专有项目构建工具。对于Terraform来说,这意味着一个庞大的开发者社区构建了提供者、文档工具、测试框架和模块。它还意味着其他公司将Terraform整合到他们自己的产品中。Terraform并不是HashiCorp唯一的开源项目。HashiCorp还有Vault和Consul等产品,其他公司也围绕这些产品构建了自己的产品。
在过去的几年里,HashiCorp在看待一些开源项目时似乎出现了新趋势,转而专注于内部开发和新产品。这在2021年加剧,当时它更新了GitHub上的贡献者文档(mng.bz/rKBE),明确表示不会再审查任何外部的Terraform项目的拉取请求。
除此之外,许多社区请求的特性被忽视,即使这些特性已经由第三方完全实现和测试。随着时间的推移,这使得更多的贡献变得不太可能,因为许多社区成员认为,Terraform最初的开源做法不再是公司优先考虑的事项。
1.6.2 HashiCorp许可证变更
在2023年,HashiCorp宣布从2024年起将停止发布其许多项目的开源版本,转而将代码发布在商业源许可证(BSL)下。这些项目不仅包括Terraform,还包括Nomad、Consul、Vault和其他一些HashiCorp项目。其他项目,如Terraform提供者本身以及HashiCorp多年来发布的许多单独库,仍然保持在MPL许可证下的开源状态。
BSL是一种共享源许可证。这意味着源代码仍然可以公开审查和审核。与开源软件不同,BSL软件具有额外的限制,旨在赋予软件所有者对其的垄断权力。BSL仍然允许个人甚至公司使用软件,但它限制了在未获得HashiCorp特别许可证的情况下,基于该软件构建产品的能力。
HashiCorp表示,这一许可证变更将防止竞争性服务基于其产品构建,这反过来将帮助它构建更好的产品。新许可证对此非常明确,因为它允许每个人在生产中使用Terraform,“前提是这种使用不包括将许可作品提供给第三方进行托管或嵌入,且与HashiCorp的产品竞争。”
1.6.3 社区反应
一旦正式宣布,许可证变更在技术界引发了广泛的讨论和辩论。在Reddit、Hacker News,甚至LinkedIn上,关于HashiCorp这一决策的影响和伦理问题的讨论(甚至争论)屡见不鲜,且至今仍在继续。
开源社区本身对于这一变更表现得相当直言不讳。许多人声称,Terraform以及其他HashiCorp产品的流行,若没有最初来自开源社区的支持,是不可能实现的。特别是Terraform,依赖第三方构建提供者,使得Terraform具有价值。此外,像Gruntworks这样的团体一直在填补Terraform周围的空白,构建测试框架和其他工具。对于许多人来说,许可证变更就像是被突然“抽走了地毯”。
在Terraform方面,受影响最大的群体是那些基于曾经的开源项目构建产品的公司,以及他们的客户。许多与Terraform程序交互的第三方产品,包括私有模块注册表和完整的持续部署平台。
最终,2023年8月发布了一份宣言,要求HashiCorp重新考虑许可证变更。这份宣言得到了150多家公司、11个软件项目以及750多名个人开发者的签署。该宣言不仅仅是一个请求,它明确表示,如果HashiCorp不参与讨论,将会创建一个新的软件项目,继续开发开源版本的Terraform。
1.6.4 OpenTofu分支
开源软件的一个主要好处是,任何开发人员都可以拿到开源软件的一部分并发布自己的版本。这被称为“分叉”项目。这个现象有很多例子:MariaDB是MySQL的一个分叉,Jenkins是Hudson的一个分叉,LibreOffice是从OpenOffice创建的。
OpenTofu作为一个分叉项目在HashiCorp没有回应宣言后宣布成立,如图1.10所示。这个宣布得到了广泛支持,几家公司(Scalr、env0、Spacelift和Harness)宣布将支持总共18名专门的开发人员,至少在未来五年内致力于这个新项目。此外,基于Terraform的开源项目支持者,例如Gruntworks(它创建了本书中将讨论的多个工具,如Terratest和Terragrunt),也承诺支持这个新项目。
OpenTofu还拥抱了开源和透明性。该项目已被接纳进入Linux基金会,并正在努力加入云原生计算基金会。通过将项目放入一个基金会中,他们确保该项目不会受到单一公司意愿的影响。作为其透明和开放性目标的一部分,OpenTofu项目有一个公开的征求意见(本质上是一个功能提案系统)流程,允许开发人员提交自己的开发想法。
1.6.5 OpenTofu与Terraform的兼容性
当然,分叉一个项目并非没有问题。许多人最大的担忧之一是他们的代码是否可以正常工作。如果你为Terraform编写了一些东西,你需要关注到底是哪个程序在运行它吗?
目前,OpenTofu仍然与Terraform编写的代码兼容。然而,OpenTofu也开始实现一些社区多年请求的功能。因此,OpenTofu可以被视为Terraform语言的超集,并在其基础上增加了额外的功能。这确实使得从Terraform迁移到OpenTofu变得更加容易,但如果你开始使用在HashiCorp Terraform中没有的功能,回迁到Terraform可能会变得更加困难。
也就是说,尽管OpenTofu保持了与Terraform语言的兼容性,但这也导致其发布略有延迟。为了重新实现Terraform中新增的功能,OpenTofu需要对该功能进行审核和评估,然后制定计划独立构建它。因此,Terraform通常会有一些新功能,而这些功能在OpenTofu中并不会立即可用。
1.6.6 使用OpenTofu
大多数情况下,OpenTofu是Terraform的直接替代品。一旦安装,tofu CLI将可在你的系统上使用。tofu CLI具有与terraform CLI相同的所有命令,这意味着你甚至可以将terraform命令创建一个别名为tofu,应该就能正常工作。
许可证变更的一个有趣影响是,许多包管理器(用于安装软件的工具)已经停止提供不再是开源的Terraform新版本。如果你使用的是Homebrew(一个常用的MacOS包管理器),你将无法获取任何版本高于v1.5.7的Terraform,许多Linux发行版也有类似情况。另一方面,OpenTofu得到了大多数开源包管理器的支持。
本书中的每个示例,除非另有说明,都可以同时在OpenTofu和Terraform中运行。在有差异的情况下,将会指出并进行描述。两者在语言本身的差异非常少,但随着我们进入后面的章节(尤其是关于测试和CI/CD的章节),我们将探讨这两个版本之间的影响和差异。
总结
- 基础设施即代码(IaC)使得软件开发的最佳实践可以应用于基础设施。
- 声明式语言专注于最终结果应该是什么样子,并允许底层引擎决定如何实现这一目标。
- Terraform是建立在HCL(HashiCorp配置语言)之上的。
- Terraform计划是有向无环图(DAG),这意味着操作是按特定顺序执行的,并且不允许循环依赖。
- Terraform有一个通用的工作流——init、plan、apply——开发人员在部署变更时会使用。
- Terraform init是Terraform下载任何需要的提供者或模块的地方。
- Terraform plan是Terraform将工作空间中的资源与代码中期望的资源和配置进行比较,以创建一个对齐这两者的计划的地方。
- 在Terraform apply阶段,Terraform执行计划中的操作。
- HashiCorp不再以开源许可证发布Terraform,这导致创建了一个新的开源分叉项目——OpenTofu。
- OpenTofu和Terraform大多数情况下可以互换,本书将指出它们之间的差异。