深入剖析全鏈路灰度技術內幕

阿里巴巴雲原生發表於2021-12-15

作者:揚少

當服務有新版本要釋出上線時,通過引流一小部分流量到新版本,可以及時發現程式問題,有效阻止大面積故障的發生。業界上已經有比較成熟的服務釋出策略,比如藍綠髮布、A/B 測試以及金絲雀釋出,這些釋出策略主要專注於如何對單個服務進行釋出。在微服務體系架構中,服務之間的依賴關係錯綜複雜,有時某個功能發版依賴多個服務同時升級上線。我們希望可以對這些服務的新版本同時進行小流量灰度驗證,這就是微服務架構中特有的全鏈路灰度場景,通過構建從閘道器到整個後端服務的環境隔離來對多個不同版本的服務進行灰度驗證。

本文將會揭開全鏈路灰度的神祕面紗,深入剖析全鏈路灰度技術內幕,引出兩種不同的實現方案,並對實現方案的技術細節進行深入探討,最後通過實踐環節來展示全鏈路灰度在實際業務中的使用場景。

微服務架構帶來的挑戰

為了滿足業務的迭代速度,開發者開始對原來的單體架構進行細粒度的拆分,將單體應用中的服務模組拆分成一個個獨立部署執行的微服務,並且這些微服務的生命週期由對應的業務團隊獨自負責,有效的解決了單體架構中存在的敏捷性不足、靈活性不強的問題。常見的做法是根據業務域或者功能域進行服務拆分,如下圖:

1.png

其中,流量閘道器是四層代理,主要功能有負載均衡、TLS 解除安裝以及一些安全防護功能;微服務閘道器是七層代理,主要用來暴露後端服務、流量治理、訪問控制和流量監控。以"高內聚、低耦合"作為設計理念的微服務架構為開發者帶來了前所未有的開發體驗,每個業務團隊專注於自身業務的程式碼邏輯,並通過 API 形式對外發布。服務依賴方只需引入服務提供方的 API 定義,即可完成服務之間通訊,無需關心服務提供方的部署形態和內部實現。

但任何架構都不是銀彈,在解決舊問題同時勢必會引入一些新的問題。微服務體系中最令人頭疼的問題,是如何對眾多微服務進行高效、便捷的治理,主要表現在可見性、連線性和安全性這三個方面。進一步細化,微服務架構帶來了以下的挑戰:

2.png

本文的重點主要關注服務釋出這一子領域,如何保證微服務體系中服務新版本升級過程中平滑無損,以及如何低成本的為多個微服務構建流量隔離環境,方便開發者同時對多個服務新版本進行充分的灰度驗證,避免故障的發生。

什麼是全鏈路灰度

單體架構下的服務釋出

首先,我們先看一下在單體架構中,如何對應用中某個服務模組進行新版本釋出。如下圖,應用中的 Cart 服務模組有新版本迭代:

3.png

由於 Cart 服務是應用的一部分,所以新版本上線時需要對整個應用進行編譯、打包以及部署。服務級別釋出問題變成了應用級別的釋出問題,我們需要對應用的新版本而不是服務來實施有效的釋出策略。

目前,業界已經有非常成熟的服務釋出方案,例如藍綠髮布和灰度釋出。藍綠髮布需要對服務的新版本進行冗餘部署,一般新版本的機器規格和數量與舊版本保持一致,相當於該服務有兩套完全相同的部署環境,只不過此時只有舊版本在對外提供服務,新版本作為熱備。當服務進行版本升級時,我們只需將流量全部切換到新版本即可,舊版本作為熱備。我們的例子使用藍綠髮布的示意圖如下,流量切換基於四層代理的流量閘道器即可完成。

4.png

在藍綠髮布中,由於存在流量整體切換,所以需要按照原服務佔用的機器規模為新版本克隆一套環境,相當於要求原來1倍的機器資源。灰度釋出的核心思想是根據請求內容或者請求流量的比例將線上流量的一小部分轉發至新版本,待灰度驗證通過後,逐步調大新版本的請求流量,是一種循序漸進的釋出方式。我們的例子使用灰度釋出的示意圖如下,基於內容或比例的流量控制需要藉助於一個七層代理的微服務閘道器來完成。

5.png

其中,Traffic Routing 是基於內容的灰度方式,比如請求中含有頭部 stag=gray 的流量路由到應用 v2 版本;Traffic Shifting 是基於比例的灰度方式,以無差別的方式對線上流量按比重進行分流。相比藍綠髮布,灰度釋出在機器資源成本以及流量控制能力上更勝一籌,但缺點就是釋出週期過長,對運維基礎設施要求較高。​

微服務架構下的服務釋出

在分散式微服務架構中,應用中被拆分出來的子服務都是獨立部署、執行和迭代的。單個服務新版本上線時,我們再也不需要對應用整體進行發版,只需關注每個微服務自身的釋出流程即可,如下:

6.png

為了驗證服務 Cart 的新版本,流量在整個呼叫鏈路上能夠通過某種方式有選擇的路由到 Cart 的灰度版本,這屬於微服務治理領域中流量治理問題。常見的治理策略包括基於 Provider 和基於 Consumer 的方式。

  • 基於 Provider 的治理策略。配置 Cart 的流量流入規則,User 路由到 Cart 時使用 Cart 的流量流入規則。
  • 基於 Consumer 的治理策略。配置 User 的流量流出規則, User 路由到 Cart 時使用 User 的流量流出規則。

此外,使用這些治理策略時可以結合上面介紹的藍綠髮布和灰度釋出方案來實施真正的服務級別的版本釋出。

全鏈路灰度

繼續考慮上面微服務體系中對服務 Cart 進行釋出的場景,如果此時服務 Order 也需要釋出新版本,由於本次新功能涉及到服務 Cart 和 Order 的共同變動,所以要求在灰度驗證時能夠使得灰度流量同時經過服務 Cart 和 Order 的灰度版本。如下圖:

7.png

按照上一小節提出的兩種治理策略,我們需要額外配置服務 Order 的治理規則,確保來自灰度環境的服務 Cart 的流量轉發至服務 Order 的灰度版本。這樣的做法看似符合正常的操作邏輯,但在真實業務場景中,業務的微服務規模和數量遠超我們的例子,其中一條請求鏈路可能經過數十個微服務,新功能釋出時也可能會涉及到多個微服務同時變更,並且業務的服務之間依賴錯綜複雜,頻繁的服務釋出、以及服務多版本並行開發導致流量治理規則日益膨脹,給整個系統的維護性和穩定性帶來了不利因素。

對於以上的問題,開發者結合實際業務場景和生產實踐經驗,提出了一種端到端的灰度釋出方案,即全鏈路灰度。全鏈路灰度治理策略主要專注於整個呼叫鏈,它不關心鏈路上經過具體哪些微服務,流量控制視角從服務轉移至請求鏈路上,僅需要少量的治理規則即可構建出從閘道器到整個後端服務的多個流量隔離環境,有效保證了多個親密關係的服務順利安全釋出以及服務多版本並行開發,進一步促進業務的快速發展。

全鏈路灰度的解決方案

如何在實際業務場景中去快速落地全鏈路灰度呢?目前,主要有兩種解決思路,基於物理環境隔離和基於邏輯環境隔離。

物理環境隔離

物理環境隔離,顧名思義,通過增加機器的方式來搭建真正意義上的流量隔離。

8.png

這種方案需要為要灰度的服務搭建一套網路隔離、資源獨立的環境,在其中部署服務的灰度版本。由於與正式環境隔離,正式環境中的其他服務無法訪問到需要灰度的服務,所以需要在灰度環境中冗餘部署這些線上服務,以便整個呼叫鏈路正常進行流量轉發。此外,註冊中心等一些其他依賴的中介軟體元件也需要冗餘部署在灰度環境中,保證微服務之間的可見性問題,確保獲取的節點 IP 地址只屬於當前的網路環境。

這個方案一般用於企業的測試、預發開發環境的搭建,對於線上灰度釋出引流的場景來說其靈活性不夠。況且,微服務多版本的存在在微服務架構中是家常便飯,需要為這些業務場景採用堆機器的方式來維護多套灰度環境。如果您的應用數目過多的情況下,會造成運維、機器成本過大,成本和代價遠超收益;如果應用數目很小,就兩三個應用,這個方式還是很方便的,可以接受的。

