EP.05DevOps 系列

藍綠部署與 Canary Release:
零 Downtime 上線策略

藍綠切換、Canary 灰度發布、Feature Flag、滾動更新 — 讓每次部署都可以安全回滾

Joseph Chen 2026 14 min read Blue-Green · Canary · Feature Flag · DevOps

1. 傳統部署的風險

大多數初學者對「部署」的理解,就是 SSH 進伺服器、拉最新程式碼、重啟服務。這種方式簡單直覺, 但在流量大的生產環境中是不可接受的操作模式。

bash
# 傳統部署(In-place deployment)
ssh production-server
git pull origin main
npm run build
pm2 restart app    # 這段時間:服務中斷!

# 問題:
# 1. 部署期間有 downtime(可能幾秒到幾分鐘)
# 2. 新版有 bug → 回滾需要重新部署(又是 downtime)
# 3. 用戶可能在操作到一半時遭遇中斷
服務中斷

pm2 restart 期間,所有進來的請求都會收到 connection refused 或 502

回滾成本高

新版出問題要 git revert + 重新 build + 重新部署,整個流程再來一遍

業務損失

用戶在購物車結帳時遭遇中斷,這筆訂單大概就丟了

業務影響試算

一個電商網站,平均每秒 $500 GMV, 10 分鐘部署 downtime = $300,000 潛在損失。 這還不算用戶信任度的損耗與 SEO 的負面影響。


2. 藍綠部署(Blue-Green Deployment)

藍綠部署的核心思路是:永遠維持兩個完整的生產環境,一個跑目前版本(藍色), 一個準備新版本(綠色)。切換流量只需要更新 Load Balancer 的指向, 整個過程對用戶而言是無感的。

架構示意圖

                Load Balancer
                      │
         ┌────────────┴────────────┐
         ▼                         ▼
   Blue(v1.0)              Green(v1.1)← 新版本在這裡
   [目前的 Production]        [準備好了但沒有流量]

部署流程(共 6 步)

1

維持藍色環境(v1.0)服務所有流量,用戶完全無感

2

在綠色環境部署 v1.1(不影響任何現有流量)

3

在綠色環境跑 smoke test,確認服務健康

4

把 Load Balancer 切換指向綠色(瞬間切換,零 downtime)

5

監控 15–30 分鐘,觀察錯誤率與 latency

6

OK → 綠色成為新的「藍色」;有問題 → 切回藍色(秒級回滾)

GitHub Actions 實作

yaml
# .github/workflows/blue-green-deploy.yml
name: Blue-Green Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Build new image
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker push registry.example.com/myapp:${{ github.sha }}

      - name: Deploy to Green environment
        run: |
          kubectl set image deployment/myapp-green \
            web=registry.example.com/myapp:${{ github.sha }}
          kubectl rollout status deployment/myapp-green

      - name: Run smoke tests on Green
        run: |
          GREEN_URL=$(kubectl get service myapp-green -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
          curl -f "$GREEN_URL/health" || exit 1
          ./scripts/smoke-test.sh "$GREEN_URL"

      - name: Switch traffic to Green
        run: |
          # 更新 Ingress 指向 Green service
          kubectl patch ingress myapp-ingress \
            --type='json' \
            -p='[{"op": "replace", "path": "/spec/rules/0/http/paths/0/backend/service/name", "value": "myapp-green"}]'

      - name: Monitor for 5 minutes
        run: sleep 300

      - name: Rename Green to Blue (for next deployment)
        run: |
          # 更新 label,讓下次部署知道哪個是「穩定版」
          kubectl label deployment myapp-green role=blue --overwrite
          kubectl label deployment myapp-blue role=green --overwrite

優點

  • • 回滾只需更改 Load Balancer 指向,速度秒級
  • • 新舊版本完全隔離,不存在混合版本問題
  • • 可以在綠色環境做充分的預熱(warm-up)

缺點

  • • 資源成本翻倍(永遠有兩套完整環境)
  • • 資料庫 migration 要特別小心(兩版本都要能讀同一份 DB)
  • • Session / 狀態管理需要考慮切換時的用戶影響

3. Canary Release(金絲雀發布)

🐤

名稱的由來

礦工下礦前會帶一隻金絲雀——金絲雀對有毒氣體極度敏感,如果牠死了礦工就知道前方有危險。 Canary Release 採用同樣的邏輯:先讓少數用戶(5–10%)接觸新版本, 如果這批「先鋒用戶」沒遇到問題,再逐步把流量全部切過去。

流量分配示意

Load Balancer
    │
    ├── 90% → Stable (v1.0)
    └── 10% → Canary (v1.1)   ← 只有 10% 用戶看到新版

Kubernetes 實作:副本數控制流量比例

yaml
# stable-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-stable
  labels:
    app: myapp
    version: stable
spec:
  replicas: 9        # 90% 流量
  template:
    metadata:
      labels:
        app: myapp
        version: stable
    spec:
      containers:
        - name: web
          image: myapp:1.0.0

---
# canary-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-canary
  labels:
    app: myapp
    version: canary
spec:
  replicas: 1        # 10% 流量(1 out of 10 total pods)
  template:
    metadata:
      labels:
        app: myapp
        version: canary
    spec:
      containers:
        - name: web
          image: myapp:1.1.0

---
# service.yaml(同一個 Service 路由到兩個 Deployment)
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp         # 同時選中 stable 和 canary(pod 比例決定流量比例)
  ports:
    - port: 80
      targetPort: 3000

Canary 的監控(這才是關鍵)

部署 Canary 後不能就放著不管。要盯著這三類指標至少 30 分鐘,才能決定全量推出或回滾。

bash
# 在 Canary 部署後監控這些指標(30 分鐘)
- 5xx Error Rate(應該 < 0.1%)
- P99 Latency(應該沒有顯著升高)
- Business Metrics(訂單轉換率、用戶行為)

# 全量推出:
kubectl scale deployment myapp-canary --replicas=10
kubectl scale deployment myapp-stable --replicas=0

# 回滾:
kubectl scale deployment myapp-canary --replicas=0

Canary 的進階做法

更精細的 Canary 不只用副本數控制,而是透過 Nginx Ingress 的 annotation Istio 做精準的流量百分比控制(例如 5.3%)。 也可以針對特定用戶群(如 Beta 用戶、內部員工)做定向灰度,而非隨機 10%。


4. Feature Flag(功能旗標)

Canary Release 是在基礎設施層面控制流量;Feature Flag 則更深一層,讓你在應用程式層控制功能的開關。 最重要的一個概念是:「部署」和「功能發布」可以分離。

為什麼需要分離?

你可以把還沒開發完的功能藏在 Feature Flag 後面,先部署到生產環境(但功能關閉), 避免長期維持一個超大 feature branch。等功能完全就緒,只需要翻轉一個 flag,不用重新部署。

自製簡易 Feature Flag 系統

typescript
// lib/featureFlags.ts
type FeatureFlag = {
  enabled: boolean;
  rolloutPercentage: number;  // 0-100,向多少 % 用戶開放
  allowList?: string[];       // 特定用戶 ID 白名單
};

const flags: Record<string, FeatureFlag> = {
  'new-checkout-flow': {
    enabled: true,
    rolloutPercentage: 10,    // 先開 10% 用戶
    allowList: ['admin@example.com'],
  },
  'ai-recommendations': {
    enabled: false,           // 還沒準備好
    rolloutPercentage: 0,
  },
};

export function isFeatureEnabled(flagName: string, userId: string): boolean {
  const flag = flags[flagName];
  if (!flag || !flag.enabled) return false;

  // 白名單用戶永遠開啟
  if (flag.allowList?.includes(userId)) return true;

  // 根據 userId 的 hash 決定(確保同一個用戶永遠看到同樣結果)
  const hash = userId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
  return (hash % 100) < flag.rolloutPercentage;
}

在元件中使用

tsx
function CheckoutPage({ userId }) {
  const showNewCheckout = isFeatureEnabled('new-checkout-flow', userId);

  return showNewCheckout ? <NewCheckoutFlow /> : <OldCheckoutFlow />;
}

推薦的 Feature Flag 服務

LaunchDarkly

業界標準,功能最完整

付費
Flagsmith

開源,可自行部署

開源
Vercel Edge Config

與 Vercel 生態深度整合

平台內建

注意:Flag 要有人管理,部署完成後記得清理不再需要的 flag。 長期累積的 feature flag 會讓程式碼可讀性急速下降,這也是「技術債」的常見來源之一。


5. 滾動更新(Rolling Update)

Kubernetes 的 Rolling Update 是最容易入門的零 downtime 部署方式,也是 Kubernetes Deployment 的預設策略。 概念是:逐批替換舊版 Pod,而非一次全部下線。在 EP.04 中我們提過這個策略,這裡深入看它的參數控制。

yaml
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 25%        # 最多比 replicas 多 25% 的 Pod(新版)
    maxUnavailable: 0    # 零 downtime:不允許任何 Pod 不可用

# 10 個 Pod 的滾動過程:
# Step 1: 啟動 3 個新版 Pod → 共 13 個(10+3)
# Step 2: 關閉 3 個舊版 Pod → 共 10 個(7+3)
# Step 3: 啟動 3 個新版 Pod → 共 13 個(7+6)
# ...直到全部換完

優點

  • • K8s 預設支援,幾乎不需要額外設定
  • • 不需要雙倍資源(藍綠才需要)
  • • 過程可以用 kubectl rollout status 監控

注意事項

  • • 部署期間新舊版本同時存在
  • • 必須確保兩版本的 API 相容(向後兼容)
  • • DB schema 異動要特別謹慎

快速回滾指令

bash
# 查看部署歷史
kubectl rollout history deployment/myapp

# 回滾到上一個版本(不需要重新 build!)
kubectl rollout undo deployment/myapp

# 回滾到指定版本
kubectl rollout undo deployment/myapp --to-revision=3

6. 各策略比較與選擇指南

四種策略各有適用場景,沒有「最好的」,只有「最適合當下團隊規模與業務風險」的那一個。

比較項目In-placeRolling UpdateBlue-GreenCanary
Downtime
回滾速度慢(重新部署)慢(重新 rollout)秒級
資源成本最低2 倍
複雜度最低
風險控制中(全切換)最佳
適合場景非關鍵系統一般服務需快速回滾高風險更新

2026 年的建議選擇

個人專案 / MVP

Rolling Update(Kubernetes 預設)—— 成本最低,夠用就好,別過早優化。

中型產品

Blue-Green —— 安全回滾機制是必要的,2 倍資源成本在這個階段完全值得。

大型 / 高風險功能

Canary + Feature Flag —— 最精細的風險控制,Netflix、Google、Spotify 標準做法。


Blue-GreenCanary ReleaseFeature FlagZero DowntimeDevOpsKubernetes