原簡體中文教程連結: Introduction.《Terraform入門教程》
Terraform 被設計成一個多雲基礎設施編排工具,不像 CloudFormation 那樣綁定 AWS 平台,Terraform 可以同時編排各種雲平台或是其他基礎設施的資源。Terraform 實現多雲編排的方法就是 Provider 套件機制。
圖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 來操作目標平台。
我們在第一章的小例子中,寫完程式碼後在 apply 之前,首先我們執行了一次 terraform init
。terraform 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 引擎永遠不會主動刪除暫存文件夾中的套件,暫存文件夾的大小可能會隨著時間而增長到非常大,這時需要手動清理。
想要了解有哪些被官方採用的 Provider,有兩種方法:
第一種方法是訪問 Terraform 官方 Provider 文件,該頁面中列出了主流的 Provider:
圖1.3.1/2 - terraform.io上的套件頁面
圖1.3.1/3 - terraform.io上的主流雲廠商套件
第二種方法就是前往 registry.terraform.io 進行搜索:
圖1.3.1/4 - registry.terraform.io上的套件頁面
目前推薦在 registry 搜索 Provider,因為大量由社群開發的 Provider 都被註冊在了那裡。
圖1.3.1/5 - registry.terraform.io 的搜索頁面
圖1.3.1/6 - Ucloud套件主頁
圖1.3.1/7 - Ucloud套件文件
一般來說,相關 Provider 如何宣告,以及相關 data、resource 的使用說明,都可以在 registry 上查閱到相關文件。
registry.terraform.io 不但可以查詢 Provider,也可以用來發布 Provider;並且它也可以用來查詢和發布模塊(Module),不過模塊將是我們後續篇章討論的話題。
一組 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
/Namespace
下 Type
必須唯一。
required_providers
中的套件宣告還宣告了該來源代碼所需要的套件的版本約束,在例子裡就是 version = ">=1.24.1"
。Terraform 套件的版本號採用 MAJOR.MINOR.PATCH 的語義化格式,版本約束通常使用操作符和版本號表達約束條件,條件之間可以用逗號拼接,表達 AND 關聯,例如 ">= 1.2.0, < 2.0.0"。可以採用的操作符有:
, >=, <, <=:與特定版本號進行比較,可以是大於、大於等於、小於、小於等於
Terraform 會檢查當前工作環境或是套件暫存中是否存在滿足版本約束的套件,如果不存在,那麼 Terraform 會嘗試下載。如果 Terraform 無法獲得任何滿足版本約束條件的套件,那麼它會拒絕繼續執行任何後續操作。
可以用添加後綴的方式來宣告預覽版,例如:1.2.0-beta
。預覽版只能通過 "=" 操作符(或是空缺操作符)後接明確的版本號的方式來指定,不可以與 >=
、~>
等搭配使用。
推薦使用 ">=" 操作符約束最低版本。如果你是在編寫旨在由他人復用的模塊程式碼時,請避免使用 "~>" 操作符,即使你知道模塊程式碼與新版本套件會有不兼容。
絕大多數 Provider 是以套件形式單獨發布的,但是目前有一個 Provider 是內建於 Terraform 主執行緒中的,那就是 terraform_remote_state
data source。該 Provider 由於是內建的,所以使用時不需要在 terraform 中宣告 required_providers
。這個內建 Provider 的源地址是 terraform.io/builtin/terraform。
provider
節宣告了 ucloud
這個 Provider 所需要的各項配置。在上文的程式碼示例中,provider "ucloud"
和 required_providers
中 ucloud = {...}
塊裡的 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 就是 ucloud
,aws_instance
這個 resource 對應的預設 provider 就是 aws
。
假如程式碼中所有明確宣告的 provider 都有別名,那麼 Terraform 運行時會構造一個所有配置均為空值的預設 provider。假如 provider 有必填字段,並且又有資源使用了預設 provider,那麼 Terraform 會拋出一個錯誤,抱怨默認預設 provider 缺失了必填字段。(譯者:原文這裡用「抱怨」,超擬人 :D)
原簡體中文教程連結: Introduction.《Terraform入門教程》