跳轉到

10.2 治理與擴展:IaC、多環境與團隊協作#

本節目標#

個人專案可以「在手動 dashboard 點一點」過日子,但只要專案要活超過一年、或團隊超過一個人,手動設定就會變成不可理解的黑盒。本節引進三個治理工具:基礎設施即程式碼(IaC) 把設定變成可版本控制的程式碼、多環境(dev/staging/prod) 把風險與實驗隔離、Cloudflare Access 把管理介面鎖在身分驗證之後。

IaC:設定即程式碼#

基礎設施即程式碼(Infrastructure as Code)的核心主張是一條公式:

\[ \text{系統狀態} = f(\text{宣告式定義檔}),\quad\text{而不是一次性手動動作的歷史堆疊} \]

你宣告「我想要一條 DNS 記錄、一條 WAF 規則、一個 Worker」,工具負責計算從現在狀態到目標狀態的差異(diff)並執行。這把 dashboard 的黑盒操作轉成三個工程屬性:

屬性 意義
版本控制 每一次設定變更都有 git commit 與作者
可重現 新環境或新公司 = 重新 apply 同一組檔案
可審查 設定變更走 PR review,就像程式碼

反面教材是「dashboard 治理」:三年後沒有人記得那條看似無用的 WAF 規則是誰為何建的;離職工程師把 production zone 的記錄刪掉,只是因為「看起來沒在用」。

Terraform 與 Cloudflare Provider#

Terraform 是業界最主流的 IaC 工具,Cloudflare 官方維護 provider(cloudflare/cloudflare)。一個典型的最小化設定:

# main.tf
terraform {
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 5.0"
    }
  }
}

provider "cloudflare" {
  api_token = var.cloudflare_api_token
}

# DNS 記錄:宣告想要的狀態
resource "cloudflare_dns_record" "www" {
  zone_id = var.zone_id
  name    = "www.example.com"
  type    = "CNAME"
  content = "example.pages.dev"
  proxied = true
}

# 安全標頭改寫規則
resource "cloudflare_ruleset" "security_headers" {
  zone_id = var.zone_id
  name    = "security headers"
  kind    = "zone"
  phase   = "http_response_headers_transform"
  rules {
    action = "rewrite"
    action_parameters {
      headers {
        name       = "Strict-Transport-Security"
        operation  = "set"
        value      = "max-age=31536000; includeSubDomains"
      }
    }
    expression = "true"
  }
}

工作流程是三個動詞:

terraform init      # 安裝 provider、初始化 state
terraform plan      # 顯示「將執行的差異」——永遠先看這個
terraform apply     # 經確認後執行

State 是 Terraform 記住「現在真實世界長什麼樣」的資料結構。實務鐵律:

1. state 是機密(含資源 ID),放遠端 backend(如 R2 一個指定前綴的 bucket),
   不要只留在本機。
2. state 一旦漂移(人工改了 dashboard),plan 就會失準——先再 import 回來。
3. 永遠先 plan 後 apply;plan 輸出直接貼進 PR 作審查依據。

覆蓋範圍與限制#

Cloudflare provider 幾乎涵蓋 zone、DNS、WAF、Workers、R2、D1、Access;但 Pages 的 deployments 通常仍走 wrangler/CI(IaC 管「配置」,CI 管「建置產物」),兩者分工。

多環境:dev / staging / prod#

多環境的意義#

程式碼變更是可逆的(git revert),資料與設定變更不可逆:prod 的 D1 少一行 schema migration、一走查 index 就回不去。多環境的用意是把「危險的自由」限制在非 prod 環境:

dev      → `*.dev.example.com`      本地 wrangler dev +workers,隨手改
staging  → `staging.example.com`    與 prod 同配置、測試資料、PR 部署自動進
prod     → `example.com`            只有 main 分支 merge 後可手動/自動部署

用 wrangler 的 environments#

wrangler 內建環境概念:同一個 wrangler.toml 裡對同一個 Worker 宣告多個部署目標。

name = "my-api"
main = "src/index.ts"

[env.staging]
name = "my-api-staging"
route = { pattern = "staging.example.com/*", zone_name = "example.com" }
d1_databases = [{ binding = "DB", database_name = "my-db-staging", database_id = "..." }]