邏輯環境隔離

另一種方案是構建邏輯上的環境隔離,我們只需部署服務的灰度版本,流量在呼叫鏈路上流轉時,由流經的閘道器、各個中介軟體以及各個微服務來識別灰度流量,並動態轉發至對應服務的灰度版本。如下圖:

9.png

上圖可以很好展示這種方案的效果,我們用不同的顏色來表示不同版本的灰度流量,可以看出無論是微服務閘道器還是微服務本身都需要識別流量,根據治理規則做出動態決策。當服務版本發生變化時,這個呼叫鏈路的轉發也會實時改變。相比於利用機器搭建的灰度環境,這種方案不僅可以節省大量的機器成本和運維人力,而且可以幫助開發者實時快速的對線上流量進行精細化的全鏈路控制。

那麼全鏈路灰度具體是如何實現呢?通過上面的討論,我們需要解決以下問題:

  1. 鏈路上各個元件和服務能夠根據請求流量特徵進行動態路由
  2. 需要對服務下的所有節點進行分組,能夠區分版本
  3. 需要對流量進行灰度標識、版本標識
  4. 需要識別出不同版本的灰度流量

接下來,會介紹解決上述問題需要用到的技術。

標籤路由

標籤路由通過對服務下所有節點按照標籤名和標籤值不同進行分組,使得訂閱該服務節點資訊的服務消費端可以按需訪問該服務的某個分組,即所有節點的一個子集。服務消費端可以使用服務提供者節點上的任何標籤資訊,根據所選標籤的實際含義,消費端可以將標籤路由應用到更多的業務場景中。

10.png

節點打標

那麼如何給服務節點新增不同的標籤呢?在如今火熱的雲原生技術推動下,大多數業務都在積極進行容器化改造之旅。這裡,我就以容器化的應用為例,介紹在使用 Kubernetes Service 作為服務發現和使用比較流行的 Nacos 註冊中心這兩種場景下如何對服務 Workload 進行節點打標。

在使用 Kubernetes Service 作為服務發現的業務系統中,服務提供者通過向 ApiServer 提交 Service 資源完成服務暴露,服務消費端監聽與該 Service 資源下關聯的 Endpoint 資源,從 Endpoint 資源中獲取關聯的業務 Pod 資源,讀取上面的 Labels 資料並作為該節點的後設資料資訊。所以,我們只要在業務應用描述資源 Deployment 中的 Pod 模板中為節點新增標籤即可。

11.png

在使用 Nacos 作為服務發現的業務系統中,一般是需要業務根據其使用的微服務框架來決定打標方式。如果 Java 應用使用的 Spring Cloud 微服務開發框架,我們可以為業務容器新增對應的環境變數來完成標籤的新增操作。比如我們希望為節點新增版本灰度標,那麼為業務容器新增spring.cloud.nacos.discovery.metadata.version=gray,這樣框架向Nacos註冊該節點時會為其新增一個標籤verison=gray

12.png

流量染色

請求鏈路上各個元件如何識別出不同的灰度流量?答案就是流量染色,為請求流量新增不同灰度標識來方便區分。我們可以在請求的源頭上對流量進行染色,前端在發起請求時根據使用者資訊或者平臺資訊的不同對流量進行打標。如果前端無法做到,我們也可以在微服務閘道器上對匹配特定路由規則的請求動態新增流量標識。此外,流量在鏈路中流經灰度節點時,如果請求資訊中不含有灰度標識,需要自動為其染色,接下來流量就可以在後續的流轉過程中優先訪問服務的灰度版本。

分散式鏈路追蹤

還有一個很重要的問題是如何保證灰度標識能夠在鏈路中一直傳遞下去呢?如果在請求源頭染色,那麼請求經過閘道器時,閘道器作為代理會將請求原封不動的轉發給入口服務,除非開發者在閘道器的路由策略中實施請求內容修改策略。接著,請求流量會從入口服務開始呼叫下一個微服務,會根據業務程式碼邏輯形成新的呼叫請求,那麼我們如何將灰度標識新增到這個新的呼叫請求,從而可以在鏈路中傳遞下去呢?

從單體架構演進到分散式微服務架構,服務之間呼叫從同一個執行緒中方法呼叫變為從本地程式的服務呼叫遠端程式中服務,並且遠端服務可能以多副本形式部署,以至於一條請求流經的節點是不可預知的、不確定的,而且其中每一跳的呼叫都有可能因為網路故障或服務故障而出錯。分散式鏈路追蹤技術對大型分散式系統中請求呼叫鏈路進行詳細記錄,核心思想就是通過一個全域性唯一的 traceid 和每一條的 spanid 來記錄請求鏈路所經過的節點以及請求耗時,其中 traceid 是需要整個鏈路傳遞的。

藉助於分散式鏈路追蹤思想,我們也可以傳遞一些自定義資訊,比如灰度標識。業界常見的分散式鏈路追蹤產品都支援鏈路傳遞使用者自定義的資料,其資料處理流程如下圖所示:

13.png

邏輯環境隔離——基於 SDK

上面我們詳細介紹了實現全鏈路灰度所需要的幾種技術,如果想為現有的業務接入全鏈路灰度能力,不可避免的需要為業務使用的開發框架 SDK 進行改造。首先,需要支援動態路由功能,對於 Spring Cloud、Dubbo 開發框架,可以對出口流量實現自定義 Filter,在該 Filter 中完成流量識別以及標籤路由。同時需要藉助分散式鏈路追蹤技術完成流量標識鏈路傳遞以及流量自動染色。此外,需要引入一箇中心化的流量治理平臺,方便各個業務線的開發者定義自己的全鏈路灰度規則。基於 SDK 實現方式的圖例如下:

14.png

邏輯環境隔離——基於 Java Agent

基於 SDK 方式的弊端在於需要業務進行 SDK 版本升級,甚至會涉及到業務程式碼的變動。企業內部各個微服務雖然使用同一種開發框架,但很難保證框架版本是一致的,所以不得不為每一個版本維護一份全鏈路灰度的程式碼。業務程式碼與 SDK 程式碼緊耦合,SDK 版本迭代會觸發業務不必要的發版變更,對業務的侵入性比較強。

另一種比較流行的方式是基於位元組碼增強技術在編譯時對開發框架進行功能擴充,這種方案業務無感知,以無侵入方式為業務引入全鏈路灰度能力。基於 Java Agent 的實現方式的圖例如下:

15.png

但仍然無法避免是開發者需要為業務使用版本不一致的開發框架維護對應的 Java Agent 的版本。如果您比較傾向於這種無侵入的方案但又不想自己來維護,您可以選擇阿里雲 MSE 服務治理產品,該產品就是一款基於 Java Agent 實現的無侵入式企業生產級服務治理產品,您不需要修改任何一行業務程式碼,即可擁有不限於全鏈路灰度的治理能力,並且支援近 5 年內所有的 Spring Boot、Spring Cloud 和 Dubbo。

邏輯環境隔離——基於 Service Mesh

在業務系統的微服務架構中,如果存在大量使用不同的技術棧、語言棧的微服務,Java Agent 的方式就無能為力了。我們可能需要為每一個語言的 SDK 編寫和維護全鏈路灰度程式碼,不僅需要不同語言棧的開發者,而且涉及到語言無關的 bug 修復時需要全語言版本的 SDK 共同升級,這種代價不見得比基於物理環境隔離方案小。

那有沒有一種與語言無關的方案呢?有,下一代微服務架構服務網格,Service Mesh。它將分散式服務的通訊層抽象為單獨的一層,在這一層中實現負載均衡、服務發現、認證授權、監控追蹤、流量控制等分散式系統所需要的功能。顯然,我們所需的全鏈路灰度能力也可以在這個流量治理基礎設施層來實現。幸運的是,服務網格明星產品Istio以宣告式 API 資源對流量治理進行了統一抽象,藉助於 VirtualService 和 DestinationRule 治理規則可以很容易實現全鏈路灰度的效果,並且Istio整合了各種主流的分散式鏈路追蹤框架。基於 Service Mesh 的實現方式的圖例如下:

16.png

