Terraform 模块实现

LinuxBeginner
立即练习

介绍

Terraform 模块是基础设施即代码(IaC)中的一个核心概念,它允许你创建可重用、可配置和有组织的组件。与其在多个地方编写相同的代码块,不如将其封装到一个模块中,并在需要时调用它。这种做法能显著提高代码的可维护性和跨项目的统一性。

在这个 Lab 中,你将学习实现 Terraform 模块的基础知识。你将从创建一个标准的模块目录结构开始。然后,你将定义一个创建本地文件的简单模块。你将使用变量使该模块可配置,并通过输出(outputs)暴露其结果。最后,你将从根 Terraform 配置中调用此模块来部署资源。

完成本 Lab 后,你将对如何构建和使用自己的 Terraform 模块有一个实际的理解。

这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 初级 级别的实验,完成率为 95%。获得了学习者 100% 的好评率。

在项目根目录创建 modules 目录

在这一步,你将为本地 Terraform 模块创建标准的目录结构。一个常见的约定是将所有本地模块放置在项目根目录下的 modules 目录中。modules 内部的每个子目录代表一个独立、自包含的模块。

首先,让我们为模块创建一个目录,并为我们将命名为 localfile_creator 的模块创建一个特定的子目录。所有操作将在 ~/project 目录中执行。

执行以下命令来创建嵌套的目录结构:

mkdir -p modules/localfile_creator

-p 标志确保如果父目录 modules 不存在,mkdir 也会创建它。

现在,你可以使用 tree 命令来验证目录是否已正确创建。如果 tree 未安装,你可以使用 ls -R

tree

你应该会看到以下输出,确认你的新目录结构:

.
└── modules
    └── localfile_creator

2 directories, 0 files

这种结构清晰地将你的可重用模块代码与将使用它的根配置分离开来。

在 local_file 模块中创建 main.tf

在这一步,你将为你新创建的模块创建主要的配置文件。每个模块都是一个自包含的 Terraform 项目,因此它有自己的一组 .tf 文件。main.tf 文件通常是定义模块核心资源的地方。

我们将定义一个 local_file 资源,它是 hashicorp/local provider 的一部分。这个资源非常适合学习,因为它只管理本地文件系统上的文件,而不需要任何云提供商凭证。

首先,使用 nano 编辑器在你模块的目录内创建 main.tf 文件:

nano modules/localfile_creator/main.tf

现在,将以下 HCL (HashiCorp Configuration Language) 代码添加到文件中。这段代码定义了一个类型为 local_file、本地名称为 example 的资源。

resource "local_file" "example" {
  content  = "This is a file created by a Terraform module."
  filename = "${path.module}/module_output.txt"
}

我们来解析一下这段代码:

  • resource "local_file" "example": 这声明了一个类型为 local_file 的资源,并给它本地名称 example
  • content: 此参数设置将写入文件中的内容。
  • filename: 此参数指定要创建的文件的路径和名称。我们使用了内置的 path.module 变量,它总是指向表达式所在模块的文件系统路径。这使得文件路径相对于模块本身。

保存文件,然后按 Ctrl+X,接着按 Y,最后按 Enter 退出 nano

定义模块变量和输出 (Variables and Outputs)

在这一步,你将使你的模块可配置,并能够返回信息。硬编码像文件名和内容这样的值会限制模块的可重用性。我们将使用输入变量(input variables)来传入自定义值,并使用输出值(output values)来暴露模块创建的资源信息。

按照惯例,变量定义在 variables.tf 文件中,输出定义在 outputs.tf 文件中。

首先,为你的模块创建 variables.tf 文件:

nano modules/localfile_creator/variables.tf

添加以下代码来定义两个输入变量:file_contentfile_name

variable "file_content" {
  description = "The content of the file."
  type        = string
  default     = "Default content."
}

variable "file_name" {
  description = "The name of the file to create."
  type        = string
}

接下来,创建 outputs.tf 文件来声明模块将返回哪些信息:

nano modules/localfile_creator/outputs.tf

添加以下代码来输出所创建文件的完整路径:

output "filename" {
  description = "The full path to the created file."
  value       = local_file.example.filename
}

最后,你需要更新模块的 main.tf 文件,以便使用这些新变量而不是硬编码的值。再次打开该文件:

nano modules/localfile_creator/main.tf

