arrow_back

与 Terraform 模块交互

加入 登录
Test and share your knowledge with our community!
done
Get access to over 700 hands-on labs, skill badges, and courses

与 Terraform 模块交互

Lab 1 小时 universal_currency_alt 5 个积分 show_chart 中级
Test and share your knowledge with our community!
done
Get access to over 700 hands-on labs, skill badges, and courses

本实验由 Google 与我们的合作伙伴 Hashicorp 共同开发。如果您在账号个人资料中选择接收产品动态、通知和优惠信息,那么我们可能会与实验赞助商 Hashicorp 共享您的个人信息。

GSP751

Google Cloud 自定进度实验

概览

随着您使用 Terraform 管理基础设施,所创建的配置会越来越复杂。单个 Terraform 配置文件或目录的复杂度并不存在固有限制,因此您可以在单个目录中不断编写和更新配置文件。但如果您真的这样做,那么可能会遇到以下一个或多个问题:

  • 配置文件越来越难以理解和导航。
  • 更新配置的风险会越来越高,因为更新配置中的一个块可能会给其他块造成意外的连带影响。
  • 配置中相似块的重复程度可能会提高,例如,假设您配置了单独的开发/预演/生产环境,那么在更新配置的这些部分时就会造成更高的负担。
  • 如果您想在多个项目和团队之间共享配置的某些部分,那么剪切配置块并在不同项目之间粘贴可能很容易出错,也难以维护。

在本实验中,您将了解到模块能如何解决这些问题、Terraform 模块的结构,以及使用和创建模块的最佳实践。

模块的用途是什么?

模块可以通过下面这些方式帮助解决以上问题:

  • 整理配置:模块能将您配置的相关部分整理到一起,从而让配置更易于导航、理解和更新。即便中等复杂度的基础设施也可能需要实现包含成百上千行代码的配置。利用模块,您可以将配置整理为逻辑组件。

  • 封装配置:使用模块的另一个好处是将配置封装为不同的逻辑组件。封装有助于防范一些意外后果(例如对配置中某一个部分的更改意外地造成其他基础设施变更),此外还有助于降低发生简单错误的可能性(例如为两个不同的资源使用同一个名称)。

  • 重复利用配置:如果在编写所有配置时都完全不利用现有代码,那么就会耗费很多时间,而且也容易出错。通过使用模块,您可以重复利用自己编写的配置、您团队中的其他成员编写的配置,或者由其他 Terraform 从业者发布并可供您使用的模块,从而节省时间并减少代价高昂的错误。您还可以与自己的团队或大众分享由您编写的模块,让他们可以受益于您的劳动成果。

  • 促进一致性并确保符合最佳实践:模块还有助于在您的配置中实现一致性。一致性让复杂的配置更易于理解,还有助于确保在您的所有配置中运用最佳实践。举例来说,云服务提供商提供了许多选项以用于配置对象存储服务,如 Amazon S3 (Simple Storage Service) 或 Google Cloud Storage 存储桶。许多备受关注的安全事件都涉及到未得到正确保护的对象存储,而且由于牵涉的复杂配置选项为数众多,很容易意外造成这些服务配置不当。

使用模块有助于减少此类错误。举例来说,您可以创建一个模块来描述贵组织的所有公开网站存储桶的配置方式,以及为用于记录应用日志的不公开存储桶创建另外一个模块。此外,如果需要更新某一类资源的一项配置,使用模块让您可以在一处执行更新,然后将更新应用于使用该模块的所有用例。

目标

在本实验中,您将学习如何执行以下任务:

  • 使用 Registry 中的模块
  • 构建一个模块

设置和要求

点击“开始实验”按钮前的注意事项

请阅读以下说明。实验是计时的,并且您无法暂停实验。计时器在您点击开始实验后即开始计时,显示 Google Cloud 资源可供您使用多长时间。

此实操实验可让您在真实的云环境中开展实验活动,免受模拟或演示环境的局限。我们会为您提供新的临时凭据,让您可以在实验规定的时间内用来登录和访问 Google Cloud。

为完成此实验,您需要:

  • 能够使用标准的互联网浏览器(建议使用 Chrome 浏览器)。
注意:请使用无痕模式或无痕浏览器窗口运行此实验。这可以避免您的个人账号与学生账号之间发生冲突,这种冲突可能导致您的个人账号产生额外费用。
  • 完成实验的时间 - 请注意,实验开始后无法暂停。
注意:如果您已有自己的个人 Google Cloud 账号或项目,请不要在此实验中使用,以避免您的账号产生额外的费用。

如何开始实验并登录 Google Cloud 控制台

  1. 点击开始实验按钮。如果该实验需要付费,系统会打开一个弹出式窗口供您选择付款方式。左侧是实验详细信息面板,其中包含以下各项:

    • 打开 Google 控制台按钮
    • 剩余时间
    • 进行该实验时必须使用的临时凭据
    • 帮助您逐步完成本实验所需的其他信息(如果需要)
  2. 点击打开 Google 控制台。 该实验会启动资源并打开另一个标签页,显示登录页面。

    提示:请将这些标签页安排在不同的窗口中,并将它们并排显示。

    注意:如果您看见选择帐号对话框,请点击使用其他帐号
  3. 如有必要,请从实验详细信息面板复制用户名,然后将其粘贴到登录对话框中。点击下一步

  4. 请从实验详细信息面板复制密码,然后将其粘贴到欢迎对话框中。点击下一步

    重要提示:您必须使用左侧面板中的凭据。请勿使用您的 Google Cloud Skills Boost 凭据。 注意:在本次实验中使用您自己的 Google Cloud 帐号可能会产生额外费用。
  5. 继续在后续页面中点击以完成相应操作:

    • 接受条款及条件。
    • 由于该帐号为临时帐号,请勿添加帐号恢复选项或双重验证。
    • 请勿注册免费试用。

片刻之后,系统会在此标签页中打开 Cloud 控制台。

注意:您可以点击左上角的导航菜单来查看列有 Google Cloud 产品和服务的菜单。 “导航菜单”图标

激活 Cloud Shell

Cloud Shell 是一种装有开发者工具的虚拟机。它提供了一个永久性的 5GB 主目录,并且在 Google Cloud 上运行。Cloud Shell 提供可用于访问您的 Google Cloud 资源的命令行工具。

  1. 点击 Google Cloud 控制台顶部的激活 Cloud Shell “激活 Cloud Shell”图标

如果您连接成功,即表示您已通过身份验证,且当前项目会被设为您的 PROJECT_ID 环境变量所指的项目。输出内容中有一行说明了此会话的 PROJECT_ID

Your Cloud Platform project in this session is set to YOUR_PROJECT_ID

gcloud 是 Google Cloud 的命令行工具。它已预先安装在 Cloud Shell 上,且支持 Tab 自动补全功能。

  1. (可选)您可以通过此命令列出活跃账号名称:
gcloud auth list
  1. 点击授权

  2. 现在,输出的内容应如下所示:

输出:

ACTIVE: * ACCOUNT: student-01-xxxxxxxxxxxx@qwiklabs.net To set the active account, run: $ gcloud config set account `ACCOUNT`
  1. (可选)您可以通过此命令列出项目 ID:
gcloud config list project

输出

[core] project = <project_ID>

输出示例

[core] project = qwiklabs-gcp-44776a13dea667a6 Note: For full documentation of gcloud, in Google Cloud, refer to the gcloud CLI overview guide.

Terraform 模块是什么?

Terraform 模块是单独一个目录内的一组 Terraform 配置文件。即便是仅包含一个目录(目录内包含一个或多个 .tf 文件)的简单配置,也是一个模块。如果您直接在这样一个目录中运行 Terraform 命令,则这个目录中的内容即被视为根模块。所以从这个意义上来说,每个 Terraform 配置都是一个模块的一部分。您可能有下面这样一组简单的 Terraform 配置文件:

├── LICENSE ├── README.md ├── main.tf ├── variables.tf ├── outputs.tf

在本例中,从 minimal-module 目录内运行 Terraform 命令时,该目录的内容即被视为根模块。