在實際生產環境中,服務多版本並行開發是很常見的事情,而且版本迭代速度非常快。版本每次變更都需要修改 VirtualSerivice 資源中路由匹配規則,另外 VirtualSerivice 資源中並沒有提供容災能力。比如存在一條路由規則訪問服務提供方的某個灰度版本,如果目標服務不存在該灰度版本或者不可用,按照目前 Istio 的實現是仍然將流量轉發至該版本,缺乏容災機制。還有一種業務場景,如果我們希望對處於一定 UID 範圍的使用者流量轉發指定灰度環境,是無法通過 Istio 現有的流量治理規則實現的。此時,您可以選擇阿里雲服務網格產品 ASM,是一個統一管理微服務應用流量、相容 Istio 的託管式平臺。ASM 針對上述兩個場景都有應對方案,輕鬆解決您在多語言場景下的全鏈路灰度訴求。

三種方式對比

下表是三種方式對比,從多個方面進行了對比。

17.png

  • 如果您傾向於使用無侵入式的 Java Agent 的方式,但又擔心自建帶來的穩定性問題,您可以選擇 MSE 微服務治理產品,該產品是阿里巴巴內部多年在微服務治理領域的沉澱的產出,經歷了各種大促考驗。
  • 如果您傾向於使用語言無關、無侵入式的 Service Mesh 的方式,但又擔心自建帶來的穩定性問題,您可以選擇阿里雲 ASM 產品,相比開源 Istio,在功能性、穩定性和安全性都有很大的提升。

流量入口:閘道器

在分散式應用中,作為流量入口的閘道器是不可或缺的。在全鏈路灰度場景中,就要求微服務閘道器具備豐富的流量治理能力,支援服務多版本路由,支援對特定路由規則上的請求進行動態打標。對於入口服務可見性問題,閘道器需要支援多種服務發現方式。安全性問題上,閘道器作為叢集對外的入口可以對所有請求流量進行認證鑑權,保障業務系統不被非法流量入侵。

在虛擬化時期的微服務架構下,業務通常採用流量閘道器 + 微服務閘道器的兩層架構,流量閘道器負責南北向流量排程和安全防護,微服務閘道器負責東西向流量排程和服務治理。在容器和 K8s 主導的雲原生時代,Ingress 成為 K8s 生態的閘道器標準,賦予了閘道器新的使命,使得流量閘道器 + 微服務閘道器合二為一成為可能。阿里雲 MSE 釋出的雲原生閘道器在能力不打折的情況下,將兩層閘道器變為一層,不僅可以節省 50% 的資源成本,還可以降低運維及使用成本。最重要的是,雲原生閘道器支援與後端微服務治理聯動實現端到端的全鏈路灰度。

18.png

從 0 到 1 實踐全鏈路灰度

看到這裡,相信大多數讀者對全鏈路灰度有了大概的認識,也瞭解了幾種解決方案以及實現細節。接下來,我們會基於文中提到的 MSE 雲原生閘道器和 MSE 服務治理產品,從 0 到 1 對全鏈路灰度進行實踐,一方面加深對全鏈路灰度的認識,另一方面可以瞭解下阿里雲是如何將阿里巴巴內部的最佳實踐輸出到雲產品中。

我們假設應用的架構由 MSE 雲原生閘道器以及後端的微服務架構(Spring Cloud)來組成,後端呼叫鏈路有 3 跳,購物車(a),交易中心(b),庫存中心(c),通過客戶端或者是 H5 頁面來訪問後端服務,它們通過 Nacos 註冊中心做服務發現。現在希望使用全鏈路灰度能力構建一個灰度環境,方便對服務 A 和服務 C 同時進行灰度驗證。

19.png

前提條件

必備的資源列表

  • 已擁有一個 MSE 雲原生閘道器
  • 已擁有一個 MSE Nacos 註冊中心
  • 已擁有一個 ACK 運維叢集
  • 已開通 MSE 微服務治理專業版

部署 Demo 應用程式

將下面的檔案儲存到 ingress-gray.yaml 中,並執行 ​​kubectl apply -f ingress-gray.yaml​​ 以部署應用,這裡我們將要部署 A,B,C 三個應用,A 和 C 應用分別部署一個基線版本和一個灰度版本,B 應用部署一個基線版本。

