通过Custom Provider来学习Terraform入门

这篇文章是SmartHR 日历2021年第18天的文章。
同时,文章只代表个人观点,并不代表任何公司或组织。

首先

大家是否知道 Terraform?
Terraform 是由 HashiCorp 公司开发的开源的基础设施即代码 (Infrastructure as a Code) 工具。
通过 Terraform,我们可以用代码来声明基础设施的配置。
通过将各种基础设施的状态,如 AWS、GCP、Azure,表示为代码,我们可以可视化了解当前的状态,并且还可以自动进行配置。

這麼方便的 Terraform 工具,你確定真的了解每個指令的作用才使用嗎?
你不小心只是隨感使用它嗎?
(順帶一提,我也是隨感使用的←)

为了更深入地了解Terraform,我们将通过实际创建一个简单的自定义提供程序来解释Terraform的各个命令是如何工作的。

我们定制的供应商

    https://github.com/sh-miyoshi/terraform-provider-sample

思考目标用户

    • terraform 初級~中級者

 

    terraformがざっくりどんなものかを知っている人が対象

什么是自定义提供商?

Custom Provider是什么?
Custom Provider类似于Terraform中的插件,用于通过terraform命令操作SaaS或其他API。
例如,Google Cloud Platform Provider就是用于通过terraform控制GCP资源的插件。

从terraform核心(terraform命令)通过RPC调用自定义提供程序,并在自定义提供程序内调用想要操作的基础设施API,如下图所示的动作形象。

core-plugins-api.png

可以在 registry.terraform.io 上找到可以用于公式的提供者。
当然,您也可以自己创建提供者,所以这次我们将创建一个简单的自定义提供者,以便了解 Terraform 的运行方式。

关于此次创建的自定义提供程序和示例应用程序。

尽管 Terraform 可以操作任何应用程序,但为了考虑到 Terraform 常被用作云管理工具的情况,本次创建的应用程序如下所示:
源代码:https://github.com/sh-miyoshi/terraform-provider-sample/tree/master/app/main.rb

undefined

关于Custom Provider的介绍,我将简单解释一下。
Custom Provider是由Go语言编写的,其源代码的基本形式如下。

terraform-provider-sample
├── main.go
└── sample
    ├── provider.go
    ├── data_source_xxx.go
    └── resource_xxx.go

在main.go文件中,我们写了当被terraform命令调用时的入口点。
main.go会调用provier.go,并在provier.go中定义一些内容。
资源的定义可以在resource_vm.go中找到,而terraform可以编写每个模式的定义和对应的CRUD操作。
如果我们在CRUD的每个回调函数中编写面向应用程序的API请求,它们就会正常运行。

这个我正在编写的Custom Provider,当terraform调用每个操作时,会显示下面这样的调试信息,使得可以清楚地知道哪个操作何时被调用。

tmp.png

请查看官方网页以获取详细信息,地址为:https://learn.hashicorp.com/collections/terraform/providers。

数据来源和资源

在说明命令之前,我们先来解释一下数据源(Data source)和资源(Resource)。
在Terraform源代码文件(以下简称tf文件)中,您可以编写resource块和data块。

资源块是用于定义应用程序资源(例如虚拟机和存储)的块,顾名思义。
对于每个资源,可以进行创建、读取、更新和删除的操作。
而数据块是只读资源。
当想要使用在另一个.tf文件中管理的资源ID等值,或者想要使用terraform未管理的资源(例如机密信息)时,可以使用数据块。

假设在样例应用中,我们可以将存储空间作为external_storage附加到VM资源上。
然后,让我们按照以下方式对tf文件进行资源管理。

myresources
├── main.tf    # providerとかを記述
├── storage.tf # storageのリソース定義
└── vm.tf      # VMのリソース定義

当您想引用在vm.tf和storage.tf中创建的资源时,可以使用以下的数据源方法。

data "sample_storage" "storage1" {
  name = "storage1"
}

resource "sample_vm" "vm1" {
  name                = "vm1"
  cpu                 = 2
  memory              = 4096
  external_storage_id = data.sample_storage.storage1.id
}

各个命令的说明

现在让我们来解释一下Terraform的各个命令。

使用terraform init 初始化

首先是init命令。
init命令是从tf文件中的terraform区块中准备所需的自定义提供商的过程。
因此,需要在最开始调用一次。

這是這次樣品應用程式的提供者資訊。

terraform {
  required_providers {
    sample = {
      source = "github.com/sh-miyoshi/sample"
      version = "0.0.1"
    }
  }
}

在terraform块中,必须提供有关提供程序(此示例中为样本)的信息。
source字段指定从何处获取提供程序,例如,如果希望获取位于官方存储库的GCP提供程序,则source = “hachicorp/google”。
※此值可以省略,省略时将从官方存储库获取。

我将实际执行一下。

$ git clone https://github.com/sh-miyoshi/terraform-provider-sample.git
$ cd terraform-provider-sample
$ terraform init

Initializing provider plugins...
- Finding github.com/sh-miyoshi/sample versions matching "0.0.1"...
- Installing github.com/sh-miyoshi/sample v0.0.1...
- Installed github.com/sh-miyoshi/sample v0.0.1 (unauthenticated)

・・・

Terraform has been successfully initialized!

・・・

查看init命令的日志,可以看到正在尝试寻找github.com/sh-miyoshi/sample的0.0.1版本。

在init命令中,会从source中指定的位置获取二进制文件,并部署到当前目录中。
如果是在GCP上,位置可能类似于./.terraform/providers/registry.terraform.io/hashicorp/google/4.3.0/linux_amd64/。

对于本次示例提供程序而言,没有将二进制文件放在任何远程存储库中。
在这种情况下,您需要事先将构建好的二进制文件部署到特定的目录中。
在Linux系统上,位置应为~/.terraform.d/plugins/github.com/sh-miyoshi/sample/<版本>/linux_amd64/下。
※这个过程可以通过执行make install命令来完成。

执行terraform计划

接下来我会解释一下plan命令。
plan命令可以将当前应用程序的状态与tf文件中的内容进行比较,并显示它们之间的差异。
它是一个显示执行计划的命令,在之后的apply命令中使用时会显示执行计划。

嗯,先试着行动起来,不要空谈八道。

$ terraform plan

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # sample_storage.storage1 will be created
  + resource "sample_storage" "storage1" {
      + id   = (known after apply)
      + name = "storage1"
      + size = 50
    }

  # sample_vm.vm1 will be created
  + resource "sample_vm" "vm1" {
      + cpu    = 1
      + id     = (known after apply)
      + memory = 2048
      + name   = "vm1"
    }

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

第一次执行计划时,不会调用提供程序的读取处理程序。这是因为Terraform无法确定哪个资源与应用程序的哪个资源相关联。
我认为,在执行了后续的应用程序命令之后,再次执行计划命令,您就可以确认在计划执行时调用了读取处理程序。
当调用提供程序的创建处理程序时,Terraform将设置用于管理资源的ID,因此以后就可以进行识别。

# こんな感じ
$ terraform plan
sample_vm.vm1: Refreshing state... [id=b33ec0b0-242f-470b-8d38-6104c90adb2c]
sample_storage.storage1: Refreshing state... [id=4087d043-32cd-4e70-b0b7-9596483a1fd5]

No changes. Your infrastructure matches the configuration.

╷
│ Warning: Call read handler
│ 
│   with sample_storage.storage1,
│   on test.tf line 18, in resource "sample_storage" "storage1":
│   18: resource "sample_storage" "storage1" {
│ 
│ Debug message: Call read handler
│ 

用于管理terraform当前状态的方法

terraform的当前状态被写入名为terraform.tfstate的文件中。
terraform通过比较terraform.tfstate的内容和通过Read handler获取的应用程序状态来显示差异。
※因此,您可以通过修改此文件来欺骗terraform执行各种操作。

{
  // 略
  "resources": [
    {
      "mode": "managed",
      "type": "sample_storage",
      "name": "storage1",
      "provider": "provider[\"github.com/sh-miyoshi/sample\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "id": "fc87b281-c659-4254-8842-a384b5bf2049", // 管理用ID
            "name": "storage1",
            "size": 50
          },
          // 略
        }
      ]
    }
  ]
}

顺便提一下,将terraform之外管理的应用资源纳入terraform管理,即将其反映到terraform.tfstate文件中,需要使用terraform import命令。由于本次无法进行详细解释,请有兴趣的人务必阅读文档。

执行terraform apply

下面将对apply命令进行解释。
apply命令是执行plan命令所生成的差异的实际命令。
如果执行apply命令时没有参数,它会先执行一次与plan命令等效的处理,然后询问是否可以执行,如果回答yes,则会被执行。
当想要在CI中执行时,可以添加-auto-approve参数,这样就可以在不需要交互的情况下执行。

我将更改main.tf并尝试应用。
由于执行日志稍微有点长,所以我将摘录并解释一下。
首先,在应用之前,计划会运行,因此会调用Read处理程序。
然后,在输入”yes”之后,可以看到一次更新和一次创建,因此可以知道Update和Create处理程序都被调用了。
反过来说,除了这些之外,没有其他被调用,所以可以看出它是一个非常简单的系统。

所有执行结果日志$ terraform apply
sample_storage.storage1: 正在刷新状态… [id=4087d043-32cd-4e70-b0b7-9596483a1fd5]
sample_vm.vm1: 正在刷新状态… [id=b33ec0b0-242f-470b-8d38-6104c90adb2c]

Terraform使用所选的提供程序生成以下执行计划。资源操作用以下符号表示:
+ 创建
~ 原地更新

Terraform将执行以下操作:

# 将在sample_vm.vm1进行原地更新
~ 资源 “sample_vm” “vm1” {
~ cpu = 1 -> 2
id = “b33ec0b0-242f-470b-8d38-6104c90adb2c”
~ memory = 2048 -> 4096
name = “vm1”
}