[env.production]
name = "my-api"
route = { pattern = "api.example.com/*", zone_name = "example.com" }
d1_databases = [{ binding = "DB", database_name = "my-db", database_id = "..." }]
npx wrangler deploy --env staging
npx wrangler deploy --env production

注意每個環境都有自己的 D1 資料庫——多環境不只是「多個 URL」,而是整組資源隔離。連 staging 的測試資料永遠污染不到 prod。

分支策略#

一條簡單就夠用的規則(GitHub Flow 變體):

feature branch → PR → 自動部署到 staging(preview)→ review → merge to main → 自動部 deploy prod

Cloudflare Pages 原生支援 Preview deployments(每個 PR 一個 *.pages.dev 預覽網址),把 staging 的性質「策略內建」在 PR 流程中;Workers 則可用 versions upload 得到一次性的 preview URL 讓人肉眼驗收。

基律理論:CI 負責「易變的部署」,IaC 負責「持久的設定」。建置產物(每天在變)走 pipeline,環境組態(數月不變)走 Terraform——這就是治理的分界線。

Cloudflare Access:鎖住 dashboard 與內部工具#

DevOps 最常忽視的攻擊面是管理介面本身:Cloudflare dashboard 若只靠一組帳號密碼,就只是一道薄薄的門檻。Cloudflare Access(Zero Trust 產品線)用反向代理 + 身分識別把 dashboard、外部管理控制台、甚至內部 preview URL 全部鎖起來:

使用者 → 經 Access 代理的 URL → IdP 驗證(Google/GitHub/OKTA…)→ 檢查 policy → 才准內容

設定策略的一句話版本:dashboard 存取必須綁定公司網域或 MFA 群組——就算有人拿到密碼也進不來。內部工具(如長期存在的 admin 頁)建議也一律套上 Access——把「內網」的隱性信任,轉為「邊緣上的零信任顯式驗證」。

Drift 檢測:地球不會照你的 plan 走#

IaC 完成的第一週,現實就會開始背離你的 state——有人手動 dashboard 加了一條記錄、另一個團隊用 CLI 建了 Worker。這叫 configuration drift。治理的最後一塊:偵測並修正漂移。

三個層次:

層次 做法
偵測 每日 CI 跑 terraform plan -detailed-exitcode;exit 2 = 有差異
告警 差異出現即 open 一個 issue 並 Slack 通知,見 10.1 的告警法則
治本 收緊 dashboard 權限(Access + 最小權限 API Token),讓「繞過 IaC 的改動」變成例外而非日常
# .github/workflows/drift.yml 的核心步驟
- run: terraform plan -detailed-exitcode -no-color
  id: plan
# exit code 2:有未計畫的變更 → 開 issue

更深一層的理論:單一真相來源(Single Source of Truth)。當同一件事可以有兩個真相(dashboard vs Terraform),提供出來的寬鬆性必被用掉;治理就是讓「唯一的改動渠道」成本最低、其他渠道最麻煩。

小結與練習#

回顧#

  • IaC 把 dashboard 黑盒轉成可版本控制、可審查、可重現的程式碼;Terraform + Cloudflare provider 是標準武器。
  • plan → apply 的工作流,配合遠端 state 與 plan 貼進 PR 是兩條鐵律。
  • 多環境(wrangler env.*)的本質是整組資源隔離:連資料庫都分開。
  • 分支策略:feature PR → staging preview → merge main → prod。
  • Access 把管理面鎖在身分驗證之後;Drift 檢測讓「手動改動」變成會被抓到的例外。
  • 治理的本體是「單一真相來源」:讓正確的事變成最容易的事。

練習#

  1. 為你目前專案挑 5 項在 dashboard 建的設定,用 Terraform import 進 state,然後 terraform plan 應顯示無差異。
  2. 寫含 staging 與 production 兩個 environment 的 wrangler.toml,且各自的 D1 資料庫分開,驗證 staging 寫壞的資料不會出現在 prod。
  3. 設定一條 Access policy:只有你的 Gmail 網域 + MFA 才能進 staging.example.com。
  4. 故意在 dashboard 手動改一條 DNS 記錄,執行 terraform plan -detailed-exitcode,觀察 drift 如何被抓出,然後用 import 或覆寫修正。