有以下注意點:

  1. 全鏈路灰度能力是與註冊中心無關的,本文用例暫以 MSE Nacos 作為註冊中心,所以需要將 spring.cloud.nacos.discovery.server-addr 換成業務自己的 Nacos 註冊中心地址
  2. 接入雲原生閘道器的服務,如果需要使用灰度釋出,需要在釋出服務時在後設資料資訊增加版本標。在我們的例子,服務 A 是需要暴露給閘道器,所以釋出時為基線版本新增spring.cloud.nacos.discovery.metadata.version=base,為灰度版本新增 spring.cloud.nacos.discovery.metadata.version=gray。
# A 應用 base 版本
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: spring-cloud-a
  name: spring-cloud-a
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-cloud-a
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-a
      labels:
        app: spring-cloud-a
    spec:
      containers:
      - env:
        - name: LANG
          value: C.UTF-8
        - name: JAVA_HOME
          value: /usr/lib/jvm/java-1.8-openjdk/jre
        - name: spring.cloud.nacos.discovery.server-addr
          value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
        - name: spring.cloud.nacos.discovery.metadata.version
          value: base
        image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:0.1-SNAPSHOT
        imagePullPolicy: Always
        name: spring-cloud-a
        ports:
        - containerPort: 20001
          protocol: TCP
        resources:
          requests:
            cpu: 250m
            memory: 512Mi
      
# A 應用 gray 版本
---            
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: spring-cloud-a-new
  name: spring-cloud-a-new
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-cloud-a-new
  strategy:
  template:
    metadata:
      annotations:
        alicloud.service.tag: gray
        msePilotCreateAppName: spring-cloud-a
      labels:
        app: spring-cloud-a-new
    spec:
      containers:
      - env:
        - name: LANG
          value: C.UTF-8
        - name: JAVA_HOME
          value: /usr/lib/jvm/java-1.8-openjdk/jre
        - name: profiler.micro.service.tag.trace.enable
          value: "true"
        - name: spring.cloud.nacos.discovery.server-addr
          value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
        - name: spring.cloud.nacos.discovery.metadata.version
          value: gray
        image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:0.1-SNAPSHOT
        imagePullPolicy: Always
        name: spring-cloud-a-new
        ports:
        - containerPort: 20001
          protocol: TCP
        resources:
          requests:
            cpu: 250m
            memory: 512Mi
            
# B 應用 base 版本
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: spring-cloud-b
  name: spring-cloud-b
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-cloud-b
  strategy:
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-b
      labels:
        app: spring-cloud-b
    spec:
      containers:
      - env:
        - name: LANG
          value: C.UTF-8
        - name: JAVA_HOME
          value: /usr/lib/jvm/java-1.8-openjdk/jre
        - name: spring.cloud.nacos.discovery.server-addr
          value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
        image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:0.2-demo-SNAPSHOT 
        imagePullPolicy: Always
        name: spring-cloud-b
        ports:
        - containerPort: 8080
          protocol: TCP
        resources:
          requests:
            cpu: 250m
            memory: 512Mi
            
# C 應用 base 版本
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: spring-cloud-c
  name: spring-cloud-c
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-cloud-c
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-c
      labels:
        app: spring-cloud-c
    spec:
      containers:
      - env:
        - name: LANG
          value: C.UTF-8
        - name: JAVA_HOME
          value: /usr/lib/jvm/java-1.8-openjdk/jre
        - name: spring.cloud.nacos.discovery.server-addr
          value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
        image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:0.2-demo-SNAPSHOT
        imagePullPolicy: Always
        name: spring-cloud-c
        ports:
        - containerPort: 8080
          protocol: TCP
        resources:
          requests:
            cpu: 250m
            memory: 512Mi
            
# C 應用 gray 版本
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: spring-cloud-c-new
  name: spring-cloud-c-new
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring-cloud-c-new
  template:
    metadata:
      annotations:
        alicloud.service.tag: gray
        msePilotCreateAppName: spring-cloud-c
      labels:
        app: spring-cloud-c-new
    spec:
      containers:
      - env:
        - name: LANG
          value: C.UTF-8
        - name: JAVA_HOME
          value: /usr/lib/jvm/java-1.8-openjdk/jre
        - name: spring.cloud.nacos.discovery.server-addr
          value: mse-455e0c20-nacos-ans.mse.aliyuncs.com:8848
        image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:0.2-demo-SNAPSHOT
        imagePullPolicy: Always
        name: spring-cloud-c-new
        ports:
        - containerPort: 8080
          protocol: TCP
        resources:
          requests:
            cpu: 250m
            memory: 512Mi

