開發與維運

大流量場景下如何雲淡風輕地進行線上發佈?

前言

本文,我們繼續聊聊《揭祕大流量場景下發布如絲般順滑背後的原因》中的另外一環,灰度發佈,也叫金絲雀發佈。

很多互聯網公司在半夜發佈的另外一個重要原因是不具備可灰度能力,新版本存在 bug 或者其它原因會影響線上的客戶,無奈之下只能選擇在半夜進行發佈來減少影響面。

我們知道默認情況下,無論是 Kubernetes 還是 ECS,新老版本都存在的情況下會根據特定的負載均衡算法隨機地路由到不同的實例上,隨機意味著出問題也會隨機出現。我們需要一套動態路由來完成灰度發佈的解決方案。

在 RPC 領域,我們稱灰度發佈為動態路由,動態路由的意思是指流量可以動態地路由到指定的實例上。

動態路由場景

動態路由是微服務裡非常核心的功能,流量動態路由意味著可以做非常多的事情。由此衍生出各個場景:

  • 金絲雀發佈:只有滿足特定規則(比如 Query Parameter、HEADER、COOKIE 中某些 KEY 滿足一些條件)或者是固定流量比例的流量才會進入新版本,其它流量都路由到老版本上。

image.png

  • 同機房優先路由:當公司規模擴大之後,應用會跨機房部署來達到高可用的目的。由於異地跨機房調用出現的網絡延遲問題,需要確保服務消費方能優先調用相同機房的服務消費方,這就需要同機房優先路由的能力。

image.png

  • 標籤路由:金絲雀發佈的新場景。金絲雀發佈一般只有新和老兩個版本,標籤路由可以在線上部署多個版本,每個版本都對於一個標籤。

  • 全鏈路灰度:在業務比較複雜,服務調用鏈路較長的場景下,每個應用都需要設置路由規則會顯得非常繁瑣,全鏈路灰度在金絲雀/標籤路由的基礎上加上了 "標籤透傳" 的能力,讓灰度流量只在灰度版本之間路由。

image.png

接下來我會分幾篇文章詳細講一下這幾個場景,今天我們先來聊聊金絲雀發佈。金絲雀發佈可以讓我們在白天流量高峰喝著茶吃著瓜子進行線上發佈,不需要在半夜為發佈的事情而苦惱。

大流量下的應用部署現狀

應用 Demo

Demo 以 Spring Cloud 為例,服務調用鏈路如下圖所示:

image.png

流量從 Netflix Zuul 對應的 Ingress 進來,會調用 SC-A 應用對應的服務,SC-A 應用內部調用 SC-B 應用的服務,SC-B 應用內部調用 SC-C 應用的服務。SC-A 有線上版本和灰度版本這兩個版本。

Helm 部署 Demo

Demo 為純開源 Spring Cloud 架構,項目地址:https://github.com/aliyun/alibabacloud-microservice-demo/tree/master/microservice-doc-demo/traffic-management

部署完畢後,阿里雲容器服務上的工作負載情況如下:

image.png

我們通過 "while true; do curl http://{ip:port}/A/a;echo;done" shell 命令不斷地去訪問 Spring Cloud 服務,各個服務的作用僅僅是打印當前服務的IP,這樣我們可以看到整體調用鏈路:

while true; do curl http://{ip:port}/A/a;echo;done
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
...

從這個過程我們明顯可以看出 Netflix Zuul -> SC-A 這條鏈路是隨機訪問的,由於 SC-A 的線上版本和灰度版本各只有 1 個 Pod,所以隨機打印了這兩個 POD 的 IP。

開源金絲雀的實現

動態路由的本質就是尋址過程中去找符合條件的實例地址。

Apache Dubbo 提供了 RouterChain 的能力去過濾 Invoker 列表,RouterChain 內部維護著 Router 列表,每個 Router 都會做過濾 Invoker 的邏輯,最後將 Invoker 列表交付給 LoadBalance 做負載均衡獲取最後的 Invoker。

ScriptRouter,ConditionRouter 和 TagRouter 這些內置的 Router 內部都是 Dubbo 自帶的動態路由能力。

Spring Cloud 的路由能力由 Ribbon 實現。Ribbon 設計了 ILoadBalancer 接口用於獲取 Server 列表,最後將這個列表交付給 IRule 做負載均衡策略獲取最後的 Server。這塊在設計上跟 Dubbo 相比是有缺陷的。

Spring Cloud Ribbon 裡的 ILoadBalancer 相當於是 Dubbo RouterChain 裡的一個 Router,IRule 相當於是 Dubbo LoadBalance。

最新版本的 Spring Cloud 新增了 Spring Cloud LoadBalancer 組件代替 Ribbon 用於做負載均衡,Spring Cloud LoadBalancer 裡的 ServiceInstanceListSupplier 用於獲取實例列表信息,ReactiveLoadBalancer 使用負載均衡策略獲取最後的實例。

這是 3 者對應組件的說明:

  • Apache Dubbo Spring Cloud Ribbon Spring Cloud LoadBalancer
    路由 Router & RouterChain ILoadBalancer ServiceInstanceListSupplier
    負載均衡策略 LoadBalance IRule ReactiveLoadBalancer
    服務實例 Invoker Server ServiceInstance

