深入剖析全链路灰度技术
作者:揚少
當服務有新版本要發布上線時,通過引流一小部分流量到新版本,可以及時發現程序問題,有效阻止大面積故障的發生。業界上已經有比較成熟的服務發布策略,比如藍綠發布、A/B 測試以及金絲雀發布,這些發布策略主要專注于如何對單個服務進行發布。在微服務體系架構中,服務之間的依賴關系錯綜復雜,有時某個功能發版依賴多個服務同時升級上線。我們希望可以對這些服務的新版本同時進行小流量灰度驗證,這就是微服務架構中特有的全鏈路灰度場景,通過構建從網關到整個后端服務的環境隔離來對多個不同版本的服務進行灰度驗證。
本文將會揭開全鏈路灰度的神秘面紗,深入剖析全鏈路灰度技術內幕,引出兩種不同的實現方案,并對實現方案的技術細節進行深入探討,最后通過實踐環節來展示全鏈路灰度在實際業務中的使用場景。
微服務架構帶來的挑戰
為了滿足業務的迭代速度,開發者開始對原來的單體架構進行細粒度的拆分,將單體應用中的服務模塊拆分成一個個獨立部署運行的微服務,并且這些微服務的生命周期由對應的業務團隊獨自負責,有效的解決了單體架構中存在的敏捷性不足、靈活性不強的問題。常見的做法是根據業務域或者功能域進行服務拆分,如下圖:
其中,流量網關是四層代理,主要功能有負載均衡、TLS 卸載以及一些安全防護功能;微服務網關是七層代理,主要用來暴露后端服務、流量治理、訪問控制和流量監控。以"高內聚、低耦合"作為設計理念的微服務架構為開發者帶來了前所未有的開發體驗,每個業務團隊專注于自身業務的代碼邏輯,并通過 API 形式對外發布。服務依賴方只需引入服務提供方的 API 定義,即可完成服務之間通信,無需關心服務提供方的部署形態和內部實現。
但任何架構都不是銀彈,在解決舊問題同時勢必會引入一些新的問題。微服務體系中最令人頭疼的問題,是如何對眾多微服務進行高效、便捷的治理,主要表現在可見性、連接性和安全性這三個方面。進一步細化,微服務架構帶來了以下的挑戰:
本文的重點主要關注服務發布這一子領域,如何保證微服務體系中服務新版本升級過程中平滑無損,以及如何低成本的為多個微服務構建流量隔離環境,方便開發者同時對多個服務新版本進行充分的灰度驗證,避免故障的發生。
什么是全鏈路灰度
單體架構下的服務發布
首先,我們先看一下在單體架構中,如何對應用中某個服務模塊進行新版本發布。如下圖,應用中的 Cart 服務模塊有新版本迭代:
由于 Cart 服務是應用的一部分,所以新版本上線時需要對整個應用進行編譯、打包以及部署。服務級別發布問題變成了應用級別的發布問題,我們需要對應用的新版本而不是服務來實施有效的發布策略。
目前,業界已經有非常成熟的服務發布方案,例如藍綠發布和灰度發布。藍綠發布需要對服務的新版本進行冗余部署,一般新版本的機器規格和數量與舊版本保持一致,相當于該服務有兩套完全相同的部署環境,只不過此時只有舊版本在對外提供服務,新版本作為熱備。當服務進行版本升級時,我們只需將流量全部切換到新版本即可,舊版本作為熱備。我們的例子使用藍綠發布的示意圖如下,流量切換基于四層代理的流量網關即可完成。
在藍綠發布中,由于存在流量整體切換,所以需要按照原服務占用的機器規模為新版本克隆一套環境,相當于要求原來1倍的機器資源。灰度發布的核心思想是根據請求內容或者請求流量的比例將線上流量的一小部分轉發至新版本,待灰度驗證通過后,逐步調大新版本的請求流量,是一種循序漸進的發布方式。我們的例子使用灰度發布的示意圖如下,基于內容或比例的流量控制需要借助于一個七層代理的微服務網關來完成。
其中,Traffic Routing 是基于內容的灰度方式,比如請求中含有頭部 stag=gray 的流量路由到應用 v2 版本;Traffic Shifting 是基于比例的灰度方式,以無差別的方式對線上流量按比重進行分流。相比藍綠發布,灰度發布在機器資源成本以及流量控制能力上更勝一籌,但缺點就是發布周期過長,對運維基礎設施要求較高。?
微服務架構下的服務發布
在分布式微服務架構中,應用中被拆分出來的子服務都是獨立部署、運行和迭代的。單個服務新版本上線時,我們再也不需要對應用整體進行發版,只需關注每個微服務自身的發布流程即可,如下:
為了驗證服務 Cart 的新版本,流量在整個調用鏈路上能夠通過某種方式有選擇的路由到 Cart 的灰度版本,這屬于微服務治理領域中流量治理問題。常見的治理策略包括基于 Provider 和基于 Consumer 的方式。
-
基于 Provider 的治理策略。配置 Cart 的流量流入規則,User 路由到 Cart 時使用 Cart 的流量流入規則。
-
基于 Consumer 的治理策略。配置 User 的流量流出規則, User 路由到 Cart 時使用 User 的流量流出規則。
此外,使用這些治理策略時可以結合上面介紹的藍綠發布和灰度發布方案來實施真正的服務級別的版本發布。
全鏈路灰度
繼續考慮上面微服務體系中對服務 Cart 進行發布的場景,如果此時服務 Order 也需要發布新版本,由于本次新功能涉及到服務 Cart 和 Order 的共同變動,所以要求在灰度驗證時能夠使得灰度流量同時經過服務 Cart 和 Order 的灰度版本。如下圖:
按照上一小節提出的兩種治理策略,我們需要額外配置服務 Order 的治理規則,確保來自灰度環境的服務 Cart 的流量轉發至服務 Order 的灰度版本。這樣的做法看似符合正常的操作邏輯,但在真實業務場景中,業務的微服務規模和數量遠超我們的例子,其中一條請求鏈路可能經過數十個微服務,新功能發布時也可能會涉及到多個微服務同時變更,并且業務的服務之間依賴錯綜復雜,頻繁的服務發布、以及服務多版本并行開發導致流量治理規則日益膨脹,給整個系統的維護性和穩定性帶來了不利因素。
對于以上的問題,開發者結合實際業務場景和生產實踐經驗,提出了一種端到端的灰度發布方案,即全鏈路灰度。全鏈路灰度治理策略主要專注于整個調用鏈,它不關心鏈路上經過具體哪些微服務,流量控制視角從服務轉移至請求鏈路上,僅需要少量的治理規則即可構建出從網關到整個后端服務的多個流量隔離環境,有效保證了多個親密關系的服務順利安全發布以及服務多版本并行開發,進一步促進業務的快速發展。
全鏈路灰度的解決方案
如何在實際業務場景中去快速落地全鏈路灰度呢?目前,主要有兩種解決思路,基于物理環境隔離和基于邏輯環境隔離。
物理環境隔離
物理環境隔離,顧名思義,通過增加機器的方式來搭建真正意義上的流量隔離。
這種方案需要為要灰度的服務搭建一套網絡隔離、資源獨立的環境,在其中部署服務的灰度版本。由于與正式環境隔離,正式環境中的其他服務無法訪問到需要灰度的服務,所以需要在灰度環境中冗余部署這些線上服務,以便整個調用鏈路正常進行流量轉發。此外,注冊中心等一些其他依賴的中間件組件也需要冗余部署在灰度環境中,保證微服務之間的可見性問題,確保獲取的節點 IP 地址只屬于當前的網絡環境。
這個方案一般用于企業的測試、預發開發環境的搭建,對于線上灰度發布引流的場景來說其靈活性不夠。況且,微服務多版本的存在在微服務架構中是家常便飯,需要為這些業務場景采用堆機器的方式來維護多套灰度環境。如果您的應用數目過多的情況下,會造成運維、機器成本過大,成本和代價遠超收益;如果應用數目很小,就兩三個應用,這個方式還是很方便的,可以接受的。
邏輯環境隔離
另一種方案是構建邏輯上的環境隔離,我們只需部署服務的灰度版本,流量在調用鏈路上流轉時,由流經的網關、各個中間件以及各個微服務來識別灰度流量,并動態轉發至對應服務的灰度版本。如下圖:
上圖可以很好展示這種方案的效果,我們用不同的顏色來表示不同版本的灰度流量,可以看出無論是微服務網關還是微服務本身都需要識別流量,根據治理規則做出動態決策。當服務版本發生變化時,這個調用鏈路的轉發也會實時改變。相比于利用機器搭建的灰度環境,這種方案不僅可以節省大量的機器成本和運維人力,而且可以幫助開發者實時快速的對線上流量進行精細化的全鏈路控制。
那么全鏈路灰度具體是如何實現呢?通過上面的討論,我們需要解決以下問題:
接下來,會介紹解決上述問題需要用到的技術。
標簽路由
標簽路由通過對服務下所有節點按照標簽名和標簽值不同進行分組,使得訂閱該服務節點信息的服務消費端可以按需訪問該服務的某個分組,即所有節點的一個子集。服務消費端可以使用服務提供者節點上的任何標簽信息,根據所選標簽的實際含義,消費端可以將標簽路由應用到更多的業務場景中。
節點打標
那么如何給服務節點添加不同的標簽呢?在如今火熱的云原生技術推動下,大多數業務都在積極進行容器化改造之旅。這里,我就以容器化的應用為例,介紹在使用 Kubernetes Service 作為服務發現和使用比較流行的 Nacos 注冊中心這兩種場景下如何對服務 Workload 進行節點打標。
在使用 Kubernetes Service 作為服務發現的業務系統中,服務提供者通過向 ApiServer 提交 Service 資源完成服務暴露,服務消費端監聽與該 Service 資源下關聯的 Endpoint 資源,從 Endpoint 資源中獲取關聯的業務 Pod 資源,讀取上面的 Labels 數據并作為該節點的元數據信息。所以,我們只要在業務應用描述資源 Deployment 中的 Pod 模板中為節點添加標簽即可。
在使用 Nacos 作為服務發現的業務系統中,一般是需要業務根據其使用的微服務框架來決定打標方式。如果 Java 應用使用的 Spring Cloud 微服務開發框架,我們可以為業務容器添加對應的環境變量來完成標簽的添加操作。比如我們希望為節點添加版本灰度標,那么為業務容器添加spring.cloud.nacos.discovery.metadata.version=gray,這樣框架向Nacos注冊該節點時會為其添加一個標簽verison=gray。
流量染色
請求鏈路上各個組件如何識別出不同的灰度流量?答案就是流量染色,為請求流量添加不同灰度標識來方便區分。我們可以在請求的源頭上對流量進行染色,前端在發起請求時根據用戶信息或者平臺信息的不同對流量進行打標。如果前端無法做到,我們也可以在微服務網關上對匹配特定路由規則的請求動態添加流量標識。此外,流量在鏈路中流經灰度節點時,如果請求信息中不含有灰度標識,需要自動為其染色,接下來流量就可以在后續的流轉過程中優先訪問服務的灰度版本。
分布式鏈路追蹤
還有一個很重要的問題是如何保證灰度標識能夠在鏈路中一直傳遞下去呢?如果在請求源頭染色,那么請求經過網關時,網關作為代理會將請求原封不動的轉發給入口服務,除非開發者在網關的路由策略中實施請求內容修改策略。接著,請求流量會從入口服務開始調用下一個微服務,會根據業務代碼邏輯形成新的調用請求,那么我們如何將灰度標識添加到這個新的調用請求,從而可以在鏈路中傳遞下去呢?
從單體架構演進到分布式微服務架構,服務之間調用從同一個線程中方法調用變為從本地進程的服務調用遠端進程中服務,并且遠端服務可能以多副本形式部署,以至于一條請求流經的節點是不可預知的、不確定的,而且其中每一跳的調用都有可能因為網絡故障或服務故障而出錯。分布式鏈路追蹤技術對大型分布式系統中請求調用鏈路進行詳細記錄,核心思想就是通過一個全局唯一的 traceid 和每一條的 spanid 來記錄請求鏈路所經過的節點以及請求耗時,其中 traceid 是需要整個鏈路傳遞的。
借助于分布式鏈路追蹤思想,我們也可以傳遞一些自定義信息,比如灰度標識。業界常見的分布式鏈路追蹤產品都支持鏈路傳遞用戶自定義的數據,其數據處理流程如下圖所示:
邏輯環境隔離——基于 SDK
上面我們詳細介紹了實現全鏈路灰度所需要的幾種技術,如果想為現有的業務接入全鏈路灰度能力,不可避免的需要為業務使用的開發框架 SDK 進行改造。首先,需要支持動態路由功能,對于 Spring Cloud、Dubbo 開發框架,可以對出口流量實現自定義 Filter,在該 Filter 中完成流量識別以及標簽路由。同時需要借助分布式鏈路追蹤技術完成流量標識鏈路傳遞以及流量自動染色。此外,需要引入一個中心化的流量治理平臺,方便各個業務線的開發者定義自己的全鏈路灰度規則。基于 SDK 實現方式的圖例如下:
邏輯環境隔離——基于 Java Agent
基于 SDK 方式的弊端在于需要業務進行 SDK 版本升級,甚至會涉及到業務代碼的變動。企業內部各個微服務雖然使用同一種開發框架,但很難保證框架版本是一致的,所以不得不為每一個版本維護一份全鏈路灰度的代碼。業務代碼與 SDK 代碼緊耦合,SDK 版本迭代會觸發業務不必要的發版變更,對業務的侵入性比較強。
另一種比較流行的方式是基于字節碼增強技術在編譯時對開發框架進行功能拓展,這種方案業務無感知,以無侵入方式為業務引入全鏈路灰度能力。基于 Java Agent 的實現方式的圖例如下:
但仍然無法避免是開發者需要為業務使用版本不一致的開發框架維護對應的 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 的實現方式的圖例如下:
在實際生產環境中,服務多版本并行開發是很常見的事情,而且版本迭代速度非常快。版本每次變更都需要修改 VirtualSerivice 資源中路由匹配規則,另外 VirtualSerivice 資源中并沒有提供容災能力。比如存在一條路由規則訪問服務提供方的某個灰度版本,如果目標服務不存在該灰度版本或者不可用,按照目前 Istio 的實現是仍然將流量轉發至該版本,缺乏容災機制。還有一種業務場景,如果我們希望對處于一定 UID 范圍的用戶流量轉發指定灰度環境,是無法通過 Istio 現有的流量治理規則實現的。此時,您可以選擇阿里云服務網格產品 ASM,是一個統一管理微服務應用流量、兼容 Istio 的托管式平臺。ASM 針對上述兩個場景都有應對方案,輕松解決您在多語言場景下的全鏈路灰度訴求。
三種方式對比
下表是三種方式對比,從多個方面進行了對比。
-
如果您傾向于使用無侵入式的 Java Agent 的方式,但又擔心自建帶來的穩定性問題,您可以選擇 MSE 微服務治理產品,該產品是阿里巴巴內部多年在微服務治理領域的沉淀的產出,經歷了各種大促考驗。
-
如果您傾向于使用語言無關、無侵入式的 Service Mesh 的方式,但又擔心自建帶來的穩定性問題,您可以選擇阿里云 ASM 產品,相比開源 Istio,在功能性、穩定性和安全性都有很大的提升。
流量入口:網關
在分布式應用中,作為流量入口的網關是不可或缺的。在全鏈路灰度場景中,就要求微服務網關具備豐富的流量治理能力,支持服務多版本路由,支持對特定路由規則上的請求進行動態打標。對于入口服務可見性問題,網關需要支持多種服務發現方式。安全性問題上,網關作為集群對外的入口可以對所有請求流量進行認證鑒權,保障業務系統不被非法流量入侵。
在虛擬化時期的微服務架構下,業務通常采用流量網關 + 微服務網關的兩層架構,流量網關負責南北向流量調度和安全防護,微服務網關負責東西向流量調度和服務治理。在容器和 K8s 主導的云原生時代,Ingress 成為 K8s 生態的網關標準,賦予了網關新的使命,使得流量網關 + 微服務網關合二為一成為可能。阿里云 MSE 發布的云原生網關在能力不打折的情況下,將兩層網關變為一層,不僅可以節省 50% 的資源成本,還可以降低運維及使用成本。最重要的是,云原生網關支持與后端微服務治理聯動實現端到端的全鏈路灰度。
從 0 到 1 實踐全鏈路灰度
看到這里,相信大多數讀者對全鏈路灰度有了大概的認識,也了解了幾種解決方案以及實現細節。接下來,我們會基于文中提到的 MSE 云原生網關和 MSE 服務治理產品,從 0 到 1 對全鏈路灰度進行實踐,一方面加深對全鏈路灰度的認識,另一方面可以了解下阿里云是如何將阿里巴巴內部的最佳實踐輸出到云產品中。
我們假設應用的架構由 MSE 云原生網關以及后端的微服務架構(Spring Cloud)來組成,后端調用鏈路有 3 跳,購物車(a),交易中心(b),庫存中心(c),通過客戶端或者是 H5 頁面來訪問后端服務,它們通過 Nacos 注冊中心做服務發現。現在希望使用全鏈路灰度能力構建一個灰度環境,方便對服務 A 和服務 C 同時進行灰度驗證。
前提條件
必備的資源列表
- 已擁有一個 MSE 云原生網關
- 已擁有一個 MSE Nacos 注冊中心
- 已擁有一個 ACK 運維集群
- 已開通 MSE 微服務治理專業版
部署 Demo 應用程序
將下面的文件保存到 ingress-gray.yaml 中,并執行???kubectl apply -f ingress-gray.yaml???以部署應用,這里我們將要部署 A,B,C 三個應用,A 和 C 應用分別部署一個基線版本和一個灰度版本,B 應用部署一個基線版本。
有以下注意點:
全鏈路灰度能力是與注冊中心無關的,本文用例暫以 MSE Nacos 作為注冊中心,所以需要將 spring.cloud.nacos.discovery.server-addr 換成業務自己的 Nacos 注冊中心地址
接入云原生網關的服務,如果需要使用灰度發布,需要在發布服務時在元數據信息增加版本標。在我們的例子,服務 A 是需要暴露給網關,所以發布時為基線版本添加spring.cloud.nacos.discovery.metadata.version=base,為灰度版本添加 spring.cloud.nacos.discovery.metadata.version=gray。
完成云原生網關初步配置
第一步,為云原生網關添加 Nacos 服務來源,服務管理,來源管理,點擊創建來源,
選擇 MSE Nacos 服務來源,選擇需要關聯的 Nacos 注冊中心,點擊確定。
第二步,導入要通過云原生網關暴露給外部的服務。選擇服務管理,服務列表,點擊創建服務。
選擇服務來源為 MSE Nacos,選擇服務 sc-A。
點擊服務 A 的策略配置,為入口服務 A 創建多版本,版本劃分依據服務注冊時所帶的元數據信息 version(注意,這里可以是任意可以區分服務版本的標簽值,取決于用戶注冊服務時所采用的元數據信息),創建以下兩個版本 base 和 gray。
路由配置
創建基線環境的路由匹配規則,關聯域名 base.example.com,路由到服務 sc-A 的 base 版本中。
創建灰度環境的路由匹配規則,關聯的域名與基線環境保持一致,注意此處增加了請求頭相關的配置,并且路由的目標服務為服務 sc-A 的 gray 版本。
這時,我們有了如下兩條路由規則,
此時,訪問??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 是如何提供不限于全鏈路灰度的流量治理能力。
最后,對網關、微服務治理領域感興趣的同學,可以釘釘搜索群號 34754806 或掃描下方二維碼加入用戶群交流、答疑。
點擊??此處??,查看微服務治理之全鏈路灰度直播回放!
總結
以上是生活随笔為你收集整理的深入剖析全链路灰度技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Game On Serverless:S
- 下一篇: 重新定义分析 - EventBridge