全鏈路多重灰度發佈

  1. 什麼是灰度發佈

讓我們從最簡單的情況開始。灰度發佈又叫金絲雀發佈,是一種發佈技術,用於減少軟件新版本發佈的風險。其想法是首先向少數用戶發佈新版本的軟件,然後逐步擴大用戶比例。例如,在這個圖中,我們先測試 10% 的用戶,然後逐漸將更多的用戶轉移到新版本,最後,當所有的用戶都使用了新版本,那麼舊版本就可以被清除並下線。

在整個測試過程中,我們可以給請求流量貼上各種業務標籤,如:用戶的設備:Android 設備、用戶的地理位置:Atlantis,等等。還要注意的是,用戶標籤不應該使用 IP 地址,因爲這是不準確和不一致的。

然後我們可以指定灰度流量規則,將用戶的某一部分流量調度 / 路由到某個灰度版本上來,例如,來自 Atlantis 的 Android 用戶被調度到服務 A 的 2.0 灰度版本。

  1. 什麼是全鏈路灰度發佈

單個服務的灰度發佈的場景仍然是有限的。在現實中,更常見的是全鏈路灰度發佈。例如,一個用戶客戶端不能直接通過路由器轉發到服務的 Canary 版本上。這是因爲該服務在整個請求鏈條中非常靠後,被其他服務隔開。

如圖所示,我們已經爲配送服務發佈了灰度版本,但是用戶和訂單服務在配送服務的前面。在這種情況下,我們需要做兩件事,以確保流量被正確調度。

如果在這裏稍微考慮一下實現層面,我們會發現有兩類方法來做全鏈路灰度發佈;要麼通過逐個改變每個服務裏面的服務調用的代碼的邏輯實現,要麼通過非侵入式的平臺級整體解決方案。而改變代碼可能是非常繁瑣而複雜,且容易出現 bug。

  1. 案例研究:現實世界中的灰度發佈

有了剛纔描述的問題模型,讓我們用另一個實際的例子來說明多灰度發佈的難度。例如,現在有兩個服務 Order v1.0 和 Email v2.0,服務 Order 調用服務 Email,服務 Email 使用第三方的 Email 提供商 A。

而我們決定在 Order 對象中增加一些信息,但先讓 Android 用戶灰度測試。由於對 Order 對象的改變影響到了有關聯兩個服務,我們準備發佈 Order v1.1 和 Email v2.1 來應用新的 Order 對象。

然後,另一個團隊決定在電子郵件服務中用電子郵件提供商 B 取代電子郵件提供商 A,所以我們添加了電子郵件 v2.0.1,只爲測試來自 Atlantis 的用戶。

而如果這兩個灰度發佈同時上了生產環境,我們就會發現問題來了。

如上圖所示,淡藍色的灰度發佈需要的是 Android 用戶,綠色的灰度需要的是 Atlantis 用戶,如果在淡藍色這邊的 Android 用戶也是 Atlantis 用戶,那麼,我是不是還要把他們調度到綠色的灰度上來?反之也一樣。如果我們實現這種邏輯,那麼就需要兩個灰度發佈互相能夠感知到對方。這種在實際中是很難做到的。

這個問題讓我們處於兩難的境地,就是 Android 用戶和 Atlantis 用戶是重疊的,有用戶既是 Android 用戶又是 Atlantis 用戶,於是導致了在兩個灰度發佈中進行流量調度的複雜度。而這種流量的複雜性會導致用戶所不期望的不一致性,這勢必會導致混亂的運維和用戶流量管理,而灰度發佈的流量也不可控。

  1. 多重灰度發佈的挑戰

讓我們退一步,分析一下灰度發佈的不同情況。讓我們首先看下圖。

例如,AB依賴Z,而A'測試 Android 流量,B'測試 iPhone 流量,他們測試的是兩個不同的用戶羣,如果他們都依賴Z'來測試,Z'承擔了兩個不同的灰度流量,將成爲混亂的根源。

那麼有兩種更好的方法,從下圖可以看出。

左邊的是把A'的流量調度Z而不是Z’,右邊的是分別把A'B'的流量調度到Z'Z''。因此,使許多事情變得更容易的原則是:

一個灰度發佈的服務只能在一個灰度規則中!!】

即使解決了上述問題,仍然存在流量規則可能重疊的問題。前面的例子是 Android 和 iPhone 的用戶流量,但如果一個灰度發佈測試 Android,另一個灰度發佈測試 Atlantis,兩個灰度發佈的流量規則將有一個共同的子集。

因此,如果流量來自一個共同的子集,例如來自 Atlantis 的 Android 設備的流量,此時我們應該如何路由這些流量?

這最終成爲兩個集合問題的數學抽象。

  1. 集合匹配:用戶流量是一個集合,灰度發佈的流量規則是一個集合。

  2. 多重匹配問題:多個灰度流量規則被匹配上,應該選擇哪一個。

  3. 案例分析 - 多個灰度規則匹配問題