Apache Dubbo 雖然內置了各種 Router,但實際使用下來卻有非常多的問題。比如 TagRouter 跟 IP 綁定,在 Kubernetes 下無法工作;ScriptRouter 用了 ScriptEngine 去做腳本的處理,會有性能問題;Dubbo Admin 的使用體驗非常糟糕等等。

Spring Cloud 官方並沒有提供動態路由的能力,只有社區上的一些開發者自己去擴展了這個能力,社區上也沒有任何的 UI 交互界面。

這時候 MSE 告訴你,MSE 的微服務解決方案提供了動態路由的能力,不需要做任何的代碼和配置的修改,就能使用 OPEN API 或者 UI 交互去完成金絲雀發佈。只需將您的應用接入 MSE 服務治理,您就能享受到金絲雀能力。

只要你的應用是基於 Spring Cloud 或 Dubbo 最近五年內的版本開發,就能直接使用完整的 MSE 微服務治理能力,不需要修改任何代碼和配置。

無需任何代碼修改就可以做到動態路由的能力,這不香嗎?

MSE 金絲雀能力

應用接入 MSE 即可享受 MSE 提供的動態路由能力,無需任何代碼修改。

引入標籤概念

MSE 引入了標籤的概念,可以針對每個標籤設置路由規則,滿足該路由規則的流量會路由到這個標籤對應的實例下。我們將 Spring Cloud Demo 進行一點改造,給 SC-A 的灰度版本打上 "blue" 標籤( Helm 已經完成了這個步驟)。

image.png

接入 MSE 後,MSE 默認會給應用分配一個 100% 路由到未打標實例的路由規則。此時,我們繼續通過 "while true; do curl http://{ip:port}/A/a;echo;done" shell 命令去執行,這個時候調用 SC-A 全部返回線上版本的 IP:

while true; do curl http://{ip:port}/A/a;echo;done
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
A[10.0.0.73] -> B[10.0.0.180] -> C[10.0.0.72]
...

設置金絲雀路由規則

我們在 MSE 上的應用詳情頁裡的金絲雀 Tab 頁裡設置 HEADER 裡 env 這個 KEY 的值為 test 的金絲雀路由條件:

image.png

傳入 HEADER 繼續使用 shell 執行:

while true; do curl -H "env:test" http://139.196.200.40/A/a;echo;done
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
A1[10.0.0.20] -> B[10.0.0.180] -> C[10.0.0.72]
...

這個時候我們發現滿足金絲雀規則的流量都去了 SC-A 的灰度版本。

金絲雀路由規則解析

大家看到金絲雀路由規則界面上有兩種分別,分別是流量比例流量規則:

image.png

流量規則:表示滿足該規則的流量會路由到對應標籤(本文使用 blue 作為灰度標籤)實例上。比如本文例子中 HEADER 裡 env=test 的流量一定會去 blue 標籤對應的實例。

流量比例:不滿足任何流量規則的流量會按照流量百分比進行路由。比如本文例子中不滿足 HEADER 裡 env=test 的流量會以 100% 的規則路由到未打標的實例上,由於是 100%,所以那些不滿足規則的流量全部都去了未打標實例上(本文未打標表示線上版本)。

MSE 提供的流量規則裡的條件支持 HEADER、Query Parameter、COOKIE 以及 Request BODY

Query Parameter、HEADER、COOKIE 和 Request Body 除了支持常規的運算符外,還支持 in(白名單),對 100 取模和百分比。這裡的百分比並不是比例規則中的總流量百分比,而是指對應參數的 hash 值取模,這樣就可以讓固定的值永遠滿足路由條件(如果按照流量比例,用戶 A 這次訪問的是線上版本,下次可能會訪問灰度版本)。舉個例子,如果 HEADER 中帶有用戶 ID,我讓想部分用戶永遠能夠訪問灰度實例,這個時候可以對用戶 ID 的 hash 值取模去完成(當然,這個也可以通過白名單去操作,白名單的缺點並不隨機,需要輸入各個名單)。

Request Body 目前支持解析 json 字符串,比如如下字符串:

{  
  "a": "aa",
  "b": [
    1,2,3
  ],
  "c": [
    {
      "d": "dd"
    }
  ],
  "e": {
    "f": "ff"
  }
}

JSON 訪問表達式 .a 的值為 aa。
JSON 訪問表達式 .b[0] 的值為 1。
JSON 訪問表達式 .c[0].d 的值為 dd。
JSON 訪問表達式 .e.f 的值為 ff。

總結

本文介紹了微服務治理下金絲雀發佈的能力,解決了發佈期間少量流量驗證新功能的問題。您的應用只需接入 MSE 服務治理,無需任何操作即可享受到動態路由的能力。除了 MSE(微服務引擎),金絲雀發佈還被 EDAS、SAE 等雲產品集成。

下一篇,我會給大家分享 MSE 全鏈路灰度能力。

微服務引擎用戶交流群

如果您在微服務引擎MSE使用過程中有任何疑問,歡迎您搜索釘釘群號 23371469 或者使用釘釘掃描如下二維碼加入釘釘群進行反饋。

image.png

掃碼瞭解更多技術內容與客戶案例:

image.png

Leave a Reply

Your email address will not be published. Required fields are marked *