Terraform 新手介紹 - 一篇立刻搞懂 Terrafrom 的核心與基礎元件
介紹
Terraform 是由 HashiCorp 開發的一種IaC管理工具,其誕生的原因來自現代 IT 基礎設施管理中對自動化、可重複性和跨平台支持的需求。隨著雲計算的普及,企業的基礎設施變得越來越動態,管理多雲環境(如 AWS、Azure、Google Cloud)和本地數據中心的資源變得複雜且容易出錯。傳統的手動配置方式不僅效率低,還容易導致環境之間的不一致。
Terraform 的產生主要為了解決以下需求:
- 基礎設施即代碼 (IaC):提供一種編程式方法來定義和管理基礎設施,使得資源配置版本化、可審核,並易於復現。
- 多雲支持:允許用戶通過單一工具管理不同雲平台和供應商的資源,避免過度依賴單一供應商。
- 可重複性和自動化:使用代碼編寫一次配置,無論是測試環境還是生產環境都可以一致地部署資源。
- 依賴管理:Terraform 能夠分析基礎設施資源之間的依賴關係,按正確順序創建或刪除資源,減少人為錯誤。
因此,Terraform 的出現使企業能夠更高效地管理和運營現代化的基礎設施,同時加強了敏捷性和穩定性。使用Terrafrom我們就不需要手動去建立或是刪除某個資源,只需要寫好腳本,就可以在不同的環境中部署相同的資源。
IaC 工具差異
講到IaC工具,不免也會聽到Ansible或是Chef等等,那這些工具與Terraform有什麼不同呢?
Ansible、Chef 和 Terraform 都是基礎設施即代碼(IaC)工具,但它們的設計目標和應用場景有很大的不同。
以下是 Terraform 與 Ansible 和 Chef 的主要差異:
工具類型和功能範圍
Terraform
:- 是一種宣告式的基礎設施編排工具,專注於資源的創建、更新和銷毀。
- 它的核心用途是管理基礎設施(如雲資源、網絡設置等),用戶定義目標狀態。
- 例子:適用於創建和管理基礎設施資源(如虛擬機、網絡設置、存儲桶等),並在多雲環境中實現統一的基礎設施管理。
Ansible
和Chef
:- 主要是配置管理工具,用於安裝軟體、管理系統配置以及持續性地維護應用程序的狀態。
- 它們的作用在於配置已經存在的資源,而不是創建資源。
- 例子:更適合於配置操作系統、部署應用程序、安裝和維護軟體(如設置服務器的防火牆規則或安裝特定版本的軟件包)。
宣告式 vs 指令式
Terraform
:- 是宣告式的,用戶只需定義基礎設施的最終狀態,具體如何執行由 Terraform 處理。
Ansible
和Chef
:- 通常是指令式的,用戶需要明確定義執行的步驟
- 例如「先安裝 Nginx,然後修改配置檔,再啟動服務」。
- 這對於複雜的操作流程更為靈活,但需要更多的細節管理。
Terraform 流程
指令主要有以下幾個:
terraform init
:初始化工作目錄,下載 provider 插件。terraform validate
:驗證配置文件的語法和邏輯錯誤。terraform fmt
:格式化配置文件,使其符合 Terraform 的標準格式,這個指令會幫你重新排版。terraform plan
:生成計劃,可以預覽將要執行的操作。但是不會真正創建或更新基礎設施。terraform apply
:應用計劃,創建或更新基礎設施。terraform destroy
:刪除基礎設施,通常用於測試環境的清理。
其他參數設定:
-auto-approve
- 可以自動跳過這個確認步驟,直接應用計畫,適合自動化腳本或 CI/CD 流程。
- 當你執行 terraform apply 時,Terraform 會預設要求用戶手動確認 (yes) 才會執行計畫 (plan) 中的變更。
plain.out
- 你可以先使用
terraform plan -out=plan.out
生成計畫文件,該文件詳細記錄了 Terraform 將執行的操作(例如資源新增、刪除或修改)。 - 在 terraform apply 時指定該文件(如
terraform apply plan.out
),可保證應用的計畫和生成時一致,避免非預期的更改。 - 簡單來說,我們會建議
terraform plan -out plan.out
,然後再terraform apply -auto-approve plan.out
,這樣可以確保apply
的結果是根據plan
來的。
- 你可以先使用
我們可以設定alias在 ~/.bashrc
,這樣就可以直接使用簡寫指令。
1 | # 設定alias在 vim ~/.bashrc |
核心元件
以下是對圖片中每個 Terraform 元件的簡單介紹與用途說明:
基礎篇
1. Providers (供應商)
- 用途:
- 負責與特定的雲端平台(如 AWS、Azure、Google Cloud)或服務(如 Kubernetes)互動。它提供 API 的橋樑,允許 Terraform 管理這些資源。
- 可以指定 provider 版本(
最佳實作
),以確保 Terraform 與特定版本的 provider 兼容。
- 範例:AWS Provider 幫助創建 EC2、S3 等資源。
1 | terraform { |
2. Resource (資源)
- 用途:
- 描述要管理的基礎設施資源,是 Terraform 的核心組件。例如 EC2 實例、S3 存儲桶。
- 範例:
1 | resource "aws_instance" "example" { |
3. Provisioners (配置器)
- 用途:
- 必須放在Resource裡面
- 用於在資源建立後執行額外的配置步驟(如執行腳本、安裝軟體)。
- 應該謹慎使用,因為它們違反了聲明式的理念。
- 範例:在 EC2 上執行初始化腳本:
1 |
|
4. Variables (變數)
- 用途:
- 用於設置 Terraform 模組的參數,變數可以用來傳遞值到模組中,像是指定 GCP 專案 ID 或 AWS 密鑰。
- 這種方式稱為依賴注入可以使用變數來指定開發環境、生產環境等不同的配置。
- 可以定義不同類型的變數,例如字符串、數字、布林值等。
- 範例:設定 GCP 專案 ID 和 GCS 存儲桶名稱:
variables.tf
1 | # 我們主要有三個變數名稱:GCP_PROJECT、gcs_name、location |
main.tf
1 | # 就可以直接使用 var.<variable-name> 來取得值 |
可以使用console 測試變數
1 | $ tf console |
變數型態
子串 | 寫法 | 範例 |
---|---|---|
字串 | type=string |
us-west-1 |
數值 | type=number |
1 |
布林 | type=bool |
true, false |
列表(型態) | type=list(string) |
[“a”, “b”, “c”] |
地圖(型態) | type=map(string) |
{ key = “value” } |
變數繼承順序
變數可以在多個地方定義,並且有一定的繼承順序,如下所示:
- 命令行參數(如
-var 'foo=bar'
):優先權最高,可以覆蓋所有其他值。 - Terraform 環境變數
- Terraform 可以直接讀取環境變數,尤其是與提供商相關的認證和配置。環境變數通常是以
TF_VAR_
為前綴的,後面跟變數名稱。 export TF_VAR_region="asia-northeast1"
輸入後,就可以在 Terraform 中使用var.region
來取得值。或是直接使用variable "region" {}
自動帶入。
- Terraform 可以直接讀取環境變數,尤其是與提供商相關的認證和配置。環境變數通常是以
- Terraform 變數文件
terraform.tfvars
中直接設置key=value
的方式,可以直接讀取。例如:region = "asia-northeast1"
- 使用的方式可以在
variable.tf
檔案設置如下:variable "region" { type=string }
就不用指定default了,儘管有指定也會被覆蓋掉。
- Terraform 設定文件
- 基本上就是
variable.tf
裡面我們會設定variable "region" { type=string default="asia-northeast1" }
,這樣就可以直接使用了。
- 基本上就是
5. Data Source (資料來源)
- 用途:
- 當需要使用現有基礎設施,像是現有的PVC, 子網, instance等,可以使用data source來取得資訊並引用。
- 從以下範例為例,我們使用
google_client_config
這個 data soucr 檢索當前Google Cloud 客戶端配置的相關資訊,可以取得目前正在使用的專案ID、區域、用戶帳戶等。
- 範例:
1 | # 使用 data source 取得 Google Cloud 客戶端配置 預設服務帳戶 |
如果想要取得bucket
的相關data souce則要確保name
要是存在的,否則會報錯。
1 | resource "google_storage_bucket" "bucket" { |
6. Outputs (輸出)
- 用途:定義從 Terraform 配置中輸出的值,方便其他模組或用戶查看重要信息。
- 範例:取得 data source 的內容輸出 GCP 專案 ID 和區域:
1 | # 使用 data source 取得 Google Cloud 客戶端配置 預設服務帳戶 |
7. State (狀態)
- 用途:
- Terraform 用於追蹤已管理的資源的當前狀態,並比較JSON配置文件的期望狀態,決定資源的增刪改操作。
- 管理State的最佳實踐
- 不要手動修改
tfstate
文件:tfstate
是 Terraform 管理的核心數據,手動修改可能導致狀態不一致或損壞。 - 使用遠端後端存儲 (Backend): 在團隊協作或生產環境中,將狀態文件存儲到安全的遠端後端(如 S3)是必要的。
- 啟用加密:如果使用 S3、GCS 等後端,啟用加密來保護狀態文件的敏感數據。
- 啟用版本控制:遠端後端通常支援版本控制(如 S3 的版本控制功能),可以用於恢復意外損壞的狀態文件。
- 鎖定狀態文件:使用遠端後端(如 S3 搭配 DynamoDB 或 Terraform Cloud)進行狀態鎖定,防止多用戶同時修改狀態文件。
- 敏感數據管理:
tfstate
文件可能包含敏感信息(如密碼、API 密鑰),需要妥善保護,避免公開或未加密存儲。
- 不要手動修改
- 範例:
8. Backend (遠端狀態後台)
- 用途:
- 定義 Terraform 的狀態檔案
tfstate
應存儲於哪裡,支持多用戶協作並發操作。 - 可以把
terraform.tfstate
的狀態儲存在S3, GCP等等,這樣不僅僅可以做版控,還可以協同編輯等等。
- 定義 Terraform 的狀態檔案
- 範例:
- 通常在專案裡面可以針對backend建立資料夾
/state
,然後在裡面建立backend.tf
檔案如下:
1
2
3
4
5resource "google_storage_bucket" "quick_start_gcs" {
name = "gcs-bucket-state"
location = "asia-east1"
force_destroy = true
}- 之後建立
terraform
的時候就可以指定backend如下:
1
2
3
4
5
6terraform {
backend "gcs" {
bucket = "gcs-bucket-state" # <=== 這邊要對應到上面的名稱
prefix = "terraform/state"
}
} - 通常在專案裡面可以針對backend建立資料夾
進階篇
上述都是比較基礎的元件與設定教學,接下來如果專案比較大,可能會需要進一步瞭解以下元件。
Modules
(模組):模組是可重複使用的配置單位,包含資源、變數等定義,幫助組織代碼並實現代碼重用。Dependency
(依賴):Terraform 會自動解析資源之間的依賴關係,並按正確順序創建或刪除資源。Lifecycle
(生命週期):控制資源的創建、更新和刪除行為,如何處理資源的變更。Workspaces
(工作區):用於管理不同環境(如開發、測試、生產)的狀態和配置,避免混淆和衝突。
1. Modules (模組)
-
用途:
- 模組是可重複使用的配置單位,包含資源、變數等定義,幫助組織代碼並實現代碼重用。
- 通常會建立一個資料夾稱作
./modules
在裡面建立相關的檔案。
-
範例:
./modules/main.tf 跟一般設定沒兩樣,設定 resource
1 | resource "google_storage_bucket" "shannon_gcs" { |
./modules/variable.tf 跟一般設定沒兩樣,設定 variable
1 | variable "gcs_name" { # 可以透過外部module引入變數 |
./modules/output.ts 注意的是儘管使用 apply 也不會印出,除非在main.tf
設定output
1 | output "bucket_name" { |
main.tf 真正執行terraform apply
的檔案
1 | # 引用 module |
2. Dependency (依賴)
- 用途
- 模組之間可以相互依賴,也可以獨立存在,也因死兩個模組之間可能產生存在先後的關係。
- 模組之間的依賴關係可以透過 resource 或 module 來建立。
- Terraform 會自動計算依賴關係,確定模組的創建以及銷毀的順序。
- 範例
- 可以透過
depends_on
參數來強制指定模組之間的依賴關係。 - 使用
depends_on
參數時,需特別注意相互依賴可能會產生的循環依賴問題(產生雞生蛋、蛋生雞問題)。
- 可以透過
./main.tf
可以使用 depends_on
讓 depend-1
先建立,再建立 depend-2
,在銷毀的時候則是先 depend-2
再 depend-1
1 | module "shannon-module-depend-1" { |
3. Lifecycle (生命週期)
- 用途
- Terraform Lifecycle 指的是指令碼的執行順序和執行方式,主要包含了以下三個階段:
create
、update
、destroy
。 - 使用者可以透過指定自定義的執行順序來控制 Lifecycle 的執行方式,通常是用來更好的控制資源的創建與更新策略。
- Terraform Lifecycle 指的是指令碼的執行順序和執行方式,主要包含了以下三個階段:
- 範例
- 常見Lifecycle屬性:
避免被刪除:prevent_destroy
⇒ 可以避免發生刪除忽略改變:ignore_changes ⇒ 例如當label有更新時,我們可以忽略更新1
2
3
4
5
6
7
8
9
10
11
12
13
14resource "google_storage_bucket" "shannon-lifecycle-gcs" {
name = "shannon-lifecycle-test"
location = var.location
force_destroy = true
labels = {
lifecycle : "original2"
}
# lifecycle 如果把labels添加在ignore_change的話labels儘管有更新有不會發生更新
lifecycle {
prevent_destroy = true
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14resource "google_storage_bucket" "shannon-lifecycle-gcs" {
name = "shannon-lifecycle-test"
location = var.location
force_destroy = true
labels = {
lifecycle : "original2"
}
# lifecycle 如果把labels添加在ignore_change的話labels儘管有更新有不會發生更新
lifecycle {
ignore_changes = [ labels ]
}
}
- 常見Lifecycle屬性:
4. Workspaces (工作區)
- 概念
- 可視為將不同環境的相關設定集合在一起的容器,例如開發、測試、生產等。
- Terraform 可以透過 workspace 切換不同的環境,讓使用者在同一個 Terraform 設定檔中管理多個環境。
- 注意事項
- 不同 Workspace 之間的資源是獨立的,不會互相干擾。
- 在不同 Workspace 中使用相同的變數時,需要在每個 Workspace 中單獨設置。
- 最佳實踐
- 我們擔心使用相同的部署檔在不同的workspace很有可能會因為在別的資源產生相同名稱的資源,導致切換成別的workspace時產生資源發生衝突
- 我們可以透過
${terraform.workspace}
來根據當前的space來使用 - 但是最佳實踐還是每個專案都有自己的設定檔比較好
- 範例
Demo 步驟
1 | # 創建dev/prd兩個工作空間workspace |
最佳實踐參考
- 可以遵循 Terraform 最佳實踐來建立專案,或是參考qwedsazxc78/terraform-project-best-practice。
- 基礎架構有很強的依賴性,最好使用數字標號,告知他們建立的順序
總結
在了解元件之後,元件與元件之間的關係我覺得可以餐這以下這個圖片這樣說明: