
Before you begin
- Labs create a Google Cloud project and resources for a fixed time
- Labs have a time limit and no pause feature. If you end the lab, you'll have to restart from the beginning.
- On the top left of your screen, click Start lab to begin
Working with Backends
/ 100
本实验由 Google 与我们的合作伙伴 Hashicorp 共同开发。如果您在账号个人资料中选择接收产品动态、通知和优惠信息,那么我们可能会与实验赞助商 Hashicorp 共享您的个人信息。
Terraform 必须存储有关您托管的基础设施和配置的状态。Terraform 利用此状态将真实的资源映射到您的配置、跟踪元数据以及提高大型基础设施的性能。
此状态默认存储在名为 terraform.tfstate
的本地文件中,但也可以远程存储。远程存储更适合团队环境。
Terraform 使用此本地状态来创建方案并对基础设施进行更改。在执行任何操作之前,Terraform 都会先刷新,以根据真实基础设施来更新状态。
Terraform 状态的主要作用是存储远程系统中的对象与您的配置中声明的资源实例之间的绑定关系。当 Terraform 因配置变更而相应地创建远程对象时,会记录该远程对象相对于特定资源实例的身份信息,以后当配置再次发生变更时,可能会相应地更新或删除该对象。
在本实验中,您将学习如何执行以下任务:
请阅读以下说明。实验是计时的,并且您无法暂停实验。计时器在您点击开始实验后即开始计时,显示 Google Cloud 资源可供您使用多长时间。
此实操实验可让您在真实的云环境中开展实验活动,免受模拟或演示环境的局限。我们会为您提供新的临时凭据,让您可以在实验规定的时间内用来登录和访问 Google Cloud。
为完成此实验,您需要:
点击开始实验按钮。如果该实验需要付费,系统会打开一个弹出式窗口供您选择付款方式。左侧是实验详细信息面板,其中包含以下各项:
点击打开 Google Cloud 控制台(如果您使用的是 Chrome 浏览器,请右键点击并选择在无痕式窗口中打开链接)。
该实验会启动资源并打开另一个标签页,显示登录页面。
提示:请将这些标签页安排在不同的窗口中,并将它们并排显示。
如有必要,请复制下方的用户名,然后将其粘贴到登录对话框中。
您也可以在实验详细信息面板中找到用户名。
点击下一步。
复制下面的密码,然后将其粘贴到欢迎对话框中。
您也可以在实验详细信息面板中找到密码。
点击下一步。
继续在后续页面中点击以完成相应操作:
片刻之后,系统会在此标签页中打开 Google Cloud 控制台。
Cloud Shell 是一种装有开发者工具的虚拟机。它提供了一个永久性的 5GB 主目录,并且在 Google Cloud 上运行。Cloud Shell 提供可用于访问您的 Google Cloud 资源的命令行工具。
如果您连接成功,即表示您已通过身份验证,且当前项目会被设为您的 PROJECT_ID 环境变量所指的项目。输出内容中有一行说明了此会话的 PROJECT_ID:
gcloud
是 Google Cloud 的命令行工具。它已预先安装在 Cloud Shell 上,且支持 Tab 自动补全功能。
点击授权。
现在,输出的内容应如下所示:
输出:
输出:
输出示例:
gcloud
, in Google Cloud, refer to the gcloud CLI overview guide.
状态是 Terraform 运行的必要条件。人们有时会问 Terraform 是否可以在没有状态的情况下工作,或者干脆不使用状态,只在每次运行时检查云资源。即便 Terraform 能够在没有状态的情况下工作,这样做也只不过是将大部分复杂性从一个地方(状态)转移到另一个地方(类似的替换概念)。本部分将解释为什么说 Terraform 状态是必要的。
Terraform 需要某种数据库来将 Terraform 配置映射到真实世界。如果您的配置包含 resource resource "google_compute_instance" "foo"
,Terraform 通过此映射了解到实例 i-abcd1234
是以该资源表示的。
Terraform 希望每个远程对象只绑定到一个资源实例,这通常是能够保证的,因为 Terraform 负责在状态中创建对象并记录它们的身份信息。如果您改为导入在 Terraform 之外创建的对象,则必须验证每个不同的对象是否仅导入至一个资源实例。
如果一个远程对象绑定到两个或更多个资源实例,Terraform 可能会对这些对象执行非预期的操作,因为从配置到远程对象状态的映射变得不明确。
除了跟踪资源与远程对象之间的映射之外,Terraform 还必须跟踪元数据,例如资源依赖关系。
Terraform 通常使用配置来确定依赖顺序。但是,当您从 Terraform 配置中移除某项资源时,Terraform 必须知道如何删除该资源。Terraform 可能发现某个映射针对的是您配置文件中未定义的资源,并会计划销毁该资源。但是,由于该资源不再存在,因此仅根据配置无法确定顺序。
为确保正确操作,Terraform 会在状态内保留最新的一组依赖项的副本。现在,当您从配置中删除一项或多项内容时,Terraform 仍然可以基于状态确定正确的销毁顺序。
如果 Terraform 知道资源类型之间所需的顺序,则可以避免这种情况。例如,可以让 Terraform 知道必须先删除服务器,然后才能删除服务器所属的子网。然而,这种方法的复杂性很快会变得难以管理:Terraform 除了了解每个云的每个资源的排序语义外,还必须了解各提供程序 (Provider) 的排序。
Terraform 还会出于类似原因存储其他元数据,例如,在存在多个别名提供程序的情况下,Terraform 还会存储指向资源最近使用的提供程序配置的指针。
除了基本映射之外,Terraform 还会在状态中存储所有资源的特性值缓存。这是 Terraform 状态的可选功能,仅作性能改进之用。
运行 terraform plan
时,Terraform 必须知道资源的当前状态,才能有效地确定实现您想要的配置所需的更改。
对于小型基础设施,Terraform 可以查询您的提供程序并同步您所有资源的最新特性。这是 Terraform 的默认行为:每次执行 plan
和 apply
时,Terraform 将同步您状态中的所有资源。
对于较大的基础设施,查询每个资源就会太慢。许多云服务提供商不提供同时查询多个资源的 API,并且每个资源的查询往返时间为数百毫秒。此外,云服务提供商几乎总是设置 API 速率限制,因此 Terraform 在一段时间内只能请求有限数量的资源。为解决此问题,Terraform 的大型用户经常同时使用 -refresh=false
标志和 -target
标志。在这些场景中,缓存的状态被视为真实的记录。
在默认配置下,Terraform 将状态存储在当前工作目录(即运行 Terraform 的目录)下的某个文件中。这在刚开始时有效,但是当在团队中使用 Terraform 时,每个人都必须使用相同的状态,这样操作才能应用于相同的远程对象。
建议使用远程状态来解决该问题。借助功能完备的状态后端,Terraform 可以使用远程锁定这种措施来避免多个不同的用户意外同时运行 Terraform。这样可确保 Terraform 每次都以最新的状态开始运行。
只要您的后端支持,Terraform 就会锁定您的状态,防止被任何可以写入状态的操作更改。这可以防止其他人获得状态锁,杜绝其破坏您的状态的可能性。
所有可以写入状态的操作都会自动触发状态锁定。您不会看到有任何消息指出正在发生状态锁定。如果状态锁定失败,Terraform 将不会继续操作。您可以使用 -lock
标志对大多数命令停用状态锁定,但不建议这样做。
如果获取锁的时间比预期的要长,Terraform 将输出一条进度消息。如果 Terraform 没有输出消息,则说明状态锁定仍在进行。
并非所有后端都支持锁定。请查看后端类型列表,详细了解某个后端是否支持锁定。
每个 Terraform 配置都有一个关联的后端,该后端定义操作的执行方式以及 Terraform 状态等永久性数据的存储位置。
存储在后端的永久性数据属于一个“工作区”。最初,后端只有一个工作区,称为“default”,因此只有一个 Terraform 状态与该配置相关联。
某些后端支持多个命名工作区,即允许一个配置与多个状态相关联。该配置仍然只有一个后端,但您可以部署该配置的多个不同实例,而无需配置新后端或更改身份验证凭据。
Terraform 中的后端决定如何加载状态以及如何执行 apply
等操作。这种抽象化实现了非本地文件状态存储、远程执行等。
默认情况下,Terraform 使用“本地”后端,这是您已习惯的 Terraform 的常规行为。在之前的实验中调用的就是这种后端。
下面列举了后端的一些好处:
terraform apply
可能需要很长时间。一些后端支持远程操作,即允许操作远程执行。这样,哪怕您关闭自己的计算机,操作仍将完成。此功能结合远程状态存储和锁定(如上所述),也可在团队环境中提供帮助。后端是完全可选的:您无需学习或使用后端也可成功使用 Terraform。不过,后端确实在一定程度上解决了困扰团队的痛点。如果您是以个人身份工作,可能无需使用后端也不会遇到问题。
即使您只打算使用“本地”后端,了解后端也很有用,因为您还可以更改本地后端的行为。
在本部分中,您将配置一个本地后端。
首次配置后端(从不定义后端转为明确配置后端)时,Terraform 会提供一个将状态迁移至新后端的选项。这样,您就可以在不丢失任何现有状态的情况下应用后端。
谨慎起见,我们始终建议您另外手动备份您的状态,只需将 terraform.tfstate
文件复制到另一个位置即可。初始化过程也应该会创建一个备份,但确保万无一失总没有坏处!
首次配置后端与以后更改配置没有什么不同:创建新配置并运行 terraform init
。Terraform 会指导您完成剩下的工作。
main.tf
配置文件:main.tf
配置文件中,并将 project
和 name
变量定义替换为您的项目 ID:请参阅 Terraform 文档,详细了解 Cloud Storage 资源。
main.tf
文件中添加一个本地后端:这会引用 terraform/state
目录中的 terraform.tfstate
文件。如需指定其他文件路径,请更改 path
变量。
本地后端会将状态存储在本地文件系统上,使用系统 API 锁定该状态,并在本地执行操作。
Terraform 在使用任何已配置的后端之前必须先对其进行初始化。为此,您需要运行 terraform init
。团队中的任何成员针对任何 Terraform 配置执行的第一个步骤都应是运行 terraform init
命令。该命令可以执行多次而不会出现任何问题,它会执行 Terraform 环境所需的全部设置操作,包括初始化后端。
在以下情况下,必须调用 init
命令:
您不需要记住这些确切情况。Terraform 会检测出何时需要初始化,并适时地显示错误消息。Terraform 不会自动初始化,因为它可能需要用户提供其他信息或执行状态迁移等。
Cloud Shell 编辑器现在应该会在 terraform/state
目录中显示名为 terraform.tfstate
的状态文件。
您的 google_storage_bucket.test-bucket-for-state
资源应该会显示。
Cloud Storage 后端将状态作为对象存储在 Cloud Storage 上指定存储桶的可配置前缀中。此后端也支持状态锁定。这会锁定您的状态,防止被所有可以写入状态的操作更改。这可以防止其他人获得状态锁,杜绝其破坏您的状态的可能性。
所有可以写入状态的操作都会自动触发状态锁定。您不会看到有任何消息指出正在发生状态锁定。如果状态锁定失败,Terraform 将不会继续操作。您可以使用 -lock
标志对大多数命令停用状态锁定,但不建议这样做。
重新定位到编辑器中的 main.tf
文件。现在您需要将当前本地后端替换为 gcs
后端。
如需更改现有的本地后端配置,请将以下配置复制到您的文件中,并替换 local
后端:
bucket
的变量定义。如果您未更改该配置,系统将使用 google_storage_bucket
资源中的 name
。此存储桶将用于托管状态文件。
出现提示时输入 yes 进行确认。
在 Cloud 控制台的导航菜单中,点击 Cloud Storage > 存储桶。
点击存储桶并定位至文件 terraform/state/default.tfstate
。您的状态文件现在已保存在 Cloud Storage 存储桶中。
terraform refresh
命令用于使 Terraform(通过它的状态文件)所了解到的状态与真实的基础设施保持一致。这可用于检测与上次已知状态的偏差并更新状态文件。
此命令不会修改基础设施,但会修改状态文件。如果状态有变化,此命令可能会导致在下次执行 plan 或 apply 命令期间发生变化。
在 Cloud 控制台中返回到您的存储桶。选中名称旁边的复选框。
点击标签标签页。
点击添加标签。设置键 1 = key
,值 1 = value
。
点击保存。
返回 Cloud Shell 并使用以下命令更新状态文件:
"key" = "value"
键值对应显示在配置的 labels 属性中。
点击“检查我的进度”以验证是否完成了以下目标:
在继续下一个任务之前,需销毁您预配的基础设施。
local
,以便您可以删除对应存储桶。使用以下命令复制并替换 gcs
配置:local
后端:出现提示时输入 yes 进行确认。
main.tf
文件中,向 google_storage_bucket
资源中添加 force_destroy = true
参数。删除存储桶时,此布尔选项会删除包含的所有对象。如果您尝试删除包含对象的存储桶,Terraform 将使该运行失败。您的资源配置应如下所示:出现提示时输入 yes
进行确认。
出现提示时输入 yes 进行确认。
在本部分中,您会将现有的 Docker 容器和映像导入一个空的 Terraform 工作区,以此来了解将真实基础设施导入 Terraform 的策略和注意事项。
默认的 Terraform 工作流涉及完全使用 Terraform 创建和管理基础设施。
编写一项 Terraform 配置,定义您要创建的基础设施。
检查该 Terraform 方案,确保配置将产生预期的状态和基础设施。
应用配置,以创建您的 Terraform 状态和基础设施。
使用 Terraform 创建基础设施后,您可以更新配置并对这些更改运行 plan 和 apply 命令。最终,当不再需要该基础设施时,您将使用 Terraform 将其销毁。此工作流假定 Terraform 将创建一个全新的基础设施。
但是,您可能需要管理不是由 Terraform 创建的基础设施。Terraform 导入功能可以将支持的资源加载到 Terraform 工作区的状态中,因而可解决此问题。
不过,导入命令不会自动生成配置来管理该基础设施。因此,将现有基础设施导入 Terraform 的过程需要执行多个步骤。
将现有基础设施置于 Terraform 的控制之下涉及五个主要步骤:
在本部分,首先您将使用 Docker CLI 创建一个 Docker 容器。接下来,您需要将它导入到一个新的 Terraform 工作区。然后,您需要使用 Terraform 更新容器的配置,最终,在完成这些操作后将其销毁。
terraform.tfstate
文件和 .terraform
目录,并将它们妥善存储。
hashicorp-learn
的容器,并通过端口 80 (HTTP) 在 Cloud Shell 虚拟机上预览该容器:Cloud Shell 会在新的浏览器窗口中打开其代理服务的预览网址,并显示 NGINX 默认索引页面。现在您有一个 Docker 映像和容器可以导入到您的工作区并使用 Terraform 进行管理。
此目录包含两个 Terraform 配置文件,它们构成您将在本指南中使用的配置:
main.tf
文件配置了 Docker 提供程序。docker.tf
文件将包含管理您在上一步中创建的 Docker 容器所需的配置。terraform init -upgrade
在 Cloud Shell 编辑器中,定位到 learn-terraform-import/main.tf
。
找到 provider: docker
资源,将 host
参数注释掉或删除:
接下来,定位到 learn-terraform-import/docker.tf
。
在 docker.tf
文件中注释掉的代码下方,定义一个空的 docker_container
资源,它表示 Terraform 资源 ID 为 docker_container.web
的一个 Docker 容器:
terraform import
命令,将现有 Docker 容器附加到刚刚创建的 docker_container.web
资源。Terraform 导入操作需要用到此 Terraform 资源 ID 和完整的 Docker 容器 ID。命令 docker inspect -f {{.ID}} hashicorp-learn
会返回完整的 SHA256 容器 ID:terraform import
接受的 ID 因资源类型而异,可导入到 Terraform 的资源的提供程序文档中会加以说明。对于本示例,请查阅 Docker 提供程序文档
此状态包含 Terraform 所知晓的有关您刚刚导入的 Docker 容器的一切信息。但是,Terraform 导入操作不会为资源创建配置。
您需要先创建 Terraform 配置,然后才能使用 Terraform 管理此容器。
image
和 name
时,Terraform 会显示错误。Terraform 无法为缺少必需参数的资源生成方案。
您可以使用两种方法来更新 docker.tf
中的配置,使其与您导入的状态相匹配。您可以按原样将资源的整个当前状态导入到您的配置,也可以逐一选择需要的属性来导入到您的配置。这两种方法分别适用于不同的情形。
使用当前状态通常更快,但可能会导致配置过于冗长,因为无论配置中是否需要,每个属性都会包含在状态中。
逐一选择需要的属性会使配置更易于管理,但前提是您了解需要在配置中设置哪些属性。
鉴于本实验的目的,您将使用资源的当前状态。
docker.tf
文件中:>
符号会将 docker.tf 的所有内容替换为 terraform show
命令的输出。虽然这适用于本示例,但要将资源导入到已在管理资源的配置,需要修改 terraform show
的输出,移除您不想完全替换其配置的现有资源,并将新资源合并到现有配置中。
检查 docker.tf
文件,查看其内容是否已替换为您刚刚运行的 terraform show 命令的输出。
运行以下代码:
Terraform 会显示关于已弃用参数 (links) 的警告和错误,以及几个只读参数(ip_address
、network_data
、gateway
、ip_prefix_length
、id
)。
这些只读参数是 Docker 容器的值,Terraform 将它们存储在其状态中,但无法通过配置来设置这些值,因为它们在 Docker 内部管理。Terraform 可以使用配置来设置 links 参数,但仍会显示警告,因为它已弃用,并且可能不受 Docker 提供程序的未来版本支持。
由于此处所示的方法会加载 Terraform 状态中表示的所有属性,因此您的配置包括可选属性,它们的值与其默认值相同。哪些属性是可选的以及属性的默认值因提供程序而异,提供程序文档中会列明这些信息。
image
、name
、ports
移除这些可选属性后,您的配置应如下所示:导入真实基础设施时,请查阅提供程序文档,了解每个参数的作用。这有助于您确定如何处理方案步骤中的任何错误或警告。例如,links
参数的相关信息包含在 Docker 提供程序文档中。
方案现在应该能成功执行。请注意,该方案指出 Terraform 将更新容器,以添加 attach
、logs
、must_run
、start
属性。
Terraform 使用这些属性来创建 Docker 容器,但 Docker 并不存储这些属性。因此,terraform import
没有将它们的值加载到状态中。当您对配置运行 plan 和 apply 命令时,Docker 提供程序会为这些属性分配默认值并将它们保存在状态中,但它们不会影响正在运行的容器。
现在您的配置文件、Terraform 状态和容器全部都已同步,接下来您可以像在通常情况下一样,使用 Terraform 来管理 Terraform 容器。
在某些情况下,您无需使用 terraform import
命令,也能将资源置于 Terraform 的控制之下。使用单个唯一 ID 或标记定义的资源(例如 Docker 映像)通常就属于这种情况。
在 docker.tf
文件中,docker_container.web
资源指定用于创建容器的映像的 SHA256 哈希 ID。这是 docker 在内部存储映像 ID 的方式,因此 terraform import
将映像 ID 直接加载到了您的状态中。但是,映像 ID 不像映像标记或名称那样易于理解,并且可能与您的意图不符。例如,您可能希望使用最新版本的“nginx”映像。
<IMAGE-ID>
替换为 docker.tf
中的映像 ID:docker.tf
文件中添加以下配置,将此映像表示为一个资源:docker_container.web
资源中的映像值,否则 Terraform 会销毁并重新创建您的容器。由于 Terraform 尚未将 docker_image.nginx
资源加载到状态中,因此它没有可与硬编码的映像 ID 进行比较的映像 ID,这会导致 Terraform 认为必须替换该容器。为避免这种情况,请先创建映像,然后再更新容器以使用它,如本实验所示。
现在,Terraform 已经为映像创建了一个资源,接下来您可以在容器的配置中引用它了。
docker_container.web
的映像值以引用新的映像资源:由于 docker_image.nginx.latest
将与您替换的硬编码映像 ID 相匹配,因此此时运行 terraform apply
不会显示任何变化。
现在 Terraform 已接管 Docker 容器,接下来使用 Terraform 更改配置。
docker.tf
文件中,将容器的外部端口从 8080
改为 8081
:出现提示时输入 yes
进行确认。
这会导致 Terraform 销毁容器并使用新的端口配置重新创建容器。
请注意,容器 ID 已更改。因为更改端口配置需要销毁并重新创建容器,所以这是一个全新的容器。
现在,您已将 Docker 容器和用于创建它的映像导入到了 Terraform。
出现提示时输入 yes
进行确认。
将资源导入 Terraform 时需要注意几个重要事项。
Terraform 导入功能只能知晓 Terraform 提供程序所报告的当前基础设施状态,而不会知晓以下信息:
导入过程涉及容易出错的手动步骤,特别是如果导入资源的人不了解最初创建这些资源的方式和原因的背景信息,出错的几率会更高。
导入过程会操作 Terraform 状态文件,因此您可能需要在导入新的基础设施之前创建备份。
Terraform 导入操作不会检测或生成基础设施之间的关系。
Terraform 不会检测不需要在配置中设置的默认属性。
并非所有提供程序和资源都支持 Terraform 导入。
将基础设施导入 Terraform 并不意味着可以销毁并重新创建 Terraform。例如,导入的基础设施可能依赖于其他非托管基础设施或配置。
遵循基础设施即代码 (IaC) 最佳实践(如不可变基础设施)有助于防止许多此类问题,但手动创建的基础设施不太可能遵循 IaC 最佳实践。
Terraformer 等工具可以自动执行与导入基础设施相关的一些手动步骤。但是,这些工具不是 Terraform 本身的一部分,也不受 HashiCorp 的背书或支持。
在本实验中,您学习了如何使用 Terraform 管理后端和状态。您创建了本地和 Cloud Storage 后端来管理状态文件,刷新了状态,并将配置导入到了 Terraform。然后,您更新了配置,并手动进行修改以使用 Terraform 完全管理 Docker 容器。
请务必查看以下资源,以获得更多关于 Terraform 的实操练习机会:
…可帮助您充分利用 Google Cloud 技术。我们的课程会讲解各项技能与最佳实践,可帮助您迅速上手使用并继续学习更深入的知识。我们提供从基础到高级的全方位培训,并有点播、直播和虚拟三种方式选择,让您可以按照自己的日程安排学习时间。各项认证可以帮助您核实并证明您在 Google Cloud 技术方面的技能与专业知识。
上次更新手册的时间:2024 年 1 月 26 日
上次测试实验的时间:2023 年 12 月 11 日
版权所有 2025 Google LLC 保留所有权利。Google 和 Google 徽标是 Google LLC 的商标。其他所有公司名和产品名可能是其各自相关公司的商标。