调用模块

Terraform 命令只会直接使用一个目录下的配置文件,该目录通常就是当前工作目录。但您的配置可以使用 module 代码块来调用其他目录中的模块。Terraform 在遇到 module 代码块时会加载并处理该模块的配置文件。

被另一项配置调用的模块有时称为该项配置的“子模块”。

本地和远程模块

可以从本地文件系统或远程来源加载模块。Terraform 支持多种远程来源,包括 Terraform Registry、大多数版本控制系统、HTTP 网址以及 Terraform Cloud 或 Terraform Enterprise 私有模块注册表。

模块最佳实践

从许多方面来说,Terraform 模块与大多数编程语言中的库、程序包或模块的概念都是相似的,而且也会提供许多同样的好处。与几乎所有重要的计算机程序一样,现实环境中的 Terraform 配置几乎总是应该使用模块,以便提供上述好处。

建议每一位 Terraform 从业者在使用模块时都遵循以下最佳实践:

  • 在开始编写配置的时候,就制定模块计划。即便是仅由一人管理的稍微复杂的 Terraform 配置,考虑到使用模块的好处,为确保正确使用模块而付出的时间也确有所值。

  • 使用本地模块来整理和封装您的代码。即便您未使用或发布远程模块,从最初起就利用模块来整理配置也有很大好处:随着基础设施复杂度的增加,这样做可以显著降低维护和更新配置的负担。

  • 使用公共 Terraform Registry 来查找有用的模块。这样就能借助他人的工作成果,快速、自信地实现您的配置。

  • 发布您的模块,并将其与您的团队共享。大多数基础设施都由一支团队管理,而模块是团队可用来创建和维护基础设施的重要工具。如前所述,您可以通过公开或非公开的形式发布模块。在本系列后续的一个实验中,您将会了解具体操作方法。

任务 1. 使用 Registry 中的模块

在本部分中,您要使用 Terraform Registry 中的模块在 Google Cloud 中预配一个示例环境。此处使用的概念适用于任意来源的任意模块。

  • 在新的浏览器标签页或窗口中打开 Terraform Network 模块的 Terraform Registry 页面。此页面应如下所示:

Terraform Registry 页面

此页面包含有关该模块的信息,以及一个指向源代码库的链接。页面的右侧包含一个下拉菜单界面,可供选择模块版本,还提供了使用该模块预配基础设施的说明。

在调用一个模块时,您必须提供 source 参数。在本例中,Terraform 将在 Terraform Registry 中搜索与给定字符串匹配的模块。您还可以使用模块来源的网址或本地文件路径。有关可用的模块来源的列表,请参阅 Terraform 文档

此处展示的另外一个参数是 version。对于受支持的来源,您可以通过 version 参数定义将要加载的模块的一个或多个版本。在本实验中,您将指定所用模块的确切版本号。您可以查看模块文档,了解指定版本的更多方法。

module 代码块的其他参数视为模块的输入变量。

创建 Terraform 配置

  1. 首先,在 Cloud Shell 中运行以下命令,以从 Google Terraform 模块 GitHub 代码库中克隆用作示例的简单项目,并切换到 v6.0.1 分支:
git clone https://github.com/terraform-google-modules/terraform-google-network cd terraform-google-network git checkout tags/v6.0.1 -b v6.0.1

这能确保您使用的是正确的版本号。

  1. 在 Cloud Shell 工具栏上,点击打开编辑器。如需在 Cloud Shell 与代码编辑器之间切换,请根据需要点击打开编辑器打开终端,或点击在新窗口中打开,以便在单独的标签页中打开编辑器。

  2. 在编辑器中,导航到 terraform-google-network/examples/simple_project 并打开 main.tf 文件。您的 main.tf 配置应如下所示:

module "test-vpc-module" { source = "terraform-google-modules/network/google" version = "~> 6.0" project_id = var.project_id # Replace this with your project ID network_name = "my-custom-mode-network" mtu = 1460 subnets = [ { subnet_name = "subnet-01" subnet_ip = "10.10.10.0/24" subnet_region = "us-west1" }, { subnet_name = "subnet-02" subnet_ip = "10.10.20.0/24" subnet_region = "us-west1" subnet_private_access = "true" subnet_flow_logs = "true" }, { subnet_name = "subnet-03" subnet_ip = "10.10.30.0/24" subnet_region = "us-west1" subnet_flow_logs = "true" subnet_flow_logs_interval = "INTERVAL_10_MIN" subnet_flow_logs_sampling = 0.7 subnet_flow_logs_metadata = "INCLUDE_ALL_METADATA" subnet_flow_logs_filter = "false" } ] }

此配置包含一个重要的代码块:

  • module "test-vpc-module" 定义了虚拟私有云 (VPC),它将为基础设施的其他部分提供网络服务。

为模块输入变量设置值

部分输入变量是“必需”的,也就是说,该模块并未提供默认值;为确保 Terraform 正确运行,必须为其提供明确的值。

  • 在模块 "test-vpc-module" 代码块中,检查您设置的输入变量。其中每个输入变量均已记录在 Terraform Registry 之中。此模块的必需输入为:

    • network_name:所创建网络的名称
    • project_id:将在其中创建此 VPC 的项目的 ID
    • subnets:所创建的子网列表

要使用大多数模块,您需要向模块配置传递输入变量。调用模块的配置负责设置其输入值,这些值会作为参数传递给 module 代码块。除了 sourceversion 之外,module 代码块的大多数参数都会设置变量值。

在 Google Cloud network 模块的 Terraform Registry 页面上,Inputs(输入)标签页描述了该模块支持的所有输入变量

定义根输入变量

输入变量与模块的配合使用方式与在任何 Terraform 配置中使用变量的方式非常相似。一种通用模式是确定您将来可能要更改什么模块输入变量,随后在您配置的 variables.tf 文件内创建匹配的变量,并设置合理的默认值,然后可将这些变量以参数的形式传递给 module 代码块。

  1. 如需检索项目 ID,请在 Cloud Shell 中运行以下命令:
gcloud config list --format 'value(core.project)'
  1. 在编辑器的同一个目录中,导航到 variables.tf

