如何构建自定义 Terraform 模块

介绍

Terraform模块通过将它们的资源分组在一起来封装基础设施的不同逻辑组件。您可以稍后通过可能的自定义重新使用它们,而无需在每次需要时重复资源定义,这对大型且结构复杂的项目有利。您可以使用您定义的输入变量自定义模块实例,也可以使用输出从中提取信息。除了创建自己的自定义模块之外,您还可以使用在Terraform Registry 上公开发布的预制模块开发人员可以使用您创建的模块等输入来使用和自定义它们,但它们的源代码存储在云端并从云端提取。

在本教程中,您将创建一个 Terraform 模块,该模块将在负载均衡器后面设置多个 Droplet 以实现冗余。您还可以使用for_eachcount该Hashicorp配置语言(HCL)的循环功能,在同一时间部署模块的多个定制实例。

先决条件

注意:本教程已专门使用 Terraform 进行了测试0.13

模块结构和优势

在本节中,您将了解模块带来的好处、它们通常放置在项目中的位置以及它们的结构方式。

创建自定义 Terraform 模块以封装在较大项目中经常一起使用和部署的连接组件。它们是自包含的,只捆绑了它们需要的资源、变量和提供者。

模块通常存储在项目根目录的中央文件夹中,每个模块都在其各自的子文件夹下。为了保持模块之间的清晰分离,始终将它们构建为具有单一目的,并确保它们从不包含子模块。

当您发现自己以不频繁的自定义重复它们时,从您的资源方案创建模块很有用。将单个资源打包为模块可能是多余的,并且会逐渐消除整体架构的简单性。

对于小型开发和测试项目,没有必要合并模块,因为在这些情况下它们不会带来太大的改进。具有定制能力的模块是复杂结构项目的构建元素。开发人员将模块用于更大的项目,因为它具有避免代码重复的显着优势。模块还提供了一个好处,即定义只需要在一个地方修改,然后将通过基础设施的其余部分传播。

接下来,您将在 Terraform 项目中定义、使用和自定义模块。

创建模块

在本节中,您将定义多个 Droplet 和一个负载均衡器作为 Terraform 资源并将它们打包到一个模块中。您还将使用模块输入使生成的模块可自定义。

您将模块存储在名为 的目录中droplet-lb,位于名为的目录下modules假设您位于terraform-modules作为先决条件的一部分创建目录中,请通过运行以下命令同时创建:

  • mkdir -p modules/droplet-lb

-p参数指示mkdir在提供的路径中创建所有目录。

导航到它:

  • cd modules/droplet-lb

如上一节所述,模块包含它们使用的资源和变量。从 Terraform 开始0.13,它们还必须包含它们使用的提供程序的定义。模块不需要任何特殊配置,注意代码代表一个模块,因为Terraform将每个包含HCL代码的目录都视为一个模块,甚至是项目的根目录。

模块中定义的变量作为其输入公开,可用于资源定义以自定义它们。您将创建的模块将有两个输入:要创建的 Droplet 数量及其组的名称。创建并打开一个名为variables.tf您将在其中存储变量的文件以进行编辑

  • nano variables.tf

添加以下几行:

变量.tf
variable "droplet_count" {}
variable "group_name" {}

保存并关闭文件。

您将 Droplet 定义存储在名为droplets.tf. 创建并打开它进行编辑:

  • nano droplets.tf

添加以下几行:

液滴.tf
resource "digitalocean_droplet" "droplets" {
  count  = var.droplet_count
  image  = "ubuntu-18-04-x64"
  name   = "${var.group_name}-${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

对于count指定要创建多少资源实例参数,您需要传入droplet_count变量。它的值将在从主项目代码调用模块时指定。每个部署的 Droplet 的名称都会不同,您可以通过将当前 Droplet 的索引附加到提供的组名称来实现。Droplets 的部署将在该fra1地区进行,它们将运行 Ubuntu 18.04。

完成后,保存并关闭文件。

现在定义了 Droplet,您可以继续创建负载均衡器。您将其资源定义存储在名为lb.tf. 通过运行创建并打开它进行编辑:

  • nano lb.tf

添加其资源定义:

磅.tf
resource "digitalocean_loadbalancer" "www-lb" {
  name   = "lb-${var.group_name}"
  region = "fra1"

  forwarding_rule {
    entry_port     = 80
    entry_protocol = "http"

    target_port     = 80
    target_protocol = "http"
  }

  healthcheck {
    port     = 22
    protocol = "tcp"
  }

  droplet_ids = [
    for droplet in digitalocean_droplet.droplets:
      droplet.id
  ]
}

您可以使用名称中的组名定义负载均衡器,以使其可区分。您将它fra1与 Droplets 一起部署在该区域。接下来的两部分指定目标和监控端口和协议。

突出显示的droplet_ids块接收应由负载均衡器管理的 Droplet 的 ID。由于有多个 Droplet,并且事先不知道它们的数量,因此您使用for循环遍历 Droplet ( digitalocean_droplet.droplets)的集合并获取它们的 ID。for用括号 ( [])循环括起来,这样生成的集合将是一个列表。

您现在已经为您的模块定义了 Droplet、负载均衡器和变量。您需要定义提供者要求,指定模块使用哪些提供者,包括它们的版本和它们所在的位置。由于 Terraform 0.13,模块必须明确定义它们使用的非 Hashicorp 维护的提供程序的来源;这是因为它们没有从父项目继承它们。

您将提供程序要求存储在名为provider.tf. 通过运行创建它以进行编辑:

  • nano provider.tf

添加以下行以要求digitalocean提供程序:

提供者.tf
terraform {
  required_providers {
    digitalocean = {
      source = "digitalocean/digitalocean"
    }
  }
  required_version = ">= 0.13"
}

完成后保存并关闭文件。droplet-lb模块现在需要digitalocean提供程序。

模块还支持输出,您可以使用它来提取有关其资源状态的内部信息。您将定义一个公开负载均衡器 IP 地址的输出,并将其存储在名为outputs.tf. 创建它以进行编辑:

  • nano outputs.tf

添加以下定义:

输出.tf
output "lb_ip" {
  value = digitalocean_loadbalancer.www-lb.ip
}

此输出检索负载平衡器的 IP 地址。保存并关闭文件。

droplet-lb模块现在功能完整,可以部署。您将从主代码中调用它,主代码将存储在项目的根目录中。首先,通过向上浏览文件目录两次来导航到它:

  • cd ../..

然后,创建并打开一个名为 的文件以进行编辑main.tf,您将在其中使用该模块:

  • nano main.tf

添加以下几行:

主文件
module "groups" {
  source = "./modules/droplet-lb"

  droplet_count = 3
  group_name    = "group1"
}

output "loadbalancer-ip" {
  value = module.groups.lb_ip
}

在此声明中,您调用droplet-lb位于指定为 的目录中模块source您配置它提供的输入,droplet_count并且group_name设置为 ,group1以便您稍后能够区分实例。

由于负载均衡器 IP 输出是在模块中定义的,因此在您应用项目时不会自动显示。对此的解决方案是创建另一个输出以检索其值 ( loadbalancer_ip)。完成后保存并关闭文件。

通过运行初始化模块:

  • terraform init

输出将如下所示:

Output
Initializing modules... - droplet-lb in modules/droplet-lb Initializing the backend... Initializing provider plugins... - Using previously-installed digitalocean/digitalocean v1.22.2 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.

您可以尝试通过运行以下命令来规划项目以查看 Terraform 将采取的操作:

  • terraform plan -var "do_token=${DO_PAT}"

输出将类似于:

Output
... An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # module.groups.digitalocean_droplet.droplets[0] will be created + resource "digitalocean_droplet" "droplets" { ... + name = "group1-0" ... } # module.groups.digitalocean_droplet.droplets[1] will be created + resource "digitalocean_droplet" "droplets" { ... + name = "group1-1" ... } # module.groups.digitalocean_droplet.droplets[2] will be created + resource "digitalocean_droplet" "droplets" { ... + name = "group1-2" ... } # module.groups.digitalocean_loadbalancer.www-lb will be created + resource "digitalocean_loadbalancer" "www-lb" { ... + name = "group1-lb" ... } Plan: 4 to add, 0 to change, 0 to destroy. ...

此输出详细说明 Terraform 将创建三个名为group1-0group1-1和 的Droplet,group1-2还将创建一个名为 的负载均衡器group1-lb,该负载均衡器将管理进出三个 Droplet 的流量。

您可以尝试通过运行将项目应用到云中:

  • terraform apply -var "do_token=${DO_PAT}"

输入yes提示时。输出将显示所有操作,负载均衡器的 IP 地址也将显示:

Output
module.groups.digitalocean_droplet.droplets[1]: Creating... module.groups.digitalocean_droplet.droplets[0]: Creating... module.groups.digitalocean_droplet.droplets[2]: Creating... ... Apply complete! Resources: 4 added, 0 changed, 0 destroyed. Outputs: loadbalancer-ip = ip_address

由于您将在下一步中显着修改配置,因此请通过运行以下命令销毁已部署的资源:

  • terraform destroy -var "do_token=${DO_PAT}"

输入yes提示时。输出将以:

Output
.. Destroy complete! Resources: 4 destroyed.

在此步骤中,您创建了一个包含可自定义数量的 Droplet 和负载均衡器的模块,这些负载均衡器将自动配置为管理其传入和传出流量。现在,您将使用for_each部署来自同一代码的模块的多个实例count

部署多个模块实例

在本节中,您将使用count多次for_each部署droplet-lb模块,并进行自定义。

使用 count

一次部署同一模块的多个实例的一种方法是将数量传递给count参数,该参数可自动用于每个模块。打开main.tf编辑:

  • nano main.tf

将其修改为如下所示:

主文件
module "groups" {
  source = "./modules/droplet-lb"

  count  = 3

  droplet_count = 3
  group_name    = "group1-${count.index}"
}

通过设置count3,您可以指示 Terraform 将模块部署三次,每次使用不同的组名。完成后,保存并关闭文件。

通过运行来规划部署:

  • terraform plan -var "do_token=${DO_PAT}"

输出会很长,看起来像这样:

Output
... An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # module.groups[0].digitalocean_droplet.droplets[0] will be created ... # module.groups[0].digitalocean_droplet.droplets[1] will be created ... # module.groups[0].digitalocean_droplet.droplets[2] will be created ... # module.groups[0].digitalocean_loadbalancer.www-lb will be created ... # module.groups[1].digitalocean_droplet.droplets[0] will be created ... # module.groups[1].digitalocean_droplet.droplets[1] will be created ... # module.groups[1].digitalocean_droplet.droplets[2] will be created ... # module.groups[1].digitalocean_loadbalancer.www-lb will be created ... # module.groups[2].digitalocean_droplet.droplets[0] will be created ... # module.groups[2].digitalocean_droplet.droplets[1] will be created ... # module.groups[2].digitalocean_droplet.droplets[2] will be created ... # module.groups[2].digitalocean_loadbalancer.www-lb will be created ... Plan: 12 to add, 0 to change, 0 to destroy. ...

输出中的 Terraform 详细说明,三个模块实例中的每一个都将具有三个 Droplet 和一个与之关联的负载均衡器。

使用 for_each

for_each当您需要更复杂的实例自定义时,或者当实例的数量取决于第三方数据(通常显示为地图)并且在编写代码时未知时,您可以使用模块。

您现在将定义一个映射,该映射将组名称与 Droplet 计数配对并droplet-lb根据它部署实例main.tf通过运行打开以进行编辑:

  • nano main.tf

修改文件,使其看起来像这样:

主文件
variable "group_counts" {
  type    = map
  default = {
    "group1" = 1
    "group2" = 3
  }
}

module "groups" {
  source   = "./modules/droplet-lb"
  for_each = var.group_counts

  droplet_count = each.value
  group_name    = each.key
}

您首先定义一个名为的映射group_counts,其中包含给定组应具有的 Droplet 数量。然后,您调用 module droplet-lb,但指定for_each循环应在var.group_counts您之前定义的地图上运行droplet_counttake each.value,当前对的值,即当前组的 Droplet 计数。group_name接收组名。

完成后保存并关闭文件。

尝试通过运行来应用配置:

  • terraform plan -var "do_token=${DO_PAT}"

输出将详细说明 Terraform 为创建具有 Droplet 和负载均衡器的两个组而采取的操作:

Output
... An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # module.groups["group1"].digitalocean_droplet.droplets[0] will be created ... # module.groups["group1"].digitalocean_loadbalancer.www-lb will be created ... # module.groups["group2"].digitalocean_droplet.droplets[0] will be created ... # module.groups["group2"].digitalocean_droplet.droplets[1] will be created ... # module.groups["group2"].digitalocean_droplet.droplets[2] will be created ... # module.groups["group2"].digitalocean_loadbalancer.www-lb will be created ...

在此步骤中,您使用countfor_each部署了来自同一代码的同一模块的多个自定义实例。

结论

在本教程中,您已经创建并部署了 Terraform 模块。您已经使用模块将逻辑链接的资源组合在一起并对其进行自定义,以便从中央代码定义部署多个不同的实例。您还使用输出来显示模块中包含的资源的属性。

如果您想了解有关 Terraform 的更多信息,请查看我们的如何使用 Terraform 管理基础设施系列。

觉得文章有用?

点个广告表达一下你的爱意吧 !😁