結論先講

Rolling Update 是 80% 場景的正解。 K8s 預設就是 Rolling Update,設定好 maxSurgemaxUnavailable 就能用。Blue-Green 適合「一秒都不能錯」的金融場景,但你要付雙倍的機器成本。Canary 適合面對大量用戶的 toC 產品,先讓 5% 流量試水溫。不確定選哪個?先用 Rolling Update,痛了再換。


三種策略一覽

Rolling Update:
  v1 v1 v1 v1     ← 4 個 Pod
  v2 v1 v1 v1     ← 逐一替換
  v2 v2 v1 v1
  v2 v2 v2 v1
  v2 v2 v2 v2     ← 全部更新完成

Blue-Green:
  [v1 v1 v1 v1]  ← Blue(正在服務)
  [v2 v2 v2 v2]  ← Green(準備好了)
  切換 → Green 開始服務,Blue 待命
  確認沒問題 → 關掉 Blue

Canary:
  v1 v1 v1 v1 v1 v1 v1 v1 v1 v1  ← 100% 流量
  v2 v1 v1 v1 v1 v1 v1 v1 v1 v1  ← 10% 流量到 v2
  觀察 metrics...
  v2 v2 v2 v1 v1 v1 v1 v1 v1 v1  ← 30% 流量到 v2
  觀察 metrics...
  v2 v2 v2 v2 v2 v2 v2 v2 v2 v2  ← 100% 流量到 v2

Rolling Update

K8s 設定

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # 最多多開 1 個 Pod
      maxUnavailable: 1  # 最多 1 個 Pod 不可用
  template:
    spec:
      containers:
        - name: order-service
          image: order-service:v2
          readinessProbe:        # 關鍵:新 Pod 準備好才接流量
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 3

優缺點

優點缺點
K8s 預設,零額外設定回滾需要時間(逐一替換回來)
不需要雙倍資源部署過程中 v1 和 v2 同時存在
對 stateless 服務完美適用DB schema 變更要特別小心

v1 和 v2 同時存在的問題

部署過程中:
  Pod 1: v2(新 API 回傳多了一個欄位)
  Pod 2: v1(舊 API 沒有那個欄位)
  
  用戶 A 的請求打到 v2 → 拿到新欄位 → 正常
  用戶 A 下一個請求打到 v1 → 沒有新欄位 → 前端爆了?

解法:
  API 變更必須向後相容(backward compatible)
  - 新增欄位:OK(v1 client 忽略新欄位)
  - 刪除欄位:先標 deprecated → 下下個版本才移除
  - 改欄位格式:開新欄位,保留舊欄位

Blue-Green Deployment

Nginx 切換

# 初始狀態:流量到 Blue
upstream order-service {
    server blue-order:8080;
}
 
# 部署 Green 環境,測試通過後:
upstream order-service {
    server green-order:8080;   # 切到 Green
}
 
# reload nginx → 流量瞬間切換,零停機

K8s 做法

# Blue:目前在服務的
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
    version: blue       # ← 指向 blue deployment
 
# 部署 Green deployment,測試通過後:
# kubectl patch service order-service -p '{"spec":{"selector":{"version":"green"}}}'
# 一行指令切換,回滾也是一行

優缺點

優點缺點
切換瞬間完成需要雙倍資源(兩套環境同時跑)
回滾一秒完成(切回去)資料庫 migration 要兩個版本都相容
可以在 Green 上充分測試成本高

Blue-Green 最適合:金融、醫療、政府等對停機零容忍的場景。


Canary Deployment

用 Nginx 做流量分配

upstream order-service {
    server order-v1:8080 weight=9;   # 90% 流量
    server order-v2:8080 weight=1;   # 10% 流量
}
 
# 觀察 10 分鐘,error rate 沒上升 → 調整為 50/50
# 再觀察 → 調整為 0/100

用 Istio 做更精細的控制

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
spec:
  http:
    - route:
        - destination:
            host: order-service
            subset: v1
          weight: 90
        - destination:
            host: order-service
            subset: v2
          weight: 10

Istio 還能做更進階的:只讓內部員工的流量打到 v2(用 header match),或只讓特定地區的用戶打到 v2。

自動化 Canary:Flagger / Argo Rollouts

手動調流量太累,用工具自動化:

1. 部署 v2,初始流量 5%
2. 每 5 分鐘檢查 metrics(error rate、latency)
3. 如果 metrics 正常 → 流量 +10%
4. 如果 metrics 異常 → 自動回滾到 v1
5. 到 100% → 部署完成

部署時跑壓測:用 k6 驗證

部署過程中跑壓測,確認服務不會掛:

// k6-deploy-test.js
import http from 'k6/http';
import { check } from 'k6';
 
export const options = {
  stages: [
    { duration: '2m', target: 50 },   // 部署前:穩定 50 用戶
    { duration: '5m', target: 50 },   // 部署中:維持 50 用戶觀察
    { duration: '1m', target: 0 },    // 收尾
  ],
  thresholds: {
    http_req_failed: ['rate<0.01'],    // 錯誤率 < 1%
    http_req_duration: ['p95<500'],    // P95 < 500ms
  },
};
 
export default function () {
  const res = http.get('https://api.example.com/orders');
  check(res, { 'status 200': (r) => r.status === 200 });
}

如同 第 41 篇第 42 篇 討論的容器化基礎,部署策略是建立在容器之上的。


怎麼選

條件Rolling UpdateBlue-GreenCanary
團隊經驗初階中階進階
額外成本低(+25%)高(+100%)中(+10-30%)
回滾速度分鐘級秒級秒級
部署風險最低
適用場景內部工具、一般 API金融、醫療toC 高流量

我的建議:從 Rolling Update 開始。 等你遇到「部署後 5 分鐘發現 bug,回滾來不及」的痛,再考慮 Canary。等你遇到「客戶合約要求 99.99% uptime」的壓力,再上 Blue-Green。


下一篇

微服務安全 — 部署搞定了,但如果服務之間的通訊沒有認證,任何人都能偽裝成 Payment Service 去呼叫 Order Service。mTLS、JWT、API Key 怎麼選?


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:CD:每個 Service 獨立 Pipeline → 下一篇:微服務安全(一):服務間認證 mTLS vs JWT vs API Key