修改文件使其如下所示。我们将硬编码的字符串替换为 var.file_contentvar.file_name

resource "local_file" "example" {
  content  = var.file_content
  filename = "${path.module}/${var.file_name}"
}

保存并退出编辑器。你的模块现在具有灵活性,可以与不同的输入一起使用了。

在根目录 main.tf 文件中调用模块

在这一步,你将创建一个调用你刚刚构建的模块的根配置文件。根 main.tf 是 Terraform 执行的入口点。在这里,你通过调用一个或多个模块来组合你的基础设施。

你现在将在项目目录 ~/project 的根目录下工作。在这里创建一个 main.tf 文件:

nano main.tf

将以下代码添加到此文件中。此配置将使用 localfile_creator 模块。

terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "2.4.0"
    }
  }
}

module "file_creator_instance" {
  source = "./modules/localfile_creator"

  file_content = "Hello from the root module!"
  file_name    = "my_test_file.txt"
}

output "created_file_path" {
  description = "Path of the file created by the module."
  value       = module.file_creator_instance.filename
}

我们来分析一下这个根配置:

  • terraform { ... }: 此块定义了 provider 需求。由于我们的模块使用了 local provider,调用它的根模块也必须声明它。
  • module "file_creator_instance" { ... }: 这是模块块。file_creator_instance 是该模块特定实例的本地名称。
  • source = "./modules/localfile_creator": 这告诉 Terraform 在哪里可以找到模块的源代码。在这种情况下,它是一个本地路径。
  • file_content = "..."file_name = "...": 在这里,你正在向模块的 variables.tf 中定义的输入变量传递值。
  • output "created_file_path" { ... }: 这个根级别的输出块从模块中检索一个值。语法是 module.<MODULE_INSTANCE_NAME>.<OUTPUT_NAME>

保存文件并退出 nano。你的项目现在已完全配置为使用该模块。

运行 terraform apply 以通过模块进行部署

在最后一步中,你将使用标准的 Terraform 命令来初始化(initialize)、规划(plan)和应用(apply)你的配置。这将执行模块中的代码并创建本地文件。

首先,初始化 Terraform 工作目录。此命令会下载必要的 provider 插件(在本例中是 hashicorp/local)。

terraform init

你应该会看到一条成功消息,表明 Terraform 已成功初始化。

Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/local versions matching "2.4.0"...
- Installing hashicorp/local v2.4.0...
- Installed hashicorp/local v2.4.0 (signed by HashiCorp)

Terraform has been successfully initialized!
...

接下来,运行 terraform plan 来查看 Terraform 将要进行哪些更改。这是一个“干运行”(dry run),它不会改变任何东西,但会向你展示执行计划。

terraform plan

输出将显示将创建一个资源(模块内的 local_file)。

...
Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + created_file_path = (known after apply)
...

现在,应用配置以创建文件。我们将使用 -auto-approve 标志来跳过交互式确认提示。

terraform apply -auto-approve

Terraform 将执行计划并创建文件。输出将确认创建并显示你定义的输出值。

...
module.file_creator_instance.local_file.example: Creating...
module.file_creator_instance.local_file.example: Creation complete after 0s [id=f73598097552a798110a31388c54c1194b539a53]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

created_file_path = "./modules/localfile_creator/my_test_file.txt"

最后,验证文件是否已使用正确的内容创建。列出模块目录中的文件,然后显示新文件的内容。

ls -l modules/localfile_creator/
cat modules/localfile_creator/my_test_file.txt

cat 命令的输出应该是:

Hello from the root module!

恭喜你,你已成功创建并使用了 Terraform 模块!

总结

在这个实验中,你成功学习了创建和使用 Terraform 模块的基础知识。你经历了整个过程,从设置正确的目录结构到通过可重用模块部署资源。

你已经学会了:

  • 为本地模块创建标准的目录结构。
  • 在模块的 main.tf 文件中定义资源。
  • 使用 variables.tf 使你的模块可配置和可重用。
  • 使用 outputs.tf 将数据从你的模块暴露给调用配置。
  • 从根 main.tf 文件中调用本地模块,传递输入变量并访问输出值。
  • 应用配置以查看模块的实际效果。

通过掌握模块,你可以编写更清晰、更有条理、更具可扩展性的基础设施即代码(Infrastructure as Code)。恭喜你完成了这个实验!