介绍
Terraform模块通过将它们的资源分组在一起来封装基础设施的不同逻辑组件。您可以稍后通过可能的自定义重新使用它们,而无需在每次需要时重复资源定义,这对大型且结构复杂的项目有利。您可以使用您定义的输入变量自定义模块实例,也可以使用输出从中提取信息。除了创建自己的自定义模块之外,您还可以使用在Terraform Registry 上公开发布的预制模块。开发人员可以使用您创建的模块等输入来使用和自定义它们,但它们的源代码存储在云端并从云端提取。
在本教程中,您将创建一个 Terraform 模块,该模块将在负载均衡器后面设置多个 Droplet 以实现冗余。您还可以使用for_each
与count
该Hashicorp配置语言(HCL)的循环功能,在同一时间部署模块的多个定制实例。
先决条件
- DigitalOcean 个人访问令牌,您可以通过 DigitalOcean 控制面板创建。可以在此链接中找到执行此操作的说明:如何生成个人访问令牌。
- Terraform 安装在您的本地计算机上,并使用 DO 提供程序设置了一个项目。完成步骤1和步骤2中的如何使用Terraform与DigitalOcean教程,并确保该项目命名文件夹
terraform-modules
,而不是loadbalance
。在步骤 2 中,不要包含pvt_key
变量和 SSH 密钥资源。 - 熟悉 HCL 数据类型和循环。有关更多信息,请参阅如何使用 Terraform 变量、依赖项和条件提高灵活性教程。
- 熟悉 Terraform 输出及其用法。您可以按照如何使用 Terraform 输出管理基础设施数据教程来了解它们。
注意:本教程已专门使用 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
添加以下几行:
variable "droplet_count" {}
variable "group_name" {}
保存并关闭文件。
您将 Droplet 定义存储在名为droplets.tf
. 创建并打开它进行编辑:
- nano droplets.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
添加其资源定义:
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
提供程序:
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
}
}
required_version = ">= 0.13"
}
完成后保存并关闭文件。该droplet-lb
模块现在需要digitalocean
提供程序。
模块还支持输出,您可以使用它来提取有关其资源状态的内部信息。您将定义一个公开负载均衡器 IP 地址的输出,并将其存储在名为outputs.tf
. 创建它以进行编辑:
- nano outputs.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
输出将如下所示:
OutputInitializing 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-0
、group1-1
和 的Droplet,group1-2
还将创建一个名为 的负载均衡器group1-lb
,该负载均衡器将管理进出三个 Droplet 的流量。
您可以尝试通过运行将项目应用到云中:
- terraform apply -var "do_token=${DO_PAT}"
输入yes
提示时。输出将显示所有操作,负载均衡器的 IP 地址也将显示:
Outputmodule.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}"
}
通过设置count
为3
,您可以指示 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_count
take 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
...
在此步骤中,您使用count
并for_each
部署了来自同一代码的同一模块的多个自定义实例。
结论
在本教程中,您已经创建并部署了 Terraform 模块。您已经使用模块将逻辑链接的资源组合在一起并对其进行自定义,以便从中央代码定义部署多个不同的实例。您还使用输出来显示模块中包含的资源的属性。
如果您想了解有关 Terraform 的更多信息,请查看我们的如何使用 Terraform 管理基础设施系列。