iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Kubernetes

初見 Kubernetes - 純網站後端開發踏入 K8s 世界的經驗分享系列 第 22 篇

Day 22 - Canary 金絲雀部署與 Blue-Green 藍綠部署

  • 分享至 

  • xImage
  •  

如果有個 AP 服務經過更版了,有 v1、v2 兩版,
一般常見的部署方式可能是將環境上的舊版服務(不論是容器或是 Web Server 下的應用程式)停止後,將容器或建置後的檔案進行更換,
並且再啟動開始接受流量。

但是相信大家都碰過,上到正式環境後發現有一些問題並且無法在當下即時處理完畢,
所以會需要緊急退版,將舊版程式部署回去。
這麼一來一回的處理其實會花費非常多時間,中途步驟若出錯,處理時間又會被拉長。

今天要介紹的兩種方式,會讓新舊版先共存,再決定什麼時候讓新版接收流量。新版出問題時,舊版還在,能先把流量切回去。這兩種方式就是「金絲雀部署」與「藍綠部署」。

金絲雀部署:讓少量流量先試新版

金絲雀部署要解決的是「新版還沒經過真實流量驗證,就一次影響所有使用者」的風險。我們先讓 v2 接一小部分 request,觀察沒問題再逐步提高比例;若有異常,先把流量切回 v1。這能限制早期受影響的請求量。

以 Day 21 的 api-a 為例,v1、v2 各有一個 Deployment,Pod 都帶 app: api-a,再分別帶 version: v1、version: v2。

api-a Service 的 selector 涵蓋兩版;DestinationRule 根據 version label 建立兩個 subset;VirtualService 才負責把進來的流量以 90/10 等比例送到對應 subset。單靠 Service 同時選中兩版 Pod,無法精確指定我們要的版本權重。Istio 的流量權重與兩版 Pod 的數量可以分開調整。

Istio 金絲雀部署的版本分流與回復路由

圖中另有一條「帶測試 header 就去 v2」的路由,讓測試者先驗新版;一般流量再走下面的權重規則。以下 YAML 只示範 一般流量 90/10,尚未加入 header route,兩者要分開看。

這裡沿用 Day 21 的 app-routes,只把 /api/a 改成版本分流,保留 /api/b 與 Portal 的路由。不要為相同 host 另外建立一份 VirtualService,也不要更新 /api/a 時不小心把原本的其他 route 覆蓋掉。

apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: api-a-versions
  namespace: team-a
spec:
  host: api-a.team-a.svc.cluster.local
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: app-routes
  namespace: team-a
spec:
  hosts:
    - demo.example.internal
  gateways:
    - demo-gateway
  http:
    - match:
        - uri:
            prefix: /api/a
      route:
        - destination:
            host: api-a.team-a.svc.cluster.local
            subset: v1
          weight: 90
        - destination:
            host: api-a.team-a.svc.cluster.local
            subset: v2
          weight: 10
    - match:
        - uri:
            prefix: /api/b
      route:
        - destination:
            host: api-b.team-a.svc.cluster.local
            port:
              number: 80
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: portal-web.team-a.svc.cluster.local
            port:
              number: 80

這份設定只控制經 demo-gateway 進來、命中 /api/a 的流量。若還有叢集內服務直接呼叫 api-a,要另外確認那些請求會走哪條路由。

權重 90/10 代表路由選擇的比例,送 10 個 request 不一定剛好有 1 個落在 v2。

權重怎麼往新版推進?

我會先讓 v2 Pod Ready,確認 Service 能找到 v2、subset 的 label 也對得上,再以受控測試請求檢查新版。正式切流可從 v1/v2 = 95/5 → 90/10 → 75/25 → 50/50 → 0/100 往前走。

請求量低就要等久一點,讓每一階有足夠的觀察樣本。

