版本管理策略簡介
發佈軟體是軟體開發生命週期 (SDLC) 中風險最高的活動之一。為了最小化停機時間和錯誤影響,架構師會使用各種部署策略。在 Google Cloud 中,Cloud Deploy、GKE 和 Cloud Run 等工具為這些模式提供了內建支援。
對於 GCP Professional Cloud Architect 來說,目標是選擇最能在風險、成本和複雜性之間取得平衡的策略。
白話文解釋部署策略 (Deployment Strategies)
比喻 1 — 橋樑建設 (藍綠部署 Blue-Green)
想像你有一座舊橋(藍色)。你不是在汽車通行時維修它,而是在它旁邊建造一座全新的橋(綠色)。一旦新橋經過測試並準備就緒,你只需更改路標(流量切換 Traffic Shifting)指向新橋即可。如果新橋開始搖晃,你就把路標改回舊橋(回滾 Rollback)。
比喻 2 — 煤礦坑裡的金絲雀 (金絲雀發佈 Canary)
在過去,礦工會帶一隻金絲雀進入煤礦坑。如果金絲雀停止鳴叫(因瓦斯中毒死亡),礦工就知道進入礦坑對人類來說是不安全的。金絲雀發佈 (Canary Release) 就像是將 5% 的使用者(金絲雀)導向新版本。如果他們沒有遇到錯誤,你再慢慢讓其餘 95% 的使用者進入。
比喻 3 — 旋轉門 (滾動更新 Rolling Update)
滾動更新 (Rolling Update) 就像一棟帶有旋轉門的大型建築。你不會為了讓新的人進來而清空整棟建築,而是一個人接一個人、或一組接一組地替換。在任何特定時間,建築物大部分都是滿的,但裡面人的「版本」正在慢慢改變,直到每個人都變成「版本 2.0」。
將部分網路流量從一個版本的應用程式重新導向到另一個版本的過程,通常由負載平衡器 (Load Balancer) 或服務網格 (Service Mesh,如 Istio/Anthos Service Mesh) 進行管理。
部署策略比較
1. 藍綠部署 (Blue-Green Deployment)
- 運作方式: 兩個相同的環境(藍色 = 目前版本,綠色 = 新版本)。一次性 100% 切換流量。
- 優點: 立即回滾,零停機時間。
- 缺點: 成本高昂(需要 2 倍資源),資料庫同步複雜。
2. 金絲雀發佈 (Canary Deployment)
- 運作方式: 將新版本部署到一小部分執行個體。逐漸增加流量(例如:5% -> 20% -> 50% -> 100%)。
- 優點: 爆炸半徑 (Blast radius) 最小,允許透過真實使用者進行實地測試。
- 缺點: 需要進階監控來偵測小規模金絲雀組中的問題。
3. 滾動更新 (Rolling Update)
- 運作方式: 遞增地用新版本替換舊版本的執行個體。這是 GKE 和受管理執行個體群組 (MIG) 的預設方式。
- 優點: 成本效益高(無需額外資源),簡單易行。
- 缺點: 無法輕鬆地「立即」回滾所有流量;一段時間內可能會有兩個版本同時運作。
GCP 上的版本管理
- Cloud Deploy: 一項用於持續交付 (CD) 的代管服務,支援 GKE、Cloud Run 和 GKE Enterprise 的金絲雀和藍綠策略。
- Global Load Balancer: 使用加權後端群組 (Weighted Backend Groups) 在「藍色」和「綠色」執行個體群組或服務之間切換流量。
- 功能標籤 (Feature Flags): 使用 ConfigCat 等工具或自定義邏輯來啟用/停用功能,而無需重新部署程式碼(暗地發佈 Dark Launching)。
架構師洞察: 在 PCA 考試中,如果情境強調**「零停機時間」和「立即回滾」高於一切,則答案是藍綠部署**。如果強調**「在生產環境中測試」和「最小化新功能的風險」,則答案是金絲雀發佈**。 ::
Cloud Deploy + Cloud Build 流水線
Cloud Build 負責建置/測試/打包階段,而 Cloud Deploy 負責推進/發布/驗證/回滾階段。兩者組合起來,是 GKE、GKE Enterprise 和 Cloud Run 上 GCP 標準的 CI/CD 流水線。
流水線結構
- Cloud Build trigger 在
git push到main(或 tag)時觸發 — 透過cloudbuild.yaml執行pnpm test、docker build、gcloud artifacts docker push到 Artifact Registry。 - Cloud Build 接著呼叫
gcloud deploy releases create rel-${SHORT_SHA} --delivery-pipeline=web-pipeline --region=us-central1 --images=app=us-central1-docker.pkg.dev/$PROJECT/web/app:$SHORT_SHA。 - Cloud Deploy 接手 release,依照
clouddeploy.yaml中定義的targets(dev→staging→prod)推進,套用deployParameters.strategy中宣告的策略(standard、canary或blueGreen)。
Canary 策略宣告
strategy:
canary:
runtimeConfig:
kubernetes:
serviceNetworking:
service: web-svc
deployment: web-deploy
canaryDeployment:
percentages: [10, 25, 50]
verify: true
設定 verify: true 後,Cloud Deploy 會在每個百分比階段之間執行一個 verify job(由 Skaffold 定義的容器)。若 verify 失敗,流水線會停止,可用 gcloud deploy rollouts retry 或 gcloud deploy rollbacks create 進行恢復。
為什麼要把 Build 和 Deploy 分開?
Cloud Build 無狀態且短暫 — 每個 step 都重新開始,這對可重現的建置很理想,但對追蹤「哪個 artifact 在哪個環境」非常糟糕。Cloud Deploy 維護交付流水線狀態(哪個 release 在哪個 target、rollout 歷史、核准門檻),讓你獲得「為部署而生的 Git」模型。在 PCA 考試中,若題目描述「每次 commit 都會建置,但只有某些 commit 會被推進到 prod」,答案就是 Cloud Build → Cloud Deploy,並在 prod target 上加上手動核准。
Cloud Run 的流量切分
Cloud Run 的 revisions 不可變,revision 之間的流量由 gcloud run services update-traffic 的 --to-revisions 旗標控制。這是 GCP 中最簡單的 canary 介面 — 不需要 Load Balancer、不需要 Service Mesh、也不需要 MIG。
Tag 式 canary 樣式
# 部署新 revision 但 0% 流量
gcloud run deploy web --image=... --no-traffic --tag=canary --region=us-central1
# 透過 tagged URL 直接測試 canary
curl https://canary---web-abc123-uc.a.run.app/healthz
# 送 5% 的生產流量
gcloud run services update-traffic web \
--to-revisions=web-00042-abc=5,web-00041-def=95 \
--region=us-central1
關鍵行為
- Revisions 帶版本(
web-00042-abc);流量百分比指派給 revision,而非 image。 - Tags 建立穩定的子網域(
canary---web-...),讓內部 QA 可以打到特定 revision,不會影響LATEST流量切分。 --no-traffic部署 revision 但不路由任何生產流量 — 適合「凌晨 3 點部署、上班時間再 ramp 上流量」的工作流程。gcloud run services update-traffic --to-latest是緊急按鈕:5 秒內把 100% 流量切回上一個穩定 revision(回滾)。
把 Cloud Run revision tagging 和 Cloud Deploy 自訂 targets 組合起來,就能得到完全代管的 canary:Cloud Deploy 推一個標記為 canary 的 revision、執行 verify、verify 通過後才下 --to-revisions=canary=100。如此一來,你不必在 shell script 裡自己手刻流量數學。
GKE 滾動 vs 透過 Anthos Service Mesh 的藍綠
GKE 原生 Deployment 資源預設為 RollingUpdate(maxSurge=25%、maxUnavailable=25%)。這便宜,但會造成混合版本同時服務請求數分鐘 — 對 stateful 或 session-pinned 工作負載而言無法接受。
滾動更新(原生)
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
優點:不需要額外的 replica。缺點:無法立即回滾 — 必須再觸發一次滾動更新切回前一個 image,耗時數分鐘。
透過 Anthos Service Mesh (ASM) 的藍綠
跑兩個 Deployment 物件(web-blue、web-green),同一個 Service 後面,用 ASM 的 VirtualService 加 weight 欄位來原子性地切換流量:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
http:
- route:
- destination: { host: web, subset: blue }
weight: 0
- destination: { host: web, subset: green }
weight: 100
一次 kubectl apply 就在一次 control-plane reconciliation cycle 內完成流量切換(多數 cluster 上 < 1 秒)。回滾就是反向 kubectl apply。
ASM 獨家絕招
- 基於 Header 的 canary:只將帶有
x-canary: true的請求路由到green,公開流量不受影響 — 適合內部 dogfooding。 - Mirror traffic:100% 流量送到 blue,但同時影子複製到 green 做負載測試而不影響使用者(
mirror: { host: web, subset: green })。 - Outlier detection:ASM 自動將 5xx 超過閾值的 green pods 剔除,在藍綠之上提供 circuit-breaking。
PCA 考試重點:若題目提到「基於 header 路由」或「將生產流量鏡射到新版本」,答案是 Anthos Service Mesh,而非原生 GKE。
GKE 原生 Deployment 資源無法做真正的藍綠 — 它只支援 RollingUpdate 和 Recreate。要在 GKE 上達成原子流量切換,必須加上 Anthos Service Mesh(Istio VirtualService)或 Cloud Deploy 的 blueGreen 策略(底層幫你管理兩個 Service 物件)。單純的 kubectl 無法做到。
透過 Global LB 加權後端服務做 A/B 測試
當工作負載跑在 MIG、Cloud Run 或 GKE 後面接 Global External Application Load Balancer 時,根本不需要 service mesh — LB 本身就在 URL map 層支援加權流量切分。
URL map 加權路由
defaultRouteAction:
weightedBackendServices:
- backendService: projects/$P/global/backendServices/web-blue
weight: 90
- backendService: projects/$P/global/backendServices/web-green
weight: 10
- Weights 是整數(通常 0-1000);LB 會正規化成百分比。
- 更新會在 30-60 秒內透過 Google 的 Envoy 機群全球生效。
- 成本:零 — 切分本身不額外計費,只算標準 LB forwarding rule 的費用。
使用情境:地理 A/B
把 weightedBackendServices 與 pathMatcher + headerAction(比對 X-Client-Geo)組合,可以讓 EU 流量 50% 進 green、US 流量 100% 進 blue。這就是在不影響美國客戶的情況下,測試新的 EU 資料駐留後端的做法。
vs Anthos Service Mesh
- Global LB 加權後端:邊緣的 L7,跨 region、跨後端類型(MIG + Cloud Run + GKE)都能同時運作。
- ASM VirtualService:cluster 內,更細粒度(per-namespace、per-header),但僅限 GKE 工作負載。
PCA 題目若提到「在兩個 Cloud Run 地區之間切分流量」或「跨混合後端做加權路由」,答案是 Global LB 加權後端服務。
Cloud Deploy Verify 階段自動回滾
Cloud Deploy 的 verify 階段是 GCP 對「自動化 canary 回滾」原生的答案。當 strategy.canary 或 strategy.standard 上設定 verify: true,Cloud Deploy 會在每個 rollout 階段之間執行一個 Skaffold verify profile。
Verify 階段運作機制
# skaffold.yaml
verify:
- name: smoke-test
container:
image: gcr.io/$PROJECT/smoke-test:latest
command: ["./run-smoke.sh"]
- name: slo-check
container:
image: gcr.io/$PROJECT/slo-checker:latest
command: ["python", "check_slo.py", "--window=5m", "--error-budget=0.99"]
- 每個 verify 容器跑在 Cloud Build worker 內,並透過 rollout 的執行身分存取目標 cluster/service。
- 結束碼 0 = 通過,推進到下一階段。非零 = 失敗,halt rollout,標記 rollout 為
FAILED。
整合 Cloud Monitoring
slo-check 容器通常呼叫 Cloud Monitoring API(projects.timeSeries.query)讀取過去 5 分鐘內 canary revision 的錯誤率。若 canary 的錯誤率超過基線(超過 SLO 預算),script 結束碼 1,Cloud Deploy 就會停止。
自動回滾
在 rollout 上設定 automaticRollback: true(透過 gcloud deploy rollouts retry 政策)或使用 Cloud Deploy 的 rollback-on-failure 行為:當某個階段 verify 失敗,Cloud Deploy 會自動建立 Rollback 資源,把上一個成功的 release 推進。恢復時間:通常 2 分鐘以內。
PCA 考試中,生產環境的 canary 不該省略 verify 階段。任何涉及「人工盯著 Cloud Monitoring dashboard、手動按回滾」的答案都是錯的 — 正確答案永遠是 verify: true + 自動化 SLO 檢查 + Cloud Deploy 回滾。
Spinnaker on GCP
Spinnaker(最初由 Netflix 開源、現屬 CNCF)在 Cloud Deploy 出現之前,是事實上的多雲 CD 工具。Google 仍維護Spinnaker for Google Cloud Platform 作為自管選項,但 Cloud Deploy 現在是建議的代管服務。
Spinnaker 何時仍勝出
- 多雲流水線:在單一流水線中把同一個 artifact 部署到 GKE 和 EKS 和 地端。
- 進階部署策略:內建 Kayenta 自動 canary 分析,用統計方法比較 baseline 與 canary 的指標。
- 複雜核准工作流程:含自訂 UI 的手動判斷、ServiceNow 整合、JIRA 驅動的核准。
Cloud Deploy 何時勝出(PCA 考試多數情況)
- 完全代管:不必運維 Spinnaker cluster(Spinnaker 本身約 10 個 microservice,需要 GKE + Redis + Cloud SQL)。
- 原生 GCP 整合:IAM、Cloud Build、Artifact Registry、Cloud Monitoring 開箱即用。
- TCO 更低:Cloud Deploy 按 rollout 計費;Spinnaker 則需付 24/7 跑它的 cluster 費用。
遷移路徑
若客戶今天跑 Spinnaker on GKE,希望降低運維複雜度:
- 把 Spinnaker pipelines 換成 Cloud Deploy
DeliveryPipelineYAML。 - 把 Kayenta canary 分析換成 Cloud Deploy
verify+ Cloud Monitoring SLO 檢查。 - 退役 Spinnaker cluster(通常每月可省 $300-800 的 GKE 節點成本)。
PCA 重點:「我們用 Spinnaker,希望降低運維負擔」→ 遷移到 Cloud Deploy。
功能標籤搭配 LaunchDarkly
功能標籤把部署(binary 已在生產環境中)與發布(功能對使用者可見)解耦。這是暗地發佈和漸進式功能 rollout 的基礎,獨立於任何基礎設施層級的 canary。
LaunchDarkly 在 GCP 上的整合樣式
- LaunchDarkly SDK 在 app 啟動時初始化;flags 在本地評估,並透過串流連線回 LaunchDarkly 邊緣取得更新(sub-200ms 傳播)。
- 目標規則:對
user.segment == "internal"提供true、對其他人提供false。接著 ramp 到user.country == "JP"、再到全體使用者的 5%,依此類推。 - 每個 flag 變更的稽核 log 透過 LaunchDarkly 的 webhook → Cloud Functions 整合,匯入 Cloud Logging 以符合稽核需求。
替代方案:把 flags 自建在 Firestore 上
對於較簡單的情境,可以把 flag 狀態存在 Firestore,用 Firestore client SDK 的即時 listener 讀取。flag 值在 Firestore 文件寫入後約 1 秒內傳達到 client。便宜,但缺少 LaunchDarkly 的百分比 rollout、segment targeting、稽核等功能。
功能標籤 vs canary 部署
- Canary:基礎設施層級 — 哪個版本的 binary 在服務請求。
- Feature flag:程式碼層級 — 哪些程式碼路徑在一個 binary 內被執行。
- 兩者可組合:用藍綠部署 v2(零風險,基礎設施兩個版本都測過),再對 1% 使用者打開
enableNewCheckout旗標(零風險,功能兩個版本都測過)。
考生常常選「Cloud Deploy canary」,但題目實際描述的是功能標籤問題(例如:「我們希望為特定客戶帳號啟用該功能,但不重新部署」)。Cloud Deploy 操作的是 revision/image;它無法針對個別使用者。針對 per-user/per-account 的開關,正確答案是功能標籤(LaunchDarkly、Firestore-backed,或 app 層級設定)。
Canary 期間的資料庫 Schema 遷移
Schema 變更是生產環境 canary 部署失敗的頭號原因,因為資料庫在 rollout 視窗期間被新舊兩版應用共用。這套紀律稱為 expand-and-contract(或 parallel change)。
expand-and-contract 模式
- Expand(canary 之前):做出向後相容的純新增 schema 變更。
ALTER TABLE orders ADD COLUMN new_status VARCHAR(32) DEFAULT NULL;— 舊程式碼忽略new_status、新程式碼讀寫它。這個遷移單獨部署,先不動 app。 - Migrate app(canary 本體):部署新 app 版本,同時寫入
status和new_status。canary 跑 10% → 25% → 100%,時間以小時/天計。新舊 app 同時存在;兩者都能運作,因為兩個欄位都存在。 - Backfill:跑一次性 job,把歷史資料的
status複製到new_status。 - Contract(100% rollout 後加上 soak time):部署第三個版本,只讀
new_status。穩定之後,最後一次遷移時把舊的status欄位 drop 掉。
GCP 上的工具
- Cloud SQL:用 Liquibase 或 Flyway,在 Cloud Deploy 推進步驟之前以 Cloud Build step 執行。遷移與 app 放在同一個 Git repo 並做版本控制。
- Spanner:新增類 schema 變更(
ADD COLUMN、CREATE INDEX)線上且不阻塞 — Spanner 是 GCP DB 中對 canary 部署最友善的,因為有這項特性。 - AlloyDB:行為類似 PostgreSQL;Liquibase/Flyway 的故事和 Cloud SQL 相同。
反樣式
- Canary 期間
DROP COLUMN:舊 pod 寫入時崩潰。除非 100% 流量都在新程式碼上且已 soak 至少一個發布週期,否則絕對不做破壞性 schema 變更。 - 在熱門表上不加
LOCK=NONE的ALTER TABLE(Cloud SQL MySQL):阻擋寫入數分鐘。用pt-online-schema-change或 gh-ost。 - 重新命名欄位:這是偽裝過的破壞性變更。視同
ADD new_name→ backfill →DROP old_name,絕不直接RENAME。
Expand-and-contract 守則: 每個 canary-safe 的 schema 變更,在 expand 階段必須完全只做新增(ADD COLUMN、ADD INDEX、新建表)。破壞性變更(DROP、RENAME、ALTER TYPE)只在 contract 階段、舊程式碼完全下架之後才執行。忘了這條守則,每個 canary 都會變成部分故障。
FAQ — 版本管理策略
Q1. 在藍綠部署期間如何處理資料庫結構 (Schema) 變更?
這是最困難的部分。你必須確保資料庫具備向後相容性。通常,你會在部署之前應用結構變更(如新增欄位),以便藍色和綠色版本都能與同一個資料庫搭配運作。
Q2. 什麼是「暗地發佈 (Dark Launch)」?
暗地發佈是指將程式碼部署到生產環境,但對使用者保持隱藏(使用功能標籤)。這讓你可以測試程式碼在真實負載下的效能和穩定性,而不會影響使用者介面。
Q3. 什麼時候應該使用滾動更新而不是藍綠部署?
對於內部工具或非關鍵服務,如果可以接受幾秒鐘的「混合版本」且希望節省基礎設施成本,請使用滾動更新。
Q4. 如何自動化金絲雀回滾?
將你的部署流水線(如 Cloud Deploy)與 Cloud Monitoring 整合。如果金絲雀版本的錯誤率超過閾值,流水線應自動觸發 gcloud deploy rollbacks create 命令。
Q5. 服務網格 (Istio) 在部署中的作用是什麼?
像 Anthos Service Mesh 這樣的服務網格提供細粒度的流量控制。你可以根據 HTTP 標頭(例如:「僅將內部員工導向金絲雀版本」)或地理位置來切換流量,這比單純基於 IP 的加權要強大得多。