# 将创建sample_vm.vm2
+ 资源 “sample_vm” “vm2” {
+ cpu = 1
+ id = (apply后确定)
+ memory = 2048
+ name = “vm2”
}

计划: 添加1个,更改1个,销毁0个。

│ 警告:调用读取处理程序

│ 在test.tf的第18行,对于resource “sample_storage” “storage1″:
│ 18: resource “sample_storage” “storage1” {

│ 调试消息:调用读取处理程序

│ (在其他地方还有一个类似的警告)

您是否要执行这些操作?
Terraform将执行上述操作。
只接受输入’yes’来批准。

输入一个值:yes

sample_vm.vm2: 正在创建…
sample_vm.vm1: 正在修改… [id=b33ec0b0-242f-470b-8d38-6104c90adb2c]
sample_vm.vm2: 创建完成,耗时0秒 [id=37389411-cccf-4db6-a9df-3ff10409dd66]
sample_vm.vm1: 修改完成,耗时0秒 [id=b33ec0b0-242f-470b-8d38-6104c90adb2c]

│ 警告:调用更新处理程序

│ 在test.tf的第23行,对于resource “sample_vm” “vm1″:
│ 23: resource “sample_vm” “vm1” {

│ 调试消息:调用更新处理程序


│ 警告:调用创建处理程序

│ 在test.tf的第29行,对于resource “sample_vm” “vm2″:
│ 29: resource “sample_vm” “vm2” {

│ 调试消息:调用创建处理程序

应用完成!资源:已添加1个,已更改1个,已销毁0个。

通过 terraform 摧毁

销毁指令是应用指令的相反操作。它会删除tf文件中定义的资源。
顺便提一下,如果想要查看销毁指令的计划,可以使用 terraform plan -destroy 命令来实现。

在执行”apply”的时候,执行日志将会放在下方,但在执行”plan”时,可以看出Read处理程序被调用,当按下”yes”时,Delete处理程序将针对每个资源分别被调用。

所有执行结果日志$ terraform destroy
sample_vm.vm2: 正在刷新状态… [id=37389411-cccf-4db6-a9df-3ff10409dd66]
sample_storage.storage1: 正在刷新状态… [id=4087d043-32cd-4e70-b0b7-9596483a1fd5]
sample_vm.vm1: 正在刷新状态… [id=b33ec0b0-242f-470b-8d38-6104c90adb2c]

Terraform使用选择的提供程序生成以下执行计划。资源操作使用以下符号表示:
– 销毁

Terraform将执行以下操作:

# sample_storage.storage1将被销毁
– resource “sample_storage” “storage1” {
– id = “4087d043-32cd-4e70-b0b7-9596483a1fd5” -> 无
– name = “storage1” -> 无
– size = 50 -> 无
}

# sample_vm.vm1将被销毁
– resource “sample_vm” “vm1” {
– cpu = 2 -> 无
– id = “b33ec0b0-242f-470b-8d38-6104c90adb2c” -> 无
– memory = 4096 -> 无
– name = “vm1” -> 无
}

# sample_vm.vm2将被销毁
– resource “sample_vm” “vm2” {
– cpu = 1 -> 无
– id = “37389411-cccf-4db6-a9df-3ff10409dd66” -> 无
– memory = 2048 -> 无
– name = “vm2” -> 无
}

计划:添加0个,更改0个,销毁3个。

│ 警告:调用读取处理程序

│与sample_storage.storage1,
│在test.tf的第18行,在资源“sample_storage”“storage1”中:
│18:资源 “sample_storage”“storage1”{

│调试消息:调用读取处理程序

│(在其他地方还有2个相似的警告)

您确定要销毁所有资源吗?
Terraform将销毁所有您管理的基础架构,如上所示。
这是不可逆的操作。只有输入“是”才能确认。

输入一个值:是

sample_vm.vm2: 正在销毁… [id=37389411-cccf-4db6-a9df-3ff10409dd66]
sample_storage.storage1: 正在销毁… [id=4087d043-32cd-4e70-b0b7-9596483a1fd5]
sample_vm.vm1: 正在销毁… [id=b33ec0b0-242f-470b-8d38-6104c90adb2c]
sample_vm.vm1: 销毁完成后1秒
sample_storage.storage1: 销毁完成后1秒
sample_vm.vm2: 销毁完成后1秒

│ 警告:调用删除处理程序

│调试消息:调用删除处理程序


│ 警告:调用删除处理程序

│调试消息:调用删除处理程序


│ 警告:调用删除处理程序

│调试消息:调用删除处理程序

销毁完成!资源:3个被销毁。

最后

因为Terraform的官方文档写得非常详细,所以光是阅读它就可以获取相当多的知识。
而且,如果遇到像这次一样简单的提供商,你可以很快地编写出来,所以希望大家都尝试一次,度过美好的Terraform生活吧!

广告
将在 10 秒后关闭
bannerAds