每一階都先確認路由設定已生效、v2 實際有收到請求,再比較 v1 與 v2 的 HTTP 錯誤率、p95/p99 延遲、請求量和關鍵業務流程。也要看 Pod 的 Ready、重啟、CPU/記憶體與應用程式 log。Istio 的 istio_requests_total 可依 destination_version、response_code 看各版請求及錯誤,延遲可看 istio_request_duration_milliseconds。

如果新版錯誤率升高、延遲變長,或登入等關鍵流程失敗,就停止升權重,把一般流量恢復到 v1,再查 v2 的 log 與 trace。

金絲雀的代價是新舊版要並存一段時間。這個例子會有兩個 Deployment,還要維護版本 label、路由、監控與回復步驟;但 Deployment 數量變成兩個,也不一定等於 Pod 或 CPU/記憶體剛好變兩倍。

v2 初期可以用較小容量,升權重前要確認它接得住下一階流量。

藍綠部署:準備好新版後直接一次切換

藍綠部署要處理的是切換與回復所花的時間。先保留正在服務的藍版 v1,同時把綠版 v2 部署好、確認 Ready,並完成必要的測試。

一般使用者仍由藍版服務,準備好後,將一般流量一次從藍版切到綠版。藍版先不要急著刪,觀察期間如果綠版出問題,就把路由切回藍版。

架構上仍可沿用同一個 api-a Service、兩個版本 Deployment、DestinationRule subsets 與 VirtualService。

切換前,一般路由是 藍 100/綠 0;切換後改為 藍 0/綠 100。如果要在正式切換前驗綠版,可以另設優先比對的測試 header route,讓指定測試者進綠版;沒有 header 的請求一定要有清楚的藍版預設路由。

Istio 藍綠部署切換前、切換後與回切的流量路徑

切換時仍然修改 Day 21 的同一份 app-routes,保留 /api/b 與 Portal。

以上面 90/10 的 /api/a route 為例,切換後只把其中兩個 weight 改為 0、100;發現異常就改回 100、0。新規則要等代理收到並生效,原本進行中的請求也可能仍在舊版完成,所以切換後要持續觀察,不能只看設定檔已套用。

金絲雀調成 100/0,不就等於藍綠?

這個問題我也有想過。就某一瞬間的路由設定來看,是一樣的:weight: 100 的版本接收這條一般路由的流量,weight: 0 的版本不接收。

金絲雀在開始時可以是 v1/v2 = 100/0,最後也會走到 0/100;藍綠切換的前後兩個狀態也正是這樣。如果另設測試 header route,即使一般路由給 v2 的權重是 0,帶 header 的測試請求仍可進 v2。

差異在切換過程與準備方式。金絲雀在 100/0 與 0/100 之間逐階放流量,逐步取得真實請求的證據;藍綠先把綠版準備到能承接完整流量,然後讓一般流量一次切過去。兩者都能用 Istio 的相同路由元件實作,並沒有「設了 100/0 就自動變成另一套部署架構」這件事。

藍綠的代價也比較直接:若藍、綠都按正式尖峰容量維持完整副本,重疊期間的應用 Pod 容量可能接近兩套,節點資源與費用都要先算。切換後也會一次讓所有一般流量進新版,預先測試沒涵蓋到的問題可能同時影響較多使用者。因此切換前要確認綠版容量,切換後立即看兩版實際流量、錯誤率、延遲、Pod 狀態與關鍵流程;異常就先切回藍版。

最後還有一個兩種方式都躲不掉的問題:API 如果會寫資料庫、更新 cache 或改變 session 格式,舊版與新版在共存期間必須能讀寫相容。路由切回 v1,只能讓後續請求再走 v1,資料庫 schema 和已寫入的資料不會跟著自動回復。

參考資料


上一篇
Day 21 - Istio 入口,接手原本應用 Gateway 的路由
系列文
初見 Kubernetes - 純網站後端開發踏入 K8s 世界的經驗分享 共 22 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言