iT邦幫忙

2023 iThome 鐵人賽

DAY 5
0

原簡體中文教程連結: Introduction.《Terraform入門教程》


1.3.1.1. Terraform基礎概念——Provider

Terraform 被設計成一個多雲基礎設施編排工具,不像 CloudFormation 那樣綁定 AWS 平台,Terraform 可以同時編排各種雲平台或是其他基礎設施的資源。Terraform 實現多雲編排的方法就是 Provider 套件機制。

Terraform通過RPC呼叫套件,套件程式碼通過呼叫SDK操作遠程資源
圖1.3.1/1 - Terraform通 過 RPC 呼叫套件,套件程式碼通過呼叫 SDK 操作雲端資源

Terraform 使用的是 HashiCorp 自行研發的 go-plugin 庫( https://github.com/hashicorp/go-plugin ),本質上各個 Provider 套件都是獨立的執行緒,與 Terraform 執行緒之間通過 rpc 進行呼叫。Terraform 引擎首先讀取並分析用戶編寫的 Terraform 程式碼,形成一個由 data 與 resource 組成的圖(Graph),再通過 rpc 呼叫這些 data 與 resource 所對應的 Provider 套件;Provider 套件的編寫者根據 Terraform 所制定的套件框架來定義各種 data 和 resource,並實現相應的 CRUD 方法;在實現這些 CRUD 方法時,可以呼叫目標平台提供的 SDK,或是直接通過呼叫 Http(s) API 來操作目標平台。

1.3.1.1.1. 下載Provider

我們在第一章的小例子中,寫完程式碼後在 apply 之前,首先我們執行了一次 terraform initterraform init會分析程式碼中所使用到的 Provider,並嘗試下載 Provider 套件到本地。如果我們觀察執行完第一章例子的文件夾,我們會發現有一個 .terraform 文件夾,我們所使用的 UCloud Provider 套件就被下載安裝在裡面。

.terraform
└── plugins
    ├── registry.terraform.io
    │   └── ucloud
    │       └── ucloud
    │           └── 1.22.0
    │               └── darwin_amd64 -> /Users/byers/.terraform.d/plugin-cache/registry.terraform.io/ucloud/ucloud/1.22.0/darwin_amd64
    └── selections.json

有的時候下載某些 Provider 會非常緩慢,或是在開發環境中存在許多的 Terraform 項目,每個項目都保有自己獨立的套件文件夾非常浪費硬碟空間,這時我們可以使用套件暫存(PLUGIN-CACHE)。

有兩種方式可以啟用套件暫存:

第一種方法是配置 TF_PLUGIN_CACHE_DIR 這個環境變數:

export TF_PLUGIN_CACHE_DIR="$HOME/.terraform.d/plugin-cache"

第二種方法是使用 CLI 配置文件。Windows 下是在相關用戶的 %APPDATA% 目錄下建立名為 "terraform.rc" 的文件,MacOS 和 Linux 用戶則是在用戶的 home下建立名為 ".terraformrc" 的文件。在文件中配置如下:

plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"

當啟用套件暫存之後,每當執行 terraform init 命令時,Terraform 引擎會首先檢查期望使用的套件在暫存文件夾中是否已經存在,如果存在,那麼就會將暫存的套件複製到當前工作目錄下的文件夾 .terraform 內。如果套件不存在,那麼 Terraform 仍然會像之前那樣下載套件,並首先保存在套件文件夾中,隨後再從套件文件夾複製到當前工作目錄下的文件夾內 .terraform。為了盡量避免同一份套件被保存多次,只要作業系統支援,Terraform 就會使用符號連接而不是實際從套件暫存目錄複製到工作目錄。

需要特別注意的是,Windows 系統下 plugin_cache_dir 的路徑也必須使用 / 作為分隔符,應使用 C:/somefolder/plugin_cahce 而不是 C:\somefolder\plugin_cache

Terrafom 引擎永遠不會主動刪除暫存文件夾中的套件,暫存文件夾的大小可能會隨著時間而增長到非常大,這時需要手動清理。

1.3.1.1.2. 搜索Provider

想要了解有哪些被官方採用的 Provider,有兩種方法:

第一種方法是訪問 Terraform 官方 Provider 文件,該頁面中列出了主流的 Provider:

terraform.io 上的套件頁面
圖1.3.1/2 - terraform.io上的套件頁面

terraform.io 上的主流雲廠商套件
圖1.3.1/3 - terraform.io上的主流雲廠商套件

第二種方法就是前往 registry.terraform.io 進行搜索:

registry.terraform.io 上的套件頁面
圖1.3.1/4 - registry.terraform.io上的套件頁面

目前推薦在 registry 搜索 Provider,因為大量由社群開發的 Provider 都被註冊在了那裡。

registry.terraform.io的搜索頁面
圖1.3.1/5 - registry.terraform.io 的搜索頁面

Ucloud 套件主頁
圖1.3.1/6 - Ucloud套件主頁

Ucloud 套件文件
圖1.3.1/7 - Ucloud套件文件

一般來說,相關 Provider 如何宣告,以及相關 data、resource 的使用說明,都可以在 registry 上查閱到相關文件。

registry.terraform.io 不但可以查詢 Provider,也可以用來發布 Provider;並且它也可以用來查詢和發布模塊(Module),不過模塊將是我們後續篇章討論的話題。

1.3.1.1.3. Provider的宣告

一組 Terraform 程式碼要被執行,相關的 Provider 必須在程式碼中被宣告。不少的 Provider 在宣告時需要傳入一些關鍵訊息才能被使用,例如我們在第一章的例子中,必須給出訪問密鑰以及期望執行的 UCloud 區域(Region)訊息

terraform {
  required_providers {
    ucloud    = {
      source  = "ucloud/ucloud"
      version = ">=1.24.1"
    }
  }
}

provider "ucloud" {
  public_key  = "your_public_key"
  private_key = "your_private_key"
  project_id  = "your_project_id"
  region      = "cn-bj2"
}

在這段 rovider 宣告中,首先在 terraform 節的裡 required_providers 宣告了本段程式碼必須要名為 ucloud 的 Provider 才可以執行,source = "ucloud/ucloud" 這一行宣告了 ucloud 這個套件的來源位址(Source Address)。一個來源位址是全球唯一的,它指示了 Terraform 如何下載該套件。一個來源位址由三部分組成:

[<HOSTNAME>/]<NAMESPACE>/<TYPE>

HostName 是選填的,預設是官方的 registry.terraform.io,讀者也可以建立自己私有的 Terraform 倉庫。Namespace 是在 Terraform 倉庫內得到組織名,這代表了發布和維護套件的組織或是個人。Type 是代表套件的一個短名,在特定的 HostName/NamespaceType 必須唯一。

required_providers 中的套件宣告還宣告了該來源代碼所需要的套件的版本約束,在例子裡就是 version = ">=1.24.1"。Terraform 套件的版本號採用 MAJOR.MINOR.PATCH 的語義化格式,版本約束通常使用操作符和版本號表達約束條件,條件之間可以用逗號拼接,表達 AND 關聯,例如 ">= 1.2.0, < 2.0.0"。可以採用的操作符有:

  • =(或者不加 =,直接使用版本號):只允許特定版本號,不允許與其他條件合併使用
  • !=:不允許特定版本號
  • , >=, <, <=:與特定版本號進行比較,可以是大於、大於等於、小於、小於等於

  • ~>:鎖定 MAJOR 與 MINOR,允許 PATCH 號大於等於特定版本號,例如,~>0.9 等價於 >=0.9, <1.0,~>0.8.4 等價於 >=0.8.4, <0.9

Terraform 會檢查當前工作環境或是套件暫存中是否存在滿足版本約束的套件,如果不存在,那麼 Terraform 會嘗試下載。如果 Terraform 無法獲得任何滿足版本約束條件的套件,那麼它會拒絕繼續執行任何後續操作。

可以用添加後綴的方式來宣告預覽版,例如:1.2.0-beta。預覽版只能通過 "=" 操作符(或是空缺操作符)後接明確的版本號的方式來指定,不可以與 >=~> 等搭配使用。

推薦使用 ">=" 操作符約束最低版本。如果你是在編寫旨在由他人復用的模塊程式碼時,請避免使用 "~>" 操作符,即使你知道模塊程式碼與新版本套件會有不兼容。

1.3.1.1.4. 內建Provider

絕大多數 Provider 是以套件形式單獨發布的,但是目前有一個 Provider 是內建於 Terraform 主執行緒中的,那就是 terraform_remote_state data source。該 Provider 由於是內建的,所以使用時不需要在 terraform 中宣告 required_providers。這個內建 Provider 的源地址是 terraform.io/builtin/terraform

1.3.1.1.5. 多Provider實例

provider 節宣告了 ucloud 這個 Provider 所需要的各項配置。在上文的程式碼示例中,provider "ucloud"required_providersucloud = {...} 塊裡的 ucloud,都是 Provider 的 Local Name,一個 Local Name 是在一個模塊中對一個 Provider 的唯一的標識。

我們也可以宣告多個同類型的Provider,並給予不同的Local Name:

terraform {
  required_version = ">=0.13.5"
  required_providers {
    ucloudbj  = {
      source  = "ucloud/ucloud"
      version = ">=1.24.1"
    }
    ucloudsh  = {
      source  = "ucloud/ucloud"
      version = ">=1.24.1"
    }
  }
}

provider "ucloudbj" {
  public_key  = "your_public_key"
  private_key = "your_private_key"
  project_id  = "your_project_id"
  region      = "cn-bj2"
}

provider "ucloudsh" {
  public_key  = "your_public_key"
  private_key = "your_private_key"
  project_id  = "your_project_id"
  region      = "cn-sh2"
}

data "ucloud_security_groups" "default" {
  provider = ucloudbj
  type     = "recommend_web"
}

data "ucloud_images" "default" {
  provider          = ucloudsh
  availability_zone = "cn-sh2-01"
  name_regex        = "^CentOS 6.5 64"
  image_type        = "base"
}

例如上面的例子,我們宣告了兩個 UCloud Provider,分別定位在北京區域和上海區域。我們在接下來的 data 宣告中明確指定了 provider 的 Local Name,這使得我們可以在一組配置文件中同時操作不同區域、不同帳號的資源。

我們也可以使用 alias 別名來區隔同類 Provider 的不同實例:

terraform {
  required_version = ">=0.13.5"
  required_providers {
    ucloud    = {
      source  = "ucloud/ucloud"
      version = ">=1.24.1"
    }
  }
}

provider "ucloud" {
  public_key  = "your_public_key"
  private_key = "your_private_key"
  project_id  = "your_project_id"
  region      = "cn-bj2"
}

provider "ucloud" {
  alias       = "ucloudsh"
  public_key  = "your_public_key"
  private_key = "your_private_key"
  project_id  = "your_project_id"
  region      = "cn-sh2"
}

data "ucloud_security_groups" "default" {
  type = "recommend_web"
}

data "ucloud_images" "default" {
  provider          = ucloud.ucloudsh
  availability_zone = "cn-sh2-01"
  name_regex        = "^CentOS 6.5 64"
  image_type        = "base"
}

和多 Local Name 相比,使用別名允許我們區分 provider 的不同實例。terraform 節的 required_providers 中只宣告了一次 ucloud,並且在 data 中指定 provider 時傳入的是 ucloud.ucloudsh。多實例 Provider 請使用別名。

每一個不帶 alias ** 屬性的 provider 宣告都是一個預設 provider** 宣告。沒有明確指定 provider 的 data 以及 resource 都使用預設資源名第一個單詞所對應的 provider,例如,ucloud_images 這個 data 對應的預設 provider 就是 ucloudaws_instance 這個 resource 對應的預設 provider 就是 aws

假如程式碼中所有明確宣告的 provider 都有別名,那麼 Terraform 運行時會構造一個所有配置均為空值的預設 provider。假如 provider 有必填字段,並且又有資源使用了預設 provider,那麼 Terraform 會拋出一個錯誤,抱怨默認預設 provider 缺失了必填字段。(譯者:原文這裡用「抱怨」,超擬人 :D)


原簡體中文教程連結: Introduction.《Terraform入門教程》


上一篇
Day4-【入門教程】Terraform 初步體驗
下一篇
Day6-【入門教程】Terraform基礎概念—狀態管理
系列文
Terraform 繁體中文25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言