  2. 使用上一条命令的输出填写 project_id 变量。您必须遵循下方的格式,并设置该变量的 default 值:

variable "project_id" { description = "The project ID to host the network in" default = "FILL IN YOUR PROJECT ID HERE" }
  1. variables.tf 中,添加 network_name 变量。您可以使用 example-vpc 这个名称,也可以使用自己喜欢的其他任何名称。您必须遵循下方的格式,并设置该变量的 default 值:
variable "network_name" { description = "The name of the VPC network being created" default = "example-vpc" }
  1. 返回 main.tf 文件,将 network_name 参数的值更新为 var.network_name,以使用您刚才定义的变量。
module "test-vpc-module" { ... project_id = var.project_id network_name = var.network_name ...
  1. main.tf 文件中,将第 354047 行的子网区域从 us-west1 更新为 。这样就能确保在项目允许的区域中创建子网。您的模块应如下所示:
subnets = [ { subnet_name = "subnet-01" subnet_ip = "10.10.10.0/24" subnet_region = "{{{project_0.default_region | REGION}}}" }, { subnet_name = "subnet-02" subnet_ip = "10.10.20.0/24" subnet_region = "{{{project_0.default_region | REGION}}}" subnet_private_access = "true" subnet_flow_logs = "true" }, { subnet_name = "subnet-03" subnet_ip = "10.10.30.0/24" subnet_region = "{{{project_0.default_region | REGION}}}" ... .. }

定义根输出值

模块还有输出值,是使用 output 关键字在模块内部定义的。若要访问它们,您可以引用 module.<模块名称>.<输出名称>。与输入变量相似,模块输出列于 Terraform Registryoutputs(输出)标签页下。

模块输出通常会传递到您配置的其他部分,或者定义为根模块的输出。在本实验中,您会看到这两种用法。

  • 导航到您的配置所在目录中的 outputs.tf 文件。确认该文件包含以下内容:
output "network_name" { value = module.test-vpc-module.network_name description = "The name of the VPC being created" } output "network_self_link" { value = module.test-vpc-module.network_self_link description = "The URI of the VPC being created" } output "project_id" { value = module.test-vpc-module.project_id description = "VPC project id" } output "subnets_names" { value = module.test-vpc-module.subnets_names description = "The names of the subnets being created" } output "subnets_ips" { value = module.test-vpc-module.subnets_ips description = "The IP and cidrs of the subnets being created" } output "subnets_regions" { value = module.test-vpc-module.subnets_regions description = "The region where subnets will be created" } output "subnets_private_access" { value = module.test-vpc-module.subnets_private_access description = "Whether the subnets will have access to Google API's without a public IP" } output "subnets_flow_logs" { value = module.test-vpc-module.subnets_flow_logs description = "Whether the subnets will have VPC flow logs enabled" } output "subnets_secondary_ranges" { value = module.test-vpc-module.subnets_secondary_ranges description = "The secondary ranges associated with these subnets" } output "route_names" { value = module.test-vpc-module.route_names description = "The routes associated with this VPC" }

预配基础设施

  1. 在 Cloud Shell 中,导航到您的 simple_project 目录:
cd ~/terraform-google-network/examples/simple_project
  1. 初始化您的 Terraform 配置:
terraform init
  1. 创建 VPC:
terraform apply
  1. 如需应用更改并继续,请在看到提示时输入 yes 回应。

太棒了!您已经使用了自己的第一个模块。您的配置输出应如下所示:

Outputs: network_name = "example-vpc" network_self_link = "https://www.googleapis.com/compute/v1/projects/qwiklabs-gcp-01-a68489b0625b/global/networks/example-vpc" project_id = "" route_names = [] subnets_flow_logs = [ false, true, true, ] subnets_ips = [ "10.10.10.0/24", "10.10.20.0/24", "10.10.30.0/24", ] subnets_names = [ "subnet-01", "subnet-02", "subnet-03", ] .... ....

了解模块的工作原理

初次使用一个新模块时,必须运行 terraform initterraform get 来安装模块。运行这两条命令中的任何一条时,Terraform 会将任何新模块安装到 .terraform/modules 目录中(位于您的配置的工作目录下)。对于本地模块,Terraform 将创建指向该模块目录的一个符号链接。因此,对本地模块的任何更改都会立即生效,不需要您再次运行 terraform get

清理基础设施

至此,您已经看到了如何使用 Terraform Registry 中的模块、如何使用输入变量配置这些模块,以及如何从这些模块获取输出值。

  1. 销毁您创建的基础设施:
terraform destroy
  1. 在看到提示时输入 yes 回应。 Terraform 将销毁您创建的基础设施。

  2. 销毁您的基础设施后,请删除 terraform-google-network 文件夹。

cd ~ rm -rd terraform-google-network -f

点击“检查我的进度”以验证是否完成了以下目标: 预配基础设施。

任务 2. 构建一个模块

在上一项任务中,您使用 Terraform Registry 中的模块,在 Google Cloud 内创建了一个 VPC 网络。尽管正确使用现有 Terraform 模块是一项重要技能,但对于每一名 Terraform 从业者来说,学会如何创建模块也大有好处。我们建议您在创建每一项 Terraform 配置时,都假设该配置将作为模块使用,这样可以帮您设计出灵活、可重复利用并且可组合的配置。

您可能已经了解,Terraform 将每一项配置都视为一个模块。在您运行 terraform 命令时,或者使用 Terraform Cloud 或 Terraform Enterprise 远程运行 Terraform 时,包含 Terraform 配置的目标目录将被视为根模块。

在本任务中,您要创建一个模块,来管理用于托管静态网站的 Compute Storage 存储桶。

模块结构

Terraform 将 module 代码块的 source 参数中引用的任何本地目录都视为模块。新模块的典型文件结构如下:

├── LICENSE ├── README.md ├── main.tf ├── variables.tf ├── outputs.tf 注意:这些文件均非必需,在 Terraform 使用您的模块时,它们也并无任何特殊含义。您可以创建具有单个 .tf 文件的模块,也可使用您喜爱的其他任何文件结构。

以上每一个文件都有各自的用途:

  • LICENSE 包含您的模块将据以分发的许可。在您共享模块时,LICENSE 文件让该模块的用户可以了解这个模块根据什么条款提供。Terraform 本身并不会用到这个文件。
  • README.md 包含 Markdown 格式的文档,其中描述了如何使用您的模块。Terraform 不会使用这个文件,但在访问者访问您的模块的 Terraform Registry 或 GitHub 页面时,Terraform Registry 和 GitHub 这些服务会向访问者显示此文件的内容。
  • main.tf 包含您的模块的主配置集。您还可以创建其他配置文件,并以对您的项目有意义的方式整理它们。
  • variables.tf 包含您的模块的变量定义。在他人使用您的模块时,这些变量会配置为 module 代码块中的参数。由于所有 Terraform 值都必须定义,任何没有默认值的变量都将变成必需的参数。具有默认值的变量也可以作为模块参数提供,以此来替换默认值。
  • outputs.tf 包含模块的输出定义。模块输出会提供给使用该模块的配置,因此通常用于将模块所定义的基础设施部分的相关信息传递给配置的其他部分。

请留意这些文件,确保不会将其作为模块的一部分加以分发:

  • terraform.tfstateterraform.tfstate.backup 文件包含您的 Terraform 状态,指定了 Terraform 如何跟踪您的配置与其预配的基础设施之间的关系。
  • .terraform 目录包含用于预配您的基础设施的模块和插件。在预配基础设施时,这些文件特定于 Terraform 的个别实例,而非适用于 .tf 文件中定义的基础设施配置。
  • *.tfvars 文件不需要随模块分发,除非您还将其用作独立的 Terraform 配置,因为模块输入变量是通过配置中 module 代码块的参数设置的。
注意:如果您在 Git 等版本控制系统内跟踪对您的模块的更改,则需要将版本控制系统配置为忽略这些文件。如需查看实例,请参阅 GitHub 上的这个 .gitignore 文件

创建模块

导航到您的主目录,通过构建一个新的 main.tf 配置文件来创建根模块。随后创建一个名为 modules 的目录,其中包含另一个名为 gcs-static-website-bucket 的文件夹。您要使用 gcs-static-website-bucket 目录内的三个 Terraform 配置文件:website.tfvariables.tfoutputs.tf

  1. 为新模块创建目录:
cd ~ touch main.tf mkdir -p modules/gcs-static-website-bucket
  1. 导航到模块所在目录,运行以下命令来创建三个空文件:
cd modules/gcs-static-website-bucket touch website.tf variables.tf outputs.tf
  1. gcs-static-website-bucket 目录中运行以下命令,以创建一个名为 README.md 且包含如下内容的文件:
tee -a README.md <<EOF # GCS static website bucket 此模块会预配 Cloud Storage 存储桶,这些存储桶针对静态网站托管而配置。 EOF 注意:为模块选择正确的许可不在本实验的讨论范围内。本实验将使用 Apache 2.0 开源许可。
  1. 创建另一个名为 LICENSE 的文件,在其中包含以下内容:
tee -a LICENSE <<EOF 根据 Apache 许可 2.0 版(以下简称“许可”)获得授权; 您只有在遵循该许可的前提下才可使用本文件。 您可以通过以下网址获得该许可的副本: http://www.apache.org/licenses/LICENSE-2.0 除非适用法律要求或已达成书面协议,否则按照该许可分发的软件均“按原样”分发,不提供任何类型的保证或条件(无论明示或暗示)。 有关本许可下特定的语言管辖权限和限制,请参阅本许可。 EOF 注意: Terraform 不需要也不会使用上述任何文件。对于可能与他人共享的模块,使用这些文件是最佳实践。

您当前的模块目录结构应如下所示:

main.tf modules/ └── gcs-static-website-bucket ├── LICENSE ├── README.md ├── website.tf ├── outputs.tf └── variables.tf
  1. 将此 Cloud Storage 存储桶资源添加到 modules/gcs-static-website-bucket 目录下的 website.tf 文件中。
resource "google_storage_bucket" "bucket" { name = var.name project = var.project_id location = var.location storage_class = var.storage_class labels = var.labels force_destroy = var.force_destroy uniform_bucket_level_access = true versioning { enabled = var.versioning } dynamic "retention_policy" { for_each = var.retention_policy == null ? [] : [var.retention_policy] content { is_locked = var.retention_policy.is_locked retention_period = var.retention_policy.retention_period } } dynamic "encryption" { for_each = var.encryption == null ? [] : [var.encryption] content { default_kms_key_name = var.encryption.default_kms_key_name } } dynamic "lifecycle_rule" { for_each = var.lifecycle_rules content { action { type = lifecycle_rule.value.action.type storage_class = lookup(lifecycle_rule.value.action, "storage_class", null) } condition { age = lookup(lifecycle_rule.value.condition, "age", null) created_before = lookup(lifecycle_rule.value.condition, "created_before", null) with_state = lookup(lifecycle_rule.value.condition, "with_state", null) matches_storage_class = lookup(lifecycle_rule.value.condition, "matches_storage_class", null) num_newer_versions = lookup(lifecycle_rule.value.condition, "num_newer_versions", null) } } } }

提供方文档请参见 GitHub

  1. 导航到您的模块中的 variables.tf 文件,并添加如下代码:
variable "name" { description = "The name of the bucket." type = string } variable "project_id" { description = "The ID of the project to create the bucket in." type = string } variable "location" { description = "The location of the bucket." type = string } variable "storage_class" { description = "The Storage Class of the new bucket." type = string default = null } variable "labels" { description = "A set of key/value label pairs to assign to the bucket." type = map(string) default = null } variable "bucket_policy_only" { description = "Enables Bucket Policy Only access to a bucket." type = bool default = true } variable "versioning" { description = "While set to true, versioning is fully enabled for this bucket." type = bool default = true } variable "force_destroy" { description = "When deleting a bucket, this boolean option will delete all contained objects. If false, Terraform will fail to delete buckets which contain objects." type = bool default = true } variable "iam_members" { description = "The list of IAM members to grant permissions on the bucket." type = list(object({ role = string member = string })) default = [] } variable "retention_policy" { description = "Configuration of the bucket's data retention policy for how long objects in the bucket should be retained." type = object({ is_locked = bool retention_period = number }) default = null } variable "encryption" { description = "A Cloud KMS key that will be used to encrypt objects inserted into this bucket" type = object({ default_kms_key_name = string }) default = null } variable "lifecycle_rules" { description = "The bucket's Lifecycle Rules configuration." type = list(object({ # Object with keys: # - type - The type of the action of this Lifecycle Rule. Supported values: Delete and SetStorageClass. # - storage_class - (Required if action type is SetStorageClass) The target Storage Class of objects affected by this Lifecycle Rule. action = any # Object with keys: # - age - (Optional) Minimum age of an object in days to satisfy this condition. # - created_before - (Optional) Creation date of an object in RFC 3339 (e.g. 2017-06-13) to satisfy this condition. # - with_state - (Optional) Match to live and/or archived objects. Supported values include: "LIVE", "ARCHIVED", "ANY". # - matches_storage_class - (Optional) Storage Class of objects to satisfy this condition. Supported values include: MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, STANDARD, DURABLE_REDUCED_AVAILABILITY. # - num_newer_versions - (Optional) Relevant only for versioned objects. The number of newer versions of an object to satisfy this condition. condition = any })) default = [] }
  1. 在您的模块内的 outputs.tf 文件中添加模块的输出:
output "bucket" { description = "The created storage bucket" value = google_storage_bucket.bucket }

与变量相似,模块中的输出所执行的功能与根模块中相同,只是访问方式有所不同。模块的输出可作为 module 对象(该对象可在调用模块的配置内使用)的只读特性访问。

  1. 返回您的根目录下的 main.tf,并添加对新模块的引用:
module "gcs-static-website-bucket" { source = "./modules/gcs-static-website-bucket" name = var.name project_id = var.project_id location = "{{{project_0.default_region | REGION}}}" lifecycle_rules = [{ action = { type = "Delete" } condition = { age = 365 with_state = "ANY" } }] }
  1. 在您的根目录中,为根模块创建一个 outputs.tf 文件:
cd ~ touch outputs.tf
  1. outputs.tf 文件中添加如下代码:
output "bucket-name" { description = "Bucket names." value = "module.gcs-static-website-bucket.bucket" }
  1. 在您的根目录中,创建一个 variables.tf 文件:
touch variables.tf
  1. 将以下代码添加到 variables.tf 文件中。将 project_idname 变量预设为您的项目 ID:
variable "project_id" { description = "The ID of the project in which to provision resources." type = string default = "FILL IN YOUR PROJECT ID HERE" } variable "name" { description = "Name of the buckets to create." type = string default = "FILL IN A (UNIQUE) BUCKET NAME HERE" } 注意:存储桶必须使用全局唯一的名称。通常,根据您的用户名和日期创建唯一的存储桶名称是较好的做法。您也可以使用项目 ID。

安装本地模块

每当您向配置添加新模块时,Terraform 都必须先安装该模块,然后才能使用它。terraform getterraform init 命令均可安装和更新模块。terraform init 命令还会初始化后端并安装插件。

  1. 安装模块:
terraform init
  1. 预配存储桶:
terraform apply
  1. 在看到提示时输入 yes 回应。系统将会预配您的存储桶和其他资源。

将文件上传到存储桶

至此,您已经配置了自己的模块,并使用它创建了一个静态网站。您可能想访问此静态网站。目前,您的存储桶内还没有任何内容,因此在这个网站上也看不到任何内容。如果想看到内容,您需要向存储桶上传对象。您可以上传 GitHub 代码库中 www 目录的内容。

  1. 将示例内容下载到您的主目录:
cd ~ curl https://raw.githubusercontent.com/hashicorp/learn-terraform-modules/master/modules/aws-s3-static-website-bucket/www/index.html > index.html curl https://raw.githubusercontent.com/hashicorp/learn-terraform-modules/blob/master/modules/aws-s3-static-website-bucket/www/error.html > error.html
  1. 将文件复制到存储桶中,将 YOUR-BUCKET-NAME 替换为您的存储桶的名称:
gsutil cp *.html gs://YOUR-BUCKET-NAME
  1. 在浏览器的新标签页内,访问网站 https://storage.cloud.google.com/YOUR-BUCKET-NAME/index.html,并将 YOUR-BUCKET-NAME 替换为您的存储桶的名称。

您应该会看到一个基本 HTML 网页,其中显示此处没有可显示的内容

点击“检查我的进度”以验证是否完成了以下目标: 构建一个模块。

清理网站和基础设施

最后,您需要销毁刚刚创建的基础设施,以清理项目。

  1. 销毁您的 Terraform 资源:
terraform destroy

看到提示时,输入 yes 作为回应,随后 Terraform 就会销毁您按照本演示的步骤创建的所有资源。

恭喜!

通过本实验,您学习了 Terraform 模块的基础,以及如何使用 Registry 中的已有模块。随后,您构建了自己的模块,用于创建一个托管在 Cloud Storage 存储桶上的静态网站。在此过程中,您定义了配置文件的输入、输出和变量,并学习了构建模块的最佳实践。

后续步骤/了解详情

以下链接提供了更多关于 Terraform 的实操练习:

Google Cloud 培训和认证

…可帮助您充分利用 Google Cloud 技术。我们的课程会讲解各项技能与最佳实践,可帮助您迅速上手使用并继续学习更深入的知识。我们提供从基础到高级的全方位培训,并有点播、直播和虚拟三种方式选择,让您可以按照自己的日程安排学习时间。各项认证可以帮助您核实并证明您在 Google Cloud 技术方面的技能与专业知识。

上次更新手册的时间:2024 年 1 月 26 日

上次测试实验的时间:2023 年 12 月 11 日

版权所有 2024 Google LLC 保留所有权利。Google 和 Google 徽标是 Google LLC 的商标。其他所有公司名和产品名可能是其各自相关公司的商标。