完成雲原生閘道器初步配置

第一步,為雲原生閘道器新增 Nacos 服務來源,服務管理,來源管理,點選建立來源,

20.png

選擇 MSE Nacos 服務來源,選擇需要關聯的 Nacos 註冊中心,點選確定。

21.png

第二步,匯入要通過雲原生閘道器暴露給外部的服務。選擇服務管理,服務列表,點選建立服務。

選擇服務來源為 MSE Nacos,選擇服務 sc-A。

22.png

點選服務 A 的策略配置,為入口服務 A 建立多版本,版本劃分依據服務註冊時所帶的後設資料資訊 version(注意,這裡可以是任意可以區分服務版本的標籤值,取決於使用者註冊服務時所採用的後設資料資訊),建立以下兩個版本 base 和 gray。

23.png

路由配置

建立基線環境的路由匹配規則,關聯域名 base.example.com,路由到服務 sc-A 的 base 版本中。

24.png

建立灰度環境的路由匹配規則,關聯的域名與基線環境保持一致,注意此處增加了請求頭相關的配置,並且路由的目標服務為服務 sc-A 的 gray 版本。

25.png

這時,我們有了如下兩條路由規則,

26.png

此時,訪問 ​base.example.com​ 路由到基線環境

curl -H "Host: base.example.com" http://118.31.118.69/a
A[172.21.240.105] -> B[172.21.240.106] -> C[172.21.240.46]

如何訪問灰度環境呢?只需要在請求中增加一個 ​header x-mse-tag: gray​ 即可。

curl -H "Host: base.example.com" -H "x-mse-tag: gray" http://118.31.118.69/a
Agray[172.21.240.44] -> B[172.21.240.146] -> Cgray[172.21.240.147]

可以看到 雲原生閘道器 將灰度流量路由到了 A 和 C 的灰度版本,由於B沒有指定的灰度版本,所以流量自動回退到基線版本。

分析

從上面可以看出,我們只需要開通 MSE 微服務治理專業版,在雲原生閘道器配置入口服務的路由規則,並且對入口流量進行灰度染色,即可滿足我們對 A 和 C 全鏈路灰度的要求。另外還有一個很重要的點是,業務需要自行對節點打標並對入口服務開啟鏈路傳遞。為 Pod 模板的 Annotations新增 alicloud.service.tag 鍵值對完成節點打標,Java Agent 會在業務向註冊中心時登記節點時自動為其新增這個後設資料資訊,同時需要為入口服務的業務容器新增環境變數 ​​profiler.micro.service.tag.trace.enable=true​​ 開啟鏈路傳遞灰度標識。MSE 服務治理元件預設使用 x-mse-tag 來標識流量,並在整個呼叫鏈路中傳遞。

另外,您可以設定其他自定義或業務已有的欄位來標識流量,更多詳情操作、場景展示參考文件:​https://help.aliyun.com/document_detail/359851.html

總結

本文從單體架構向微服務架構演進過程中帶來的挑戰展開,著重對其子領域服務釋出在單體架構和微服務架構體系下的形態進行分析,引出了分散式應用場景中特有的全鏈路灰度問題。針對業務對全鏈路能力的要求,介紹了基於物理環境隔離和基於邏輯環境隔離兩種方案,其中對基於邏輯環境隔離方案進行詳細分析,對涉及到的各個技術點也進行了很好的解釋,接著提出了三種基於邏輯環境隔離的落地方案,並進行了簡單的對比分析,最後引出了阿里雲 MSE 雲原生、MSE 服務治理和服務網格 ASM 是如何提供不限於全鏈路灰度的流量治理能力。

27.png

最後,對閘道器、微服務治理領域感興趣的同學,可以釘釘搜尋群號 34754806 或掃描下方二維碼加入使用者群交流、答疑。

點選​此處​,檢視微服務治理之全鏈路灰度直播回放!

相關文章