如何构建 Terraform 项目

介绍

Terraform项目根据其用例和感知的复杂性进行结构化是确保其在日常操作中可维护和可扩展的关键。

在本教程中,您将了解如何根据其一般目的和复杂性来结构Terraform项目,然后,您将使用Terraform的更常见功能创建一个简单的结构项目:变量,本地,数据源和供应商。

前提条件

您可以通过DigitalOcean控制面板创建一个DigitalOcean个人访问代码。DigitalOcean产品文档中可以找到如何创建个人访问代码(https://docs.digitalocean.com/reference/api/create-personal-access-token/)。

<$>[注] 注: 本教程已专门用Terraform 1.0.2进行测试。

了解 Terraform 项目的结构

在本节中,您将了解Terraform认为什么是项目,如何构建基础设施代码,以及何时选择哪种方法,您还将了解Terraform的工作区,他们做什么,以及Terraform如何存储状态。

一个 resource是云服务的实体(如DigitalOcean Droplet)在Terraform代码中声明,该实体是根据指定的和推断的属性创建的。

Terraform 使用一个专门的编程语言来定义基础设施,称为 Hashicorp Configuration Language (HCL)。 HCL 代码通常存储在用扩展 tf 结束的文件中。

Terraform state是它跟踪实际部署在云中的资源的机制。 状态存储在后端(本地在磁盘上或远程在文件存储云服务或专门的状态管理软件)中,以获得最佳的冗余性和可靠性。 您可以在 Terraform 文档中阅读更多关于不同的后端。

Project workspaces允许您在相同的后端中具有多个状态,连接到相同的配置. 这允许您部署相同的基础设施的多个不同的实例. 每个项目都以名为默认的工作区开始,如果您不明确创建或切换到另一个工作区,则将使用此工作区。

Modules在Terraform(类似于其他编程语言的库)中是包含多个资源声明的参数化代码容器,它们允许您抽象你的基础设施的一部分,并以不同的输入再使用它。

Terraform 项目还可以包含用于动态数据输入的外部代码文件,这些文件可以解析 CLI 命令的 JSON 输出并提供用于资源声明。

现在你知道Terraform项目的构成是什么,让我们审查Terraform项目结构的两种一般方法。

简单结构

一个简单的结构适用于小型和测试项目,有几种不同类型的资源和变量。 它有几个配置文件,通常每个资源类型(或更多的辅助元件),而没有自定义模块,因为大多数资源都是独一无二的,并且没有足够的资源来概括和重复使用。 随后,大多数代码存储在相同的目录中,彼此相邻。 这些项目通常有几种变量(如访问云的API密钥),并且可能使用动态数据输入和其他Terraform和HCL功能,尽管不突出。

作为这种方法的文件结构的例子,这就是你将在本教程中构建的项目最终会是什么样子:

 1.
 2└── tf/
 3    ├── versions.tf
 4    ├── variables.tf
 5    ├── provider.tf
 6    ├── droplets.tf
 7    ├── dns.tf
 8    ├── data-sources.tf
 9    └── external/
10        └── name-generator.py

由于该项目将部署Apache Web 服务器Droplet并设置DNS记录,项目变量定义、DigitalOcean Terraform提供商、Droplet和DNS记录将存储在各自的文件中。

复杂结构

与简单的结构不同,这种方法适用于大型项目,具有明确定义的子目录结构,包含不同复杂程度的多个模块,除了通常的代码。这些模块可以相互依赖。

开发、阶段化、质量保证和生产基础设施实例也可以通过依赖于常见模块在不同的目录中托管在同一个项目之下,从而消除重复代码,使该项目成为真理的中心来源。

 1.
 2└── tf/
 3    ├── modules/
 4       ├── network/
 5          ├── main.tf
 6          ├── dns.tf
 7          ├── outputs.tf
 8          └── variables.tf
 9       └── spaces/
10           ├── main.tf
11           ├── outputs.tf
12           └── variables.tf
13    └── applications/
14        ├── backend-app/
15           ├── env/
16              ├── dev.tfvars
17              ├── staging.tfvars
18              ├── qa.tfvars
19              └── production.tfvars
20           └── main.tf
21        └── frontend-app/
22            ├── env/
23               ├── dev.tfvars
24               ├── staging.tfvars
25               ├── qa.tfvars
26               └── production.tfvars
27            └── main.tf

这种方法在系列中进一步探讨了(https://www.digitalocean.com/community/tutorial_series/how-to-manage-infrastructure-with-terraform)。

现在你知道什么是Terraform项目,如何根据感知的复杂性进行最佳结构,以及Terraform工作区的作用。在接下来的步骤中,你将创建一个具有简单结构的项目,该项目将提供一个Droplet,安装了Apache网络服务器,并为您的域设置了DNS记录。

步骤1 - 设置您的初始项目

在本节中,您将添加 DigitalOcean Terraform 提供商到您的项目,定义项目变量,并声明一个 DigitalOcean 提供商实例,以便 Terraform 能够连接到您的帐户。

开始创建您的Terraform项目的目录,使用以下命令:

1mkdir ~/apache-droplet-terraform

导航它:

1cd ~/apache-droplet-terraform

由于此项目将遵循简单的结构化方法,您将将提供商、变量、Droplet 和 DNS 记录代码存储在单独的文件中,每个文件结构来自上一节。

创建一个名为 `versions.tf 的文件,并通过运行来打开它以进行编辑:

1nano versions.tf

添加以下几行:

1[label ~/apache-droplet-terraform/versions.tf]
2terraform {
3  required_providers {
4    digitalocean = {
5      source = "digitalocean/digitalocean"
6      version = "~> 2.0"
7    }
8  }
9}

在这个‘terraform’块中,你列出所需的提供商(DigitalOcean,版本‘2.x’)。

然后,按照存储不同类型的资源在单独的代码文件中的方法,定义您项目将在 variables.tf 文件中暴露的变量:

1nano variables.tf

添加以下变量:

1[label ~/apache-droplet-terraform/variables.tf]
2variable "do_token" {}
3variable "domain_name" {}

保存并关闭文件。

do_token变量将持有您的 DigitalOcean Personal Access Token,而domain_name将指定您想要的域名。

接下来,我们来定义这个项目的 DigitalOcean provider 实例. 你会将其存储在一个名为 provider.tf 的文件中。

1nano provider.tf

添加供应商:

1[label ~/apache-droplet-terraform/provider.tf]
2provider "digitalocean" {
3  token = var.do_token
4}

您已定义了digitalocean提供商,该提供商与您在provider.tf中提前指定的所需提供商相匹配,并将其代币设置为变量值,该值将在运行时提供。

在此步骤中,您为您的项目创建了一个目录,请求DigitalOcean提供商可用,声明项目变量,并设置与DigitalOcean提供商实例的连接,以使用后来提供的身份验证代码。

步骤 2 — 创建动态数据的Python脚本

在继续定义Droplet之前,您将创建一个Python脚本,该脚本将动态地生成Droplet的名称,并声明数据源资源来解析它.该名称将通过连接一个恒定的字符串(web)与本地机器的当前时间来生成,以 UNIX 时代格式表示。

您将将脚本存储在名为name-generator.py的文件中,在名为external的目录中。

1mkdir external

外部目录位于项目的根部,将存储非HCL代码文件,就像你要写的Python脚本一样。

外部下创建name-generator.py并打开它以进行编辑:

1nano external/name-generator.py

添加以下代码:

1[label external/name-generator.py]
2import json, time
3
4fixed_name = "web"
5result = {
6  "name": f"{fixed_name}-{int(time.time())}",
7}
8
9print(json.dumps(result))

这个Python脚本导入了jsontime模块,声明了一个名为结果的字典(LINK0),并将名称密钥的值设置为一个插入的字符串,将fixed_name与运行机器的当前UNIX时间相结合,然后将结果转换为JSON并输出到stdout

1[secondary_label Output]
2{"name": "web-1597747959"}

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

<$>[注] 注: 大型和复杂的结构化项目需要更多地考虑外部数据源是如何创建和使用的,特别是在便携性和错误处理方面。

有关Terraform所期待的更多信息,请访问数据源的官方文件(https://registry.terraform.io/providers/hashicorp/external/latest/docs/data-sources/data_source)。

现在脚本已经准备好了,您可以定义数据源,这将从脚本中提取数据. 您将将数据源存储在名为 data-sources.tf 的文件中,根据简单的结构化方法,在项目的根部。

通过运行来创建编辑:

1nano data-sources.tf

添加以下定义:

1[label ~/apache-droplet-terraform/data-sources.tf]
2data "external" "droplet_name" {
3  program = ["python3", "${path.module}/external/name-generator.py"]
4}

保存并关闭文件。

这个数据源被称为droplet_name并使用Python 3执行name-generator.py脚本,该脚本位于你刚刚创建的外部目录中,它自动解析其输出,并在其结果属性下提供 deserialized数据,以便在其他资源定义中使用。

有了现在声明的数据源,您可以定义 Apache 将运行的 Droplet。

步骤3 - 定义滴滴

在此步骤中,您将写下Droplet资源的定义,并将其存储在专门用于Droplets的代码文件中,根据简单的结构化方法,其名称将来自您刚刚创建的动态数据源,并且每次部署都将不同。

创建并打开droplets.tf文件进行编辑:

1nano droplets.tf

添加以下 Droplet 资源定义:

 1[label ~/apache-droplet-terraform/droplets.tf]
 2data "digitalocean_ssh_key" "ssh_key" {
 3  name = "your_ssh_key_name"
 4}
 5
 6resource "digitalocean_droplet" "web" {
 7  image  = "ubuntu-20-04-x64"
 8  name   = data.external.droplet_name.result.name
 9  region = "fra1"
10  size   = "s-1vcpu-1gb"
11  ssh_keys = [
12    data.digitalocean_ssh_key.ssh_key.id
13  ]
14}

您首先宣布一个名为ssh_key的 DigitalOcean SSH 密钥资源,该资源将以其名义从您的帐户中获取密钥。

然后,您声明一个 Droplet 资源,称为Web。云中的实际名称将不同,因为它是从droplet_name外部数据源中请求的。

目前,这就是您所需的与 `dll.dll 相关的配置,所以在完成后保存和关闭文件。

现在,您将写入 DNS 记录的配置,该配置将指向您的域名刚刚宣布的 Droplet。

步骤 4 – 定义 DNS 记录

该过程的最后一步是配置 DNS 记录,指向您的域的 Droplet。

您将将 DNS 配置存储在名为 'dns.tf' 的文件中,因为它是您在上一步创建的其他资源类型。

1nano dns.tf

添加以下几行:

1[label ~/apache-droplet-terraform/dns.tf]
2resource "digitalocean_record" "www" {
3  domain = var.domain_name
4  type   = "A"
5  name   = "@"
6  value  = digitalocean_droplet.web.ipv4_address
7}

此代码声明一个DigitalOcean DNS记录在您的域名(通过使用变量),类型 A. 该记录有一个名称 @,这是一个位置持有者路由到域本身和与Dropplet IP地址作为其 `值。

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

现在您已配置 Droplet、名称生成器数据源和 DNS 记录,您将继续在云中部署该项目。

步骤 5 – 规划和应用配置

在本节中,您将初始化您的 Terraform 项目,将其部署到云中,并检查所有内容是否正确配置。

现在,项目基础设施已经完全定义,在部署之前,只需启动Terraform项目即可完成。

1terraform init

您将收到以下输出:

 1[secondary_label Output]
 2Initializing the backend...
 3
 4Initializing provider plugins...
 5- Finding digitalocean/digitalocean versions matching "~> 2.0"...
 6- Finding latest version of hashicorp/external...
 7- Installing digitalocean/digitalocean v2.10.1...
 8- Installed digitalocean/digitalocean v2.10.1 (signed by a HashiCorp partner, key ID F82037E524B9C0E8)
 9- Installing hashicorp/external v2.1.0...
10- Installed hashicorp/external v2.1.0 (signed by HashiCorp)
11
12Partner and community providers are signed by their developers.
13If you'd like to know more about provider signing, you can read about it here:
14https://www.terraform.io/docs/cli/plugins/signing.html
15
16Terraform has created a lock file .terraform.lock.hcl to record the provider
17selections it made above. Include this file in your version control repository
18so that Terraform can guarantee to make the same selections by default when
19you run "terraform init" in the future.
20
21Terraform has been successfully initialized!
22
23You may now begin working with Terraform. Try running "terraform plan" to see
24any changes that are required for your infrastructure. All Terraform commands
25should now work.
26
27If you ever set or change modules or backend configuration for Terraform,
28rerun this command to reinitialize your working directory. If you forget, other
29commands will detect it and remind you to do so if necessary.

您现在将能够将您的Droplet以动态生成的名称和伴随的域名部署到您的DigitalOcean帐户中。

首先,将域名、SSH 密钥指纹和个人访问代码定义为环境变量,因此您不必每次运行 Terraform 时复制这些值。

1export DO_PAT="your_do_api_token"
2export DO_DOMAIN_NAME="your_domain"

您可以在您的 DigitalOcean 控制面板中找到您的 API 代币。

运行计划命令与传递的变量值,看看Terraform将采取哪些步骤来部署您的项目:

1terraform plan -var "do_token=${DO_PAT}" -var "domain_name=${DO_DOMAIN_NAME}"

结果将类似于以下:

 1[secondary_label Output]
 2Terraform used the selected providers to generate the following execution plan. Resource
 3actions are indicated with the following symbols:
 4  + create
 5
 6Terraform will perform the following actions:
 7
 8  # digitalocean_droplet.web will be created
 9  + resource "digitalocean_droplet" "web" {
10      + backups              = false
11      + created_at           = (known after apply)
12      + disk                 = (known after apply)
13      + id                   = (known after apply)
14      + image                = "ubuntu-20-04-x64"
15      + ipv4_address         = (known after apply)
16      + ipv4_address_private = (known after apply)
17      + ipv6                 = false
18      + ipv6_address         = (known after apply)
19      + locked               = (known after apply)
20      + memory               = (known after apply)
21      + monitoring           = false
22      + name                 = "web-1625908814"
23      + price_hourly         = (known after apply)
24      + price_monthly        = (known after apply)
25      + private_networking   = (known after apply)
26      + region               = "fra1"
27      + resize_disk          = true
28      + size                 = "s-1vcpu-1gb"
29      + ssh_keys             = [
30          + "...",
31        ]
32      + status               = (known after apply)
33      + urn                  = (known after apply)
34      + vcpus                = (known after apply)
35      + volume_ids           = (known after apply)
36      + vpc_uuid             = (known after apply)
37    }
38
39  # digitalocean_record.www will be created
40  + resource "digitalocean_record" "www" {
41      + domain = "your_domain'"
42      + fqdn   = (known after apply)
43      + id     = (known after apply)
44      + name   = "@"
45      + ttl    = (known after apply)
46      + type   = "A"
47      + value  = (known after apply)
48    }
49
50Plan: 2 to add, 0 to change, 0 to destroy.
51...

以绿色+开头的线条意味着Terraform将创建随后的每个资源 - 这正是应该发生的事情,因此您可以应用配置:

1terraform apply -var "do_token=${DO_PAT}" -var "domain_name=${DO_DOMAIN_NAME}"

输出将与以前相同,但这次您将被要求确认:

1[secondary_label Output]
2Plan: 2 to add, 0 to change, 0 to destroy.
3
4Do you want to perform these actions?
5  Terraform will perform the actions described above.
6  Only 'yes' will be accepted to approve.
7
8  Enter a value: `yes`

输入,Terraform将提供您的Droplet和DNS记录:

1[secondary_label Output]
2digitalocean_droplet.web: Creating...
3...
4digitalocean_droplet.web: Creation complete after 33s [id=204432105]
5digitalocean_record.www: Creating...
6digitalocean_record.www: Creation complete after 1s [id=110657456]
7
8Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Terraform 现在已经在其状态中记录了部署的资源. 为了确认 DNS 记录和 Droplet 已成功连接,您可以从本地状态中提取 Droplet 的 IP 地址,并检查它是否匹配您的域名的公共 DNS 记录。

1terraform show | grep "ipv4"

您将收到您的 Droplet 的 IP 地址:

1[secondary_label Output]
2ipv4_address       = "your_Droplet_IP"
3...

您可以通过运行查看公众 A 记录:

1nslookup -type=a your_domain | grep "Address" | tail -1

输出将显示 A 记录指向的 IP 地址:

1[secondary_label Output]
2Address: your_Droplet_IP

它们是相同的,它们应该是,这意味着Droplet和DNS记录已成功提供。

若要在下一步发生的更改,请通过运行来摧毁部署的资源:

1terraform destroy -var "do_token=${DO_PAT}" -var "domain_name=${DO_DOMAIN_NAME}"

当被提示时,输入以继续。

在此步骤中,您已经创建了基础设施,并将其应用到您的DigitalOcean帐户,现在您将修改它,以便使用Terraform提供商自动安装Apache网页服务器在提供的Droplet上。

步骤 6 – 使用提供商运行代码

现在,您将通过使用远程执行提供程序在部署的 Droplet 上设置Apache Web 服务器的安装,以执行自定义命令。

Terraform 提供者可用于在创建的远程资源(远程执行提供器)或执行代码的本地机器(使用本地执行提供器)上执行特定操作。

要连接到提供的 Droplet,Terraform 需要在 Droplet 上设置的私钥的私钥 SSH。 通过使用变量传输私钥的位置的最佳方法是打开 variables.tf 进行编辑:

1nano variables.tf

添加突出的线条:

1[label ~/apache-droplet-terraform/variables.tf]
2variable "do_token" {}
3variable "domain_name" {}
4variable "private_key" {}

您现在已将一个名为private_key的新变量添加到您的项目中. 保存并关闭文件。

接下来,您将将连接数据和远程提供商声明添加到您的 Droplet 配置中。

1nano droplets.tf

用突出的行扩展现有代码:

 1[label ~/apache-droplet-terraform/droplets.tf]
 2data "digitalocean_ssh_key" "ssh_key" {
 3  name = "your_ssh_key_name"
 4}
 5
 6resource "digitalocean_droplet" "web" {
 7  image  = "ubuntu-20-04-x64"
 8  name   = data.external.droplet_name.result.name
 9  region = "fra1"
10  size   = "s-1vcpu-1gb"
11  ssh_keys = [
12    data.digitalocean_ssh_key.ssh_key.id
13  ]
14
15  connection {
16    host        = self.ipv4_address
17    user        = "root"
18    type        = "ssh"
19    private_key = file(var.private_key)
20    timeout     = "2m"
21  }
22
23  provisioner "remote-exec" {
24    inline = [
25      "export PATH=$PATH:/usr/bin",
26      # Install Apache
27      "apt update",
28      "apt -y install apache2"
29    ]
30  }
31}

连接块规定了Terraform应该如何连接到目标Droplet。供应商块包含在线上参数内的命令组合,它将在提供后执行,即更新包管理器缓存并安装Apache。

您也可以为私钥路径创建临时环境变量:

1export DO_PRIVATE_KEY="private_key_location"

<$>[注] **注:**私钥,以及您想从Terraform中下载的任何其他文件,必须放在项目中。您可以参阅 如何在Linux服务器上配置SSH基于密钥的身份验证教程,以获取有关Ubuntu 20.04或其他发行版上设置SSH密钥的更多信息。

尝试再次使用配置:

1terraform apply -var "do_token=${DO_PAT}" -var "domain_name=${DO_DOMAIN_NAME}" -var "private_key=${DO_PRIVATE_KEY}"

当提示时输入时,您将收到与以前相似的输出,但随后从远程执行提供器获得长的输出:

 1[secondary_label Output]
 2digitalocean_droplet.web: Creating...
 3digitalocean_droplet.web: Still creating... [10s elapsed]
 4digitalocean_droplet.web: Still creating... [20s elapsed]
 5digitalocean_droplet.web: Still creating... [30s elapsed]
 6digitalocean_droplet.web: Provisioning with 'remote-exec'...
 7digitalocean_droplet.web (remote-exec): Connecting to remote host via SSH...
 8digitalocean_droplet.web (remote-exec):   Host: ...
 9digitalocean_droplet.web (remote-exec):   User: root
10digitalocean_droplet.web (remote-exec):   Password: false
11digitalocean_droplet.web (remote-exec):   Private key: true
12digitalocean_droplet.web (remote-exec):   Certificate: false
13digitalocean_droplet.web (remote-exec):   SSH Agent: false
14digitalocean_droplet.web (remote-exec):   Checking Host Key: false
15digitalocean_droplet.web (remote-exec): Connected!
16...
17digitalocean_droplet.web: Creation complete after 1m5s [id=204442200]
18digitalocean_record.www: Creating...
19digitalocean_record.www: Creation complete after 1s [id=110666268]
20
21Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

您现在可以在 Web 浏览器中导航到您的域名,您将看到默认的 Apache 欢迎页面。

Apache Web Server - Default Page

这意味着Apache已成功安装,Terraform提供了正确的配置。

要摧毁部署的资源,运行以下命令,并在提示时输入:

1terraform destroy -var "do_token=${DO_PAT}" -var "domain_name=${DO_DOMAIN_NAME}" -var "private_key=${DO_PRIVATE_KEY}"

您现在已经完成了一个小型Terraform项目,有一个简单的结构,它部署了Apache网络服务器在Droplet上,并为所需的域设置了DNS记录。

结论

按照简单的结构化方法,并使用远程执行提供程序执行命令,您随后部署了一个 Droplet 运行 Apache 的 DNS 记录为您的域。

作为参考,这里是您在本教程中创建的项目的文件结构:

 1.
 2└── tf/
 3    ├── versions.tf
 4    ├── variables.tf
 5    ├── provider.tf
 6    ├── droplets.tf
 7    ├── dns.tf
 8    ├── data-sources.tf
 9    └── external/
10        └── name-generator.py

您定义的资源(Droplet、DNS记录和动态数据源、DigitalOcean提供商和变量)分别存储在其自己的单独文件中,根据本教程的第一部分概述的简单项目结构。

有关 Terraform 供应商及其参数的更多信息,请参阅 官方文件

本教程是《如何使用Terraform管理基础设施》系列的一部分,该系列涵盖了许多Terraform主题,从首次安装Terraform到管理复杂项目。

Published At
Categories with 技术
comments powered by Disqus