那麼,讓我們用一個例子來說明前面提到的所有問題,例如,有一個送餐應用的後端服務棧。該服務由三個微服務組成。訂單服務,餐廳服務,和配送服務。

5.1 完美匹配

當系統收到帶有 Atlantis 用戶標籤的流量時,它與配送服務 Atlantis 灰度的路由規則相匹配,流量遵循綠色虛線路徑。

另一邊,帶有 Android 用戶標籤的流量與灰度的餐廳和配送的路由規則相符。該流量遵循藍色虛線路徑。

5.2 多重匹配

但是,對於同時包含 Android 和 Atlantis 標籤的流量會發生什麼?它與所有三個灰度流量規則相匹配,而且沒有明確的方法來引導流量。

從數學集合來看,Atlantis 流量和 Android 流量分別在綠色和藍色的灰度上匹配,而 Atlantis&Android 流量可以在所有灰度規則上匹配。

從數學上看,如果灰度流量是用戶流量的一個子集,那麼灰度規則就是匹配成功的。那麼,我們應該如何處理多重匹配的問題呢?

上圖顯示了,是 Android 也是 Atlantis 的用戶同時屬於三個集合,這就是爲什麼他們會匹配三條灰度規則的原因。

5.3 匹配優先級

解決這個問題的一個簡單易行的方法是指定灰度規則的優先級。每個流量規則都有一個數字表示優先級,數字越小優先級越高。從圖中可以看出,Traffic Atlantis & Android 匹配了三個灰度規則,但由於餐廳灰度的 Atlantis & Android 的優先級最高,即優先級爲 1,因此紅色 Canary 被選中。

5.4 匹配規則遮蔽問題

即使優先權解決了多重匹配的問題,但仍然存在着配置誤用的問題。匹配規則遮蔽問題。在下面這個例子中,紅色規則被藍色規則所遮蔽,因爲藍色的優先級更高,而藍色的集合是紅色集合的超集。這意味着沒有流量被路由到 Atlantis & Android 這個規則上來。

這個跟 C++/Java 中的異常捕捉的道理是一樣的,如果範圍更大的異常在前面的話,那麼後面所定義的異常就會永遠捕捉不到。

  1. 技術實現

實現整個無侵入式的全鏈路多重灰度發佈並不容易,不過,我們已經在我們的開源項目中做到了這一點 - EaseMesh

在這裏我們解釋一下 EaseMesh 的技術細節,並概述一下我們是如何實現多重灰度發佈的。

首先,我們所有的服務都在 Kubernetes Pods 中運行。這裏的三個服務對應於 EaseMesh 中的三個服務,請注意同一服務下的不同版本也是一個服務的一部分。

所以一個 Mesh Service 會有多個版本同時運行。爲了將流量路由到正確的版本,需要完成兩件事。

首先要保證的是流量裏通過服務時帶有的用戶標籤信息,在整個服務調用鏈中不丟失。

這需要 Sidecar 和業務服務協同合作。Sidecar 自然知道所有灰度的流量規則和用戶標籤,比如一些特定的 HTTP Headers,它將與流量一起轉發。

同時,業務應用本身也需要傳遞用戶標籤給 Sidecar,這可以由 EaseMesh 官方提供的 JavaAgent 組件與 Sidecar 合作完成(這兩個組件同樣開源 EaseAgentEasegress),用戶無需做任何代碼的修改,完全透明。Sidecar 會通過 JavaAgent 來傳遞所有和全鏈路灰度相關的信息。至於其他語言如 Golang,由於沒有字節碼技術,只需要一個專用的 SDK 來配合完成即可。

所以 EaseMesh 在這個高級功能上也支持多種語言,只需要透傳用戶標籤即可。

EaseMesh 灰度版本的第二個要求是流量路由。

所有的組件,包括 EaseMesh 的 IngressController,以及每個服務 Pod 中的 Sidecar 都有能力將灰度流量路由到下一個服務的相應的灰度版本。我們可以看到,上圖中所有的服務組件,無論是接收請求還是發送請求,都會通過 Sidecar。而在向外發送請求時,Sidecar 會根據流量特性決定是否需要將流量路由給下一個服務的某個灰度版本。而這些都是由 Sidecar 自動完成,不需要 JavaAgent 或 SDK 的參與。

  1. 總結

現在我們來總結一下平臺的設計原則,以及其使用的最佳實踐。

7.1 設計原則

  1. 一個灰度服務版本最多屬於一個灰度規則。

  2. 一個請求最多隻能被路由到一個灰度規則。

  3. 灰度規則對傳入的流量進行明確的選擇。

  4. 不符合灰度規則的正常流量將走非灰度的正常版本。

7.2 最佳實踐

  1. 標記流量必須使用用戶側的具有業務屬性的信息,而客戶的 IP 地址不是一個好的屬性。

  2. 當標記的流量重疊時,使用明確的優先級來指導流量進行路由。

  3. 應該將範圍更小的灰度規則賦予更高的優先級。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ZmeaR9KqPgZXk2OyPMlIAg