Terraform 的核心功能仰賴 Provider 與 Provisioner,理解其運作機制對於有效管理基礎設施至關重要。本文除了講解 Provider 的版本鎖定技巧與多 Provider 的組態方法外,也探討了 Terraform 的外掛化架構,讓讀者瞭解其設計理念與優勢。此外,文章也涵蓋了 Terraform 0.13 版本後 Provider 管理的重大變革,包括如何利用 required_providers 區塊精確控制 Provider 版本,以及使用本地檔案系統映象和外掛快取等機制提升效率。最後,文章也討論了 Provisioners 的使用時機以及 Local-Exec 和 Remote-Exec 的實際應用場景,並提供相關程式碼示例,讓讀者更清楚如何在 Terraform 中執行本地和遠端指令碼。

Terraform 基礎知識解析

瞭解 Terraform 版本管理

Terraform 的版本管理對於確保基礎設施的一致性和穩定性至關重要。透過 required_providers 區塊,可以為所有提供者(包括子模組中的提供者)設定版本,從而簡化複雜組態中的版本更新流程。

版本指定引數

在指定版本號時,可以使用多種引數,包括:

  • >= 1.41.0:大於或等於指定版本
  • <= 1.41.0:小於或等於指定版本
  • ~> 1.41.0:允許在 1.41.X 範圍內進行更新,適合保持在相同主版本下進行次版本更新
  • >= 1.20, <= 1.41:指定版本範圍,在 1.20 至 1.41 之間(含)

其中,~> 引數常用於保持在相同主版本下進行次版本更新,例如,若設定 ~> 1.0,則允許所有 1.x 版本的更新,但會阻止 2.0 等大版本的更新。

重點整理

  • Terraform 使用單一二進位制檔案,並透過提供者與其他服務互動
  • 可以指定提供者的版本,以確保組態的一致性

描述根據外掛的架構

Terraform 的核心元件被封裝成單一二進位制檔案,但與各提供者和 provisioner 的互動則透過外掛實作。這些外掛作為獨立程式執行,透過 RPC 介面與 Terraform 核心二進位制檔案進行通訊。

為何採用外掛架構?

HashiCorp 將提供者的程式碼從 Terraform 二進位制檔案中分離出來,形成外掛架構,這樣做的好處包括:

  • 縮小 Terraform 二進位制檔案的大小
  • 降低潛在的攻擊面
  • 簡化核心程式碼的除錯
  • 允許提供者和 provisioner 獨立開發和更新

重點整理

  • Terraform 使用外掛來實作提供者和 provisioner 的功能,而不是將它們封裝在核心二進位制檔案中
  • 這種架構提高了靈活性和可維護性

示範多提供者的使用

在實際應用中,可能會遇到需要使用多個不同提供者或多個相同提供者的情況。

使用多個不同提供者

即使是基本的組態,也可能涉及多個提供者。例如:

provider "aws" {
  region = "us-east-1"
}

resource "random_integer" "rand" {
  min = 10000
  max = 99999
}

resource "aws_s3_bucket" "bucket" {
  name = "unique-name-${random_integer.rand.result}"
  # ...
}

上述組態中使用了 Random 提供者和 AWS 提供者,分別用於生成隨機整數和建立 S3 儲存桶。

使用多個相同提供者

有時需要使用多個相同提供者的例項,例如,在不同區域建立 AWS 資源。為此,可以使用 alias 元引數為提供者指定別名:

provider "aws" {
  region = "us-east-1"
}

provider "aws" {
  region = "us-west-2"
  alias  = "west"
}

resource "aws_ec2_instance" "example-west" {
  name     = "instance2"
  provider = aws.west
  # ...
}

在上述示例中,EC2 例項將在 us-west-2 區域建立,因為在資源區塊中指定了使用 aws.west 提供者。

若未指定提供者別名,Terraform 將預設使用無別名的提供者例項。

重點整理

  • 可以在組態中使用多個不同提供者或多個相同提供者的例項
  • 使用 alias 元引數可以為提供者指定別名,以實作多例項管理

Terraform基礎知識:瞭解Provider的擷取與管理

Terraform在版本0.13的引入下,對於Provider的擷取與管理方式有了重大的改變。本章節將介紹在0.12(含)以前版本中Terraform如何與Provider互動,以及在新版本中這些功能的變化。

早期版本的Provider管理(<=0.12)

在Terraform 0.13之前的版本,Provider主要分為兩類別:由HashiCorp發布的Provider和第三方Provider。由HashiCorp發布的Provider會被自動下載,而第三方Provider則需要手動放置在指定的本機外掛目錄中。對於Windows系統,這個目錄是%APPDATA%\terraform.d\plugins;對於其他作業系統,則是~/.terraform.d/plugins

新版本的Provider管理(>=0.13)

從0.13版本開始,Terraform允許從公有或私有倉函式庫自動下載Provider。官方的公有倉函式庫位於registry.terraform.io,包含了三種型別的Provider:官方(Official)、驗證(Verified)和社群(Community)。官方Provider由HashiCorp維護,驗證Provider由其他廠商維護但經過了合作夥伴Provider的審核流程,而社群Provider則由個人發布和維護。

使用required_providers區塊新增Provider

Provider可以透過在terraform程式碼區塊內新增required_providers巢狀區塊來指定。下面是一個範例:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

在這個範例中,aws是本地名稱,用於組態中的本地參考。source引數指定了Provider的來源,可以是任何公有或私有的註冊中心,格式為hostname/organization/provider。如果省略了hostname,Terraform會預設使用公有的Terraform註冊中心registry.terraform.io

本地檔案系統映象

對於不想託管自己註冊中心的組織,可以使用source引數指向本地檔案系統映象位置。Terraform會在初始化時檢查這些位置,找到必要的Provider可執行檔,並將它們複製到.terraform目錄中。本地檔案系統映象的位置取決於作業系統型別,可以在官方Terraform檔案中找到相關資訊。

Provider可執行檔的下載與快取

對於所有版本的Terraform,Provider可執行檔可以被下載或存放在本機目錄或檔案分享中。當Terraform組態被初始化時,它會查詢組態中使用的Provider,並檢索Provider外掛,將它們儲存在當前組態資料夾中的.terraform子目錄下。

預設情況下,每個組態都會下載自己的Provider外掛副本。為了減少下載時間和提高效能,可以透過Terraform CLI組態檔案中的plugin_cache_dir設定或使用TF_PLUGIN_CACHE_DIR環境變數來啟用外掛快取。當啟用快取時,Terraform會在下載前檢查快取中是否有可用的外掛。如果找不到合適的外掛,Terraform會將外掛下載到快取和.terraform資料夾中。

禁止自動下載外掛

在某些情況下,例如斷網或高度監管的環境,您可能不希望Terraform自動下載外掛。在這種情況下,您可以提前下載所需的外掛並將它們存放在外掛目錄中。

外掛目錄的位置可以透過設定環境變數TF_PLUGIN_DIR或在初始化時使用--plugin-dir引數來組態。可以透過多次使用該引數來指定多個外掛目錄。如果設定了外掛目錄,Terraform將不會自動從網際網路下載外掛或檢查第三方外掛目錄。

內容解密:

  1. 指定本地名稱和來源:在required_providers區塊中,可以為Provider指定一個本地名稱和來源。來源可以是公有或私有的註冊中心。
  2. 使用本地檔案系統映象:對於不想託管自己註冊中心的組織,可以使用本地檔案系統映象。這需要在source引數中指定本地路徑。
  3. 啟用外掛快取:透過啟用外掛快取,可以減少下載時間和提高效能。這可以透過設定plugin_cache_dir或使用環境變數TF_PLUGIN_CACHE_DIR來實作。
  4. 禁止自動下載外掛:在某些環境下,可以透過設定外掛目錄來禁止Terraform自動下載外掛。

重點整理

  • Terraform會在初始化組態時自動從required_providers區塊中指定的來源下載外掛。
  • 私有外掛可以託管在倉函式庫中或存放在映象資料夾中。
  • 可以透過使用外掛快取或指定包含外掛的目錄來改變預設行為。

補充資訊

此圖示顯示了Terraform如何管理Provider的流程:

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Terraform Provider 版本管理與外掛架構解析

package "Terraform 工作流程" {
    actor "DevOps" as dev

    package "本地開發" {
        component [.tf 設定檔] as tf
        component [terraform.tfvars] as vars
        component [.tfstate] as state
    }

    package "Terraform Core" {
        component [Init] as init
        component [Plan] as plan
        component [Apply] as apply
        component [Destroy] as destroy
    }

    package "Providers" {
        cloud "AWS" as aws
        cloud "GCP" as gcp
        cloud "Azure" as azure
    }

    database "Remote State" as remote
}

dev --> tf : 編寫配置
tf --> init : 初始化
init --> plan : 規劃變更
plan --> apply : 執行變更
apply --> aws : 建立資源
apply --> gcp : 建立資源
apply --> azure : 建立資源
apply --> state : 更新狀態
state --> remote : 同步狀態

@enduml

此圖示說明瞭Terraform管理Provider的基本流程,包括檢查本地快取、下載外掛以及儲存到本地快取等步驟。

Terraform 基礎知識:瞭解何時使用 Provisioners 及 Local-Exec 與 Remote-Exec

