背景
我們在系列文章
K8s 應用管理之道 - 更新篇(一)中介紹了不同部署形式下應用的更新方法,同時展示了如何配置停機釋出、滾動釋出這兩類 k8s 原生支援的部署更新政策。本文将介紹如何通過二次開發或使用一些第三方工具在 k8s 中實作應用的藍綠釋出、金絲雀釋出和 A/B 測試。
藍綠釋出
如果新老版本的應用無法共存,但又希望實作零中斷更新,在系統資源充足的前提下,可以選用藍綠釋出。通過 k8s 的 label-selector 機制可以輕松實作藍綠釋出。如下圖所示,您需要做的僅僅是為不同版本的應用打上對應的标簽,然後通過更改 service 的 selector 實作流量切換。

配置方法
這裡和藍綠釋出相關的配置如下(完整配置參見
blue-green)。
[...]
kind: Service
spec:
# Service 的 selector 會選擇 app 标簽和 version 标簽都比對的 pod
selector:
app: spring-boot-probes
# 通過更改 version 的取值實作流量切換,例如 v2.0.0
version: v1.0.0
[...]
kind: Deployment
metadata:
name: spring-boot-probes-v1
spec:
selector:
matchLabels:
app: spring-boot-probes
version: v1.0.0
template:
metadata:
labels:
app: spring-boot-probes
# Pod 的 version 标簽
version: v1.0.0
[...]
kind: Deployment
metadata:
name: spring-boot-probes-v2
spec:
selector:
matchLabels:
app: spring-boot-probes
version: v2.0.0
template:
metadata:
labels:
app: spring-boot-probes
# Pod 的 version 标簽
version: v2.0.0
[...]
操作步驟
完成上述配置後,可以通過以下步驟實作藍綠釋出:
- 運作指令
部署 v2 版本的應用。等到 v2 版本的 pod 全部變成就緒狀态後流量并不會發往它們,這是因為 service 的 selector 隻與 v1 版本 pod 的标簽相比對。kubectl apply -f ./spring-boot-probes-v2.yaml
- 當一切準備就緒後,可以運作指令
更改 service 的 selector,讓其指向 v2 版本的 pod。這時流量會全部發往 v2。kubectl patch service spring-boot-probes-svc -p '{"spec":{"selector":{"version":"v2.0.0"}}}'
- 如果 v2 版本的應用運作一段時間後出現了問題,可以通過指令
進行復原,将流量再次切回到 v1 上來。kubectl patch service spring-boot-probes-svc -p '{"spec":{"selector":{"version":"v1.0.0"}}}'
- 如果 v2 版本應用的行為符合預期,可以運作指令
删掉 v1 版本的 deployment 以節省資源。kubectl delete deploy spring-boot-probes-v1
金絲雀釋出
金絲雀釋出通過逐漸切換流量的方式大大降低了更新和變更可能帶來的風險,是以被廣泛使用。相較于其他釋出方式,其最大特點在于對流量的靈活控制。回到 k8s 的場景,實作金絲雀釋出最推薦的方式是使用
istio。利用 istio 強大的流量管理功能,使用者可以靈活地控制發往不同版本的流量,不論該版本部署了多少個 pod 執行個體。而在沒有 istio 的 k8s 環境中,發往新老版本的流量和它們的 pod 執行個體數成正比。
下圖展示了基于 istio 的金絲雀釋出的關鍵元件,使用者可以通過調節
VirtualService的 weight 字段指定将 10% 的流量發往金絲雀版本。
以下配置指定将 90% 的流量發往服務 spring-boot-probes-svc-v1,将 10% 的流量發往服務 spring-boot-probes-svc-v2。而服務 spring-boot-probes-svc-v1 綁定了目前版本的 pod,服務 spring-boot-probes-svc-v2 綁定了金絲雀版本的 pod(完整配置參見
canary[...]
kind: VirtualService
metadata:
name: spring-boot-probes
spec:
hosts:
- spring-boot-probes.local
gateways:
- spring-boot-probes
http:
- route:
- destination:
host: spring-boot-probes-svc-v1
weight: 90
- destination:
host: spring-boot-probes-svc-v2
weight: 10
[...]
kind: Service
metadata:
name: spring-boot-probes-svc-v1
spec:
selector:
app: spring-boot-probes
version: v1.0.0
[...]
kind: Service
metadata:
name: spring-boot-probes-svc-v2
spec:
selector:
app: spring-boot-probes
version: v2.0.0
[...]
完成上述配置後,可以通過以下步驟實作金絲雀釋出:
-
部署 v2 版本的 service 和少量的 pod 執行個體。由于此時 VirtualService 的路由目标隻有 v1,流量并不會被發到 v2 上。kubectl apply -f ./spring-boot-probes-v2.yaml
- 當 v2 版的 pod 就緒後,運作指令
更改 VirtualService 的路由規則,讓少量流量能夠發往 v2。kubectl apply -f ./virtualservice-weight.yaml
- 如果 v2 版本應用的行為符合預期,可以通過調整權重繼續擴大發往 v2 的流量比例。同時,v1 和 v2 的 pod 執行個體數也可以根據實際情況進一步調整,甚至可以為它們配置 Horizontal Pod Autoscaler 。
- 如果 v2 版本在運作過程中出現了問題,可以更改 VirtualService 的路由規則将流量重新切回到 v1 上來。
- 待全部流量都落在 v2 上後,運作指令
删除 v1 版應用的資源。kubectl delete -f ./spring-boot-probes-v1
A/B 測試
如果您網站的 UI 作了改版,但并不确定新版本是否會獲得使用者青睐,這時可以通過 A/B 測試收集使用者意見,并根據實際效果确定最佳方案。A/B 測試實施的關鍵在于根據不同條件将通路流量分發到不同的版本。實際使用中,往往會根據具體情況選用不同的條件。常用的條件包括使用者位置資訊、user-agent、自定義 header、請求參數、語言等。得益于強大的流量管理功能,istio 是 k8s 場景下實作 A/B 測試的最佳方案。下圖展示了如何根據 user-agent 分發通路流量。
以下配置指定将來自 Andriod 的通路流量發往服務 spring-boot-probes-svc-v1,将來自 iPhone 的通路流量發往服務 spring-boot-probes-svc-v2。而 spring-boot-probes-svc-v1 和 spring-boot-probes-svc-v2 分别綁定了不同版本的 pod
(完整配置參見
ab-testing[...]
kind: VirtualService
metadata:
name: spring-boot-probes
spec:
hosts:
- spring-boot-probes.local
gateways:
- spring-boot-probes
http:
- route:
- destination:
host: spring-boot-probes-svc-v1
match:
- headers:
user-agent:
exact: Andriod
- route:
- destination:
host: spring-boot-probes-svc-v2
match:
- headers:
user-agent:
exact: iPhone
[...]
kind: Service
metadata:
name: spring-boot-probes-svc-v1
spec:
selector:
app: spring-boot-probes
version: v1.0.0
[...]
kind: Service
metadata:
name: spring-boot-probes-svc-v2
spec:
selector:
app: spring-boot-probes
version: v2.0.0
[...]
完成上述配置後,可以通過以下步驟實施 A/B 測試:
-
部署 v2 版本的 service 和對應的 pod 執行個體。kubectl apply -f ./spring-boot-probes-v2.yaml
-
更改 VirtualService 的路由規則,讓來自 Andriod 的流量發往 v1,來自 iPhone 的流量發往 v2。kubectl apply -f ./virtualservice-match.yaml
- 讓兩個版本的應用同時運作一段時間并不斷收集使用者回報。
- 如果發現使用 user-agent 作為條件并不能達到很好的測試效果,可以更改 VirtualService 的路由規則,根據其他條件進行測試。
- 經綜合分析後,如果發現 v1 更受青睐,則更改 VirtualService 的路由規則,讓流量隻發往 v1,然後删除 v2 版應用的資源。反之亦然。
總結
本文帶您探索了在 k8s 中進行藍綠釋出、金絲雀釋出和 A/B 測試的最佳實踐。可以看到,運用好 istio 強大的流量管理功能可以簡化複雜釋出的實施流程,大大降低實作成本。
參考資料
- Fully automated canary deployments in Kubernetes
- A Detailed Guide to Canary Deployments
- Istio in Practice – Routing with VirtualService
- 《分布式系統設計模式》系列幹貨:管理設計篇之"部署更新政策"
擴充閱讀
阿裡雲日志服務針對容器場景提供了一站式日志解決方案,想了解相關内容可參考下列文章: