iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
自我挑戰組

雲端與資料平台實戰:從抽象概念到落地技術系列 第 24

Day24 Terraform 專案架構與組態管理

  • 分享至 

  • xImage
  •  

延伸昨天的 Terraform 基礎,今天我們來談談 Terraform 的專案架構與組態管理

Terraform 在初始化階段(terraform init)會以目錄為單位匯入各種設定檔,並讀取該目錄下的 backend(儲存位置) 設定。因此,可以說 Terraform 管理資源的基本邏輯是——「一個目錄,就是一個 state(狀態) 的範圍」


📁 目錄層級的管理邏輯

在管理多環境的 Terraform 專案時,清晰的目錄結構能有效劃分邏輯邊界,避免不同環境間的設定混淆或資源衝突。一般做法是為每個環境建立獨立的目錄,每個目錄內包含完整的 Terraform 配置檔。這樣的設計不僅讓專案結構直覺易懂,也方便 CI/CD 流程針對不同環境進行自動化部署。

範例結構:

infra/
├── prod/
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   └── backend.tf
└── staging/
    ├── main.tf
    ├── variables.tf
    ├── outputs.tf
    └── backend.tf

特點:

  • infra/prodinfra/staging 各自代表一個獨立的 Terraform 專案。
  • 兩者即使內容幾乎相同,也會產生兩個獨立的 state 檔案,互不影響。
  • 目錄結構清楚劃分了環境邊界,讓多環境管理更直覺。
  • State:Terraform 的 state 記錄資源的實際狀態,確保每次執行時 Terraform 能正確掌握目前的資源情況。
  • Backend:定義 state 的儲存位置,可為本地檔案或遠端儲存(如 GCS、S3)。

🧩 多個 .tf 的讀取邏輯

目錄除了是劃分資源與狀態的基礎,也是terraform 機制的基礎,在同一個目錄底下,Terraform 不在乎檔案名稱,只要副檔名是 .tf,都會被一併讀入並整合成同一份設定

staging/
├── main.tf
├── network.tf
├── compute.tf
├── variables.tf
└── outputs.tf

Terraform 會將這些檔案內容「拼接」成完整設定,因此以下兩種結構結果相同:

# 單一檔案寫法
main.tf

# 拆檔寫法
main.tf
network.tf
compute.tf

拆分 .tf 檔案的目的只是為了維護可讀性與模組化結構,而非影響執行結果。


⚙️ 變數與 .tfvars 的搭配

若設定中使用了變數(variable),Terraform 會從以下來源讀取值:

  1. 指定的 -var-file 檔案,例如 terraform apply -var-file=staging.tfvars
  2. 目錄下的 terraform.tfvars*.auto.tfvars
  3. CLI 的 -var 參數

這樣可以透過 tfvars 區分環境設定,而不必複製整份 .tf 檔案。

範例:建立一個 GCP bucket

infra/
├── main.tf
├── variables.tf
└── staging.tfvars

variables.tf

variable "project_id" {
  description = "GCP 專案 ID"
  type        = string
}

variable "bucket_name" {
  description = "要建立的儲存桶名稱"
  type        = string
}

variable "location" {
  description = "GCP 區域"
  type        = string
  default     = "asia-southeast1"
}

main.tf

provider "google" {
  project = var.project_id
  region  = var.location
}

resource "google_storage_bucket" "example" {
  name          = var.bucket_name
  location      = var.location
  force_destroy = true
}

staging.tfvars

project_id  = "my-staging-project"
bucket_name = "staging-data-bucket"
location    = "asia-east1"

執行:

terraform apply -var-file="staging.tfvars"

若要切換到其他環境,只需建立另一份 prod.tfvars 並指定即可。


🧩 模組化設計與引用

拆分 .tf 檔案後,我們可以進一步透過模組化設計,把共用資源抽象出來:

module/
├── vpc/
├── gce/
├── loadbalancer/
└── ...

在不同環境中引用:

staging/
└── main.tf
prod/
└── main.tf

範例引用模組:

module "prod_vpc" {
  source = "../module/vpc"
  env    = "prod"
}

透過這種方式,每個環境只需維護環境特定的參數,其共用資源邏輯集中在模組中,大幅提升可維護性與一致性。

🧱 組態區塊(Configuration Blocks)

Terraform 作為宣告式組態語言,有一套固定的**組態區塊(Configuration Blocks)**語法,每個區塊負責不同的功能。官方文件提供完整參考,常用的區塊類型包括:

  • provider:定義與雲端平台或服務的連線設定。
  • resource:宣告要建立、修改或刪除的資源。
  • variable:定義可配置的參數。
  • terraform:包含全域設定,尤其用於指定 backend 等 state 管理。

範例:

terraform {
  backend "gcs" {
    bucket = "my-terraform-state"
    prefix = "prod"
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
}

variable "project_id" {
  description = "GCP 專案 ID"
  type        = string
}

resource "google_storage_bucket" "example" {
  name          = var.bucket_name
  location      = var.region
  force_destroy = true
}

每個區塊都有其語法結構與作用範圍,組合起來形成完整的 Terraform 設定。


🔹 狀態管理(State Management)

透過前面的介紹,相信你已經對部屬一組服務有大致的概念。回到開頭所說:Terraform 管理資源的核心邏輯是——「一個目錄,就是一個 state 的範圍」

State(狀態)記錄了 Terraform 所管理的資源的實際情況,相當於資源的快照,確保每次操作都基於最新狀態。Terraform 透過 backend(後端儲存位置)來保存 state,支援本地或遠端集中管理,並在每次執行時讀取,以維持環境一致性與操作安全性。

因此,狀態管理是 Terraform 使用的核心之一。

在本地開發時,每個目錄會自動將對應服務的 state 儲存在該目錄下。然而,若將 state 檔案直接上傳至 GitHub 或其他版本控制系統,會帶來以下問題:

  • 每次 Terraform 修改後回寫的 state 可能與他人修改衝突
  • 對多人協作不友善
  • 不利於管理多環境或多資源專案

解決方式是指定一個固定且集中管理的儲存位置,例如雲端儲存服務(GCS、S3 等)。對於多資源、多環境的專案而言,state 儲存與目錄設計需要謹慎規劃,以確保環境隔離、協作順暢以及資源管理一致性。

好的,我幫你補上示意圖說明,讓「本地 state vs 遠端 backend」的差異更直觀。文字版示意如下,可直接放入教學文件:


🔹 State 儲存方式示意

1️⃣ 本地 state (Local)

infra/
├── staging/
│   ├── main.tf
│   └── terraform.tfstate   <-- 本地儲存
└── prod/
    ├── main.tf
    └── terraform.tfstate   <-- 本地儲存

特點:
- 每個目錄各自管理自己的 state
- 適合單人開發或測試環境
- 多人協作容易衝突
2️⃣ 遠端 state (Backend)

infra/
├── staging/
│   └── main.tf
└── prod/
    └── main.tf

Backend (GCS / S3 / Terraform Cloud / etc.)
└── terraform.tfstate      <-- 集中管理

特點:
- State 統一存放在遠端儲存
- 多人協作安全可靠
- 支援鎖定(state locking),避免衝突
- 適合多環境、多資源專案

💡 小結:

  • 本地 state:簡單、快速,但協作困難
  • 遠端 backend:初期設定稍複雜,但適合團隊與多環境管理,推薦在生產環境使用

📖 推薦的練習教程

在系列文中,並未提供從零開始一步步部屬服務的「Hello World」式教程,原因是篇幅有限。重點放在透過概念與實作經驗,理解 Terraform 的運作邏輯與最佳實踐。

對於 Terraform,大多數教程都要求使用雲端服務(如 GCP、AWS 等)進行練習。但在官方教程中,也提供了一種利用 Docker 的方式。雖然較抽象,但從概念上來說,container 就是最小的虛擬化環境,透過 Docker 在本機運行,就可以模擬雲端環境進行 Terraform 練習,對於不方便申請雲端帳號或資源的使用者,是一個很好的替代方案。

官方教程連結:Terraform Docker Tutorial

範例設定如下:

terraform {
  required_providers {
    docker = { 
      source  = "kreuzwerker/docker"
      version = "~> 3.0.1"
    }
  }
}

provider "docker" {} # 在雲端情境下可替換成 GCP 或 AWS provider

resource "docker_image" "nginx" {  # 對應雲端時為作業系統映像
  name         = "nginx:latest"
  keep_locally = false
}

resource "docker_container" "nginx" {  # 對應雲端時為 VM(GCE/EC2)
  image = docker_image.nginx.image_id
  name  = "tutorial"
  ports {
    internal = 80
    external = 8000
  }
}

這個練習可以讓你在本地環境就能完整體驗 Terraform 的工作流程:初始化、計畫、套用與管理資源,而無需實際操作雲端平台。完成後,你對於多環境管理、state、backend 與模組化設計的概念都能更有感。


📌 今日小結

今天介紹了 Terraform 的檔案架構、狀態管理與練習方式,幫助建立多環境管理與模組化設計的基礎概念。

內容:

  • 目錄與 State

    • 每個目錄對應一份獨立的 state 檔案,管理各環境的資源。
    • 清楚的目錄結構讓多環境治理與 CI/CD 流程更直覺。
  • 多檔案讀取與模組化

    • 同一目錄下所有 .tf 檔案會被整合成單一設定,不影響執行結果。
    • 拆分檔案提升可讀性,模組化設計讓資源邏輯能重用且維持一致性。
  • 組態區塊(Configuration Blocks)

    • 今天認識了常用的區塊:providerresourcevariableterraform(backend)。
    • 每個區塊都有特定功能,組合後形成完整 Terraform 設定。
  • 狀態管理(State & Backend)

    • 本地 state 適合單人或測試環境,但多人協作容易衝突。
    • 遠端 backend 可以集中管理 state,支援鎖定與多環境協作,是生產環境的推薦做法。
  • 練習方式

    • 可以使用 Docker 在本機模擬雲端環境,完整體驗 Terraform 流程。
    • 官方 Docker 教程提供範例,方便熟悉初始化、計畫、套用與資源管理。

今天我們聚焦於組態區塊、目錄結構與狀態管理等核心細節。在 Terraform 的世界中,每個資源都是一份設定檔,目錄則用來分組管理,就像 Linux 的「everything is a file」理念。掌握這種結構化管理,能讓多環境專案更清晰、模組化設計更可靠,也為狀態管理與協作打下穩固基礎。

期望透過今天的內容,大家能熟悉 Terraform 模組化與目錄管理的實務操作。

謝謝大家的閱讀,我們明天見!


上一篇
Day23 Terraform 基礎介紹
系列文
雲端與資料平台實戰:從抽象概念到落地技術24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言