在 Terraform 中,當資源被建立時,您可能需要在本地或遠端資源上執行一些指令碼或操作。Terraform provisioners 就是用來實作這一目標的工具。然而,它們在一定程度上打破了 Terraform 的宣告式和冪等模型。因為 provisioners 的執行不一定是原子性或冪等的,Terraform 無法像追蹤其他資源一樣追蹤 provisioners 的結果和狀態。因此,HashiCorp 建議僅在最後的手段中使用 provisioners。

何時使用 Provisioners

Provisioners 的使用案例包括:

  • 將資料載入到虛擬機器中
  • 為組態管理器引導虛擬機器
  • 在本地系統上儲存資料

remote-exec provisioner 允許您透過 WinRM 或 SSH 連線到遠端機器並執行指令碼。但是,這需要機器接受遠端連線。與其使用 remote-exec 將資料傳遞給虛擬機器,不如使用雲端服務提供商的工具,例如 AWS 中的 user_data 引數或 Azure 中的 custom_data

除了 remote-exec provisioner 之外,還有針對 Chef、Puppet、Salt 等組態管理器的 provisioners。它們允許您引導虛擬機器以使用您選擇的組態管理器。更好的替代方案是建立一個自定義映像,其中已經安裝了組態管理器軟體,並在啟動時使用上述資料載入選項之一向您的組態管理伺服器註冊。

您可能希望使用 local-exec provisioner 在 Terraform 組態中執行本地指令碼。在某些情況下,已經有提供所需功能的提供者,例如 Local 提供者可以與本地系統上的檔案互動。但是,在某些情況下,本地指令碼可能是唯一的選擇。

Provisioner 詳細資訊

儘管盡了最大努力,您還是決定需要使用 provisioner。Provisioner 是資源組態的一部分,可以在資源被建立或銷毀時觸發。它不能在資源被修改時觸發。

當建立時的 provisioner 失敗時,它會將資源標記為受損,因為它不知道如何在不刪除資源並重試的情況下解決問題。這種行為可以使用 on_failure 引數進行更改。當銷毀時的 provisioner 失敗時,Terraform 不會銷毀資源,並在下一次套用時再次嘗試執行 provisioner。

單個資源可以有多個 provisioners 在其組態區塊中描述。Provisioners 將按照它們出現的順序執行。

使用 Local-Exec Provisioner

resource "null_resource" "example" {
  provisioner "local-exec" {
    command = "echo Hello World!"
  }
}

#### 內容解密:
這段程式碼展示瞭如何在 Terraform 中使用 `local-exec` provisioner 執行本地命令在這個例子中 `null_resource` 被建立時將執行 `echo Hello World!` 命令

使用 Remote-Exec Provisioner

resource "null_resource" "example" {
  provisioner "remote-exec" {
    connection {
      type        = "ssh"
      host        = "example.com"
      user        = "root"
      private_key = file("~/.ssh/id_rsa")
    }
    inline = [
      "echo Hello World!",
    ]
  }
}

#### 內容解密:
這段程式碼展示瞭如何在 Terraform 中使用 `remote-exec` provisioner 透過 SSH 連線到遠端機器並執行命令在這個例子中 `null_resource` 被建立時將透過 SSH 連線到 `example.com` 並執行 `echo Hello World!` 命令

重點整理

  • Provisioners 是最後的手段。
  • remote-exec 將透過 WinRM 或 SSH 在遠端機器上執行指令碼。
  • local-exec 將在本地機器上執行指令碼。

使用 Terraform CLI(核心工作流程以外)

本章節將討論如何在標準核心工作流程之外使用 Terraform 命令列介面(CLI)。雖然 HashiCorp 將 CLI 的介紹放在核心工作流程之前可能會讓人感到困惑,但這是官方的教學順序。如果你是 Terraform 的新手,建議先閱讀 Objective 6: Navigate Terraform Workflow,然後再回來閱讀本章節。

4A:瞭解 Help 指令(terraform help)

如同許多命令列介面(CLI),Terraform 也內建了幫助系統。當你剛開始使用 Terraform 時,你會學習到主要的指令,如 initplanapplydestroy。如果你遇到不熟悉的指令或想了解更多,可以在網上搜尋相關資訊,通常會找到官方的 Terraform Commands (CLI) 頁面。

使用幫助系統

你也可以直接在命令列中使用幫助系統。執行 terraformterraform help 指令即可列出所有可用的指令。

$> terraform help
Usage: terraform [-version] [-help] <command> [args]

The available commands for execution are listed below.
The most common, useful commands are shown first, followed by
less common or more advanced commands. If you're just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.

Common commands:
    apply              Builds or changes infrastructure
    console            Interactive console for Terraform interpolations
    destroy            Destroy Terraform-managed infrastructure
    env                Workspace management
    fmt                Rewrites config files to canonical format
    get                Download and install modules for the configuration
    graph              Create a visual graph of Terraform resources
    import             Import existing infrastructure into Terraform
    init               Initialize a Terraform working directory
    output             Read an output from a state file
    plan               Generate and show an execution plan
    providers          Prints a tree of the providers used in the configuration
    refresh            Update local state file against real resources
    show               Inspect Terraform state or plan
    taint              Manually mark a resource for recreation
    untaint            Manually unmark a resource as tainted
    validate           Validates the Terraform files
    version            Prints the Terraform version
    workspace          Workspace management

All other commands:
    0.12upgrade        Rewrites pre-0.12 module source code for v0.12
    debug              Debug output management (experimental)
    force-unlock       Manually unlock the terraform state
    push               Obsolete command for Terraform Enterprise legacy (v1)
    state              Advanced state management

若要了解某個指令的詳細資訊,只需輸入 terraform commandName -help。例如,檢視 terraform plan 的幫助資訊,可以執行 terraform plan -help

$> terraform plan -help
Usage: terraform plan [options] [DIR]

Generates an execution plan for Terraform.

This execution plan can be reviewed prior to running apply to get a
sense for what Terraform will do. Optionally, the plan can be saved to
a Terraform plan file, and apply can take this plan file to execute
this plan exactly.

Options:
    -destroy           If set, a plan will be generated to destroy all resources
                       managed by the given configuration and state.
    -detailed-exitcode Return detailed exit codes when the command exits. This
                       will change the meaning of exit codes to:
                       0 - Succeeded, diff is empty (no changes)
                       1 - Errored
                       2 - Succeeded, there is a diff
    -input=true        Ask for input for variables if not directly set.
    -lock=true         Lock the state file when locking is supported.
    -lock-timeout=0s   Duration to retry a state lock.
    -var 'foo=bar'     Set a variable in the Terraform configuration. This
                       flag can be set multiple times.
    -var-file=foo      Set variables in the Terraform configuration from
                       a file. If "terraform.tfvars" or any ".auto.tfvars"
                       files are present, they will be automatically loaded.

重點整理:

Terraform 具有內建的幫助系統,可以透過命令列存取。在指令後面加上 -help 旗標,即可列印出使用說明。

4B:根據場景選擇使用 Terraform FMT 格式化程式碼

Terraform 的 fmt 指令用於重新格式化 Terraform 設定檔,以符合 Terraform 語言樣式慣例所規定的標準格式和風格。由於標準格式會隨著 HashiCorp Configuration Language 和 Terraform 的功能和語法演變而更新,因此建議在更新到新版本的 Terraform 後執行 terraform fmt

使用 terraform fmt

fmt 指令的語法為 terraform fmt [options] [DIR]。預設情況下,fmt 指令會掃描目前目錄下的 .tf.tfvars 設定檔,除非指定了 DIR 引數。該指令提供了多個選項,如 listwritediffcheckrecursive,更多資訊可以透過執行 terraform fmt -help 獲得。

場景範例:

假設你正在團隊中工作,你編寫了一個 Terraform 設定檔,內容如下:

provider "azurerm" {
  environment = "public"
}

module "vnet" {
  source              = "Azure/vnet/azurerm"
  resource_group_name = "tacos"
  location            = "westus"
  address_space       = "10.0.0.0/16"
  subnet_prefixes     = ["10.0.1.0/24", "10.0.2.0/24"]
  subnet_names        = ["cheese", "beans"]
}

這段程式碼可讀性良好且組織結構清晰。執行 terraform fmt 後,程式碼的格式將被標準化。

程式碼格式化前後對比:

執行 terraform fmt 後的程式碼將更加符合標準格式,例如:

provider "azurerm" {
  environment = "public"
}

module "vnet" {
  source              = "Azure/vnet/azurerm"
  resource_group_name = "tacos"
  location            = "westus"
  address_space       = "10.0.0.0/16"
  subnet_prefixes     = ["10.0.1.0/24", "10.0.2.0/24"]
  subnet_names        = ["cheese", "beans"]
}

#### 內容解密:

  1. Terraform FMT 的作用:Terraform FMT 用於格式化 Terraform 設定檔,使其符合官方的標準格式和風格。
  2. 使用場景:在團隊協作中,保持程式碼的一致性非常重要。使用 terraform fmt 可以自動格式化程式碼,避免格式混亂。
  3. 指令語法terraform fmt [options] [DIR],其中 [DIR] 是可選的,用於指定需要格式化的目錄。
  4. 常見選項-list-write-diff-check-recursive 等,用於控制格式化的行為。
  5. 實際操作:在終端機中執行 terraform fmt,即可自動格式化目前目錄下的所有 .tf.tfvars 檔案。