導語:
在 Service Mesh 落地之後,我們曾設想過 Service Mesh 再向前探索可能會遇到的種種困難,包括資源利用率、效能損耗等等,但是未曾想到過去一年中最大的挑戰是研發效能。
研發能效的挑戰當 Service Mesh 的優勢逐漸顯現,越來越多的能力希望下沉到 MOSN 中。而我們也逐漸發現 MOSN 在實際迭代過程中遇到的一些困難,其中比較突出的兩個難題:
1. 變更量大:大量的功能希望進入 MOSN,因此每一個 MOSN 的版本釋出都是一次程式碼量巨大的變更。這對質量保障和技術風險的挑戰都是很大的。
2. 集中式釋出:以前由業務應用升級基礎元件 SDK 的方式,由各個業務應用主動發起升級的釋出,釋出時主要由該業務應用自己監控其系統穩定性。這種升級方式從覆蓋的空間和時間上來說都是分散的,便於灰度和發現問題。而 MOSN 的升級方式則是集中式的。即使按照萬分之一容器數量進行灰度,這個數字都是巨大的。這對質量保障和技術風險的挑戰也是很大的。
面對這些難題,每一個 MOSN 的版本在上線之前需要很長時間的測試,上線過程中需要很長時間的灰度。但是當最終釋出上線後,如果出現一個問題,則又會導致整個版本的回滾,長時間的測試和灰度付之一炬,又需從頭再來。在面對穩定性這個壓倒一切的要求時,退讓的就是 MOSN 的研發效能。即各個功能從開始開發到最終全叢集覆蓋的速度。這是在犧牲 Service Mesh 的核心價值啊!這是我們所不能接受的。因此只有在質量保障和技術風險上突破掉這些挑戰,才能讓 Service Mesh 發揮出它真正的價值。
質量保證在 MOSN 研發流程中,質量保障作為研發效能中的關鍵一環,重點需要解決的難題:
多模組共建質量:雲原生背景下,除去 Service Mesh 自身外,很多業務方的訴求也一併下沉至 MOSN,多個業務方的共建質量複雜度幾何倍的提升,如何保障共建方也持續高質量的輸出。
版本穩定性:MOSN 的每一次釋出,變更內容多、變更範圍廣,如何確保每個版本的穩定性,以及線上多個版本共存情況下,版本之間的相容性。
效能提升:在保障質量穩定性的前提下,如何提升版本測試的效能。
測試策略上,我們主要透過雲原生多模組質量資料建模和版本穩定性兩個維度解決上述問題。
雲原生多模組質量資料建模對於 MOSN 裡的每個模組的功能,除了基礎的單元測試和整合測試保障手段外,我們期望通過錄制線上的真實流量,經過資料清洗和建模後,獲取 MOSN 中不同模組的真實業務場景,作為 MOSN 中多模組測試場景的輸入;另外,MOSN 支援 MOCK 模式,對這些線上錄製的業務場景,線上下做自動化的回放驗證。多模組資料取樣這裡採集的資料共包含:流量資料、業務特徵資料和記憶體配置資料。MOSN 使用 Golang 語言編寫,無法像 JAVA 一樣,透過 JVM 插樁的方式實現流量的錄製和回放。對於 Golang 語言的錄製回放工具,開源也有優秀的工具,如 tcpcopy 和 goreplay 等,這些工具主要是從程式外部,對生產的正式流量進行錄製,而這對於 MOSN 並不適用。如能力建設部分所述,螞蟻內部通訊目標 100% 覆蓋鏈路加密,使用上述工具錄製的流量為加密後的流量資料,線下回放時,會因為證書問題導致回放失敗。因此,我們需要在 MOSN 內,建立一套流量錄製、業務特徵資料上報、記憶體配置資料上報的能力,我們稱之為 Test Mesh 的能力。流量錄製:錄製的是 TLS 解密後的二進位制資料。資料上報:提供統一的資料上報介面,供 MOSN 中不同的業務模組上報各自的流量特徵資料,透過和錄製的流量做關聯,達到流量清洗和線上流量建模的目的。資料取樣:對於持久化的資料(包括二進位制資料、流量特徵資料、記憶體映象和靜態配置),透過配置中心控制取樣開關和取樣頻率,在開關開啟的情況下,根據取樣頻率開啟取樣視窗,每個取樣視窗內同一個業務型別採集一筆流量。熔斷保護:為了不影響主流程,流量錄製和資料上報均設計為同步受理非同步處理的模式,且支援根據 CPU, MEM 的水位自動熔斷的能力,水位的閾值支援動態調整。建模清洗資料同步:線上錄製的資料持久化到磁碟檔案中,透過資料服務平臺將資料同步至離線 ODPS 表中。資料清洗:演算法模組包裝了資料清洗的原子能力(如:正則匹配、去重等),使用者透過不同模組上報的流量特徵資料,指定清洗規則,由演算法平臺定時做資料清洗,並將清洗後的資料同步至儲存平臺,構成不同業務模組的業務場景基線。模型場景回放有了業務場景和二進位制流量,我們構建了回放系統,並在 MOSN 內部支援了 MOCK 模式,支援線下回放驗證。MOCK 模式:線下回放時,MOSN 基於線上取樣的記憶體配置載入起來後,其與外圍的配置需要全部 MOCK 掉,確保記憶體不會被線下的 Registry、動態配置中心 等更新掉,保障回放結果的正確性。流量回放:回放系統提供回放任務觸發、任務編排、MOSN 以 MOCK 模式拉起、回放執行、結果展示等功能。版本穩定性持續整合我們在原有持續整合 pipeline 流水線的基礎上,提出了更高的要求:每個程式碼 PR 合併後即達到可釋出的狀態。提交的每個 PR 均會觸發這樣一條流水線的執行,每個 PR 獨立構建測試環境,並在整個 pipeline 流程中集成了研發、測試活動,從而確保 PR 合併後即可達到釋出狀態。這裡僅簡單介紹了 pipeline 的流程,pipeline 中的每個 job 還在持續建設中。功能預演原先在 MOSN 的研發流程中,MOSN 版本交付後,逐步灰度升級生產的業務應用,這中間缺失了一環 MOSN 自身生產升級的灰度驗證能力。為此,我們搭建了 MOSN 自身的預演應用,這些應用模擬線上業務應用的真實使用場景,且按照線上真實業務應用部署。MOSN 每個版本交付後,先對這些應用做升級驗證,確認無問題後再做業務應用的升級,從而實現 MOSN 自身的灰度釋出和版本升級的質量保障。對於這裡說的“真實使用場景”,其資料也是來源於 Test Mesh 錄製到的流量特徵資料,這些流量特徵資料經過清洗建模後,構建了業務場景基線,預演應用參照業務場景基線達到對線上“真實使用場景”的全面覆蓋。技術風險通常來說,減小升級頻率就會將穩定性的風險減小。畢竟程式碼變更是導致故障的一大來源。但是這對 MOSN 來說是不可接受的,例如一年我們只做一次釋出升級,前面提到了 MOSN 的程式碼變更量是較大的,可想而知一年堆積下來的程式碼變更量是多麼巨大。加上集中式的釋出方式,這就好像是把一顆“程式碼變更炸彈”在短時間內扔在了不小數量的容器上。對 MOSN 自身的這一大量程式碼變更來說,要保證完全沒有問題有不小挑戰,容易導致我們無法成功完成這次全叢集升級。所以這不僅對穩定性是挑戰,對 MOSN 的研發效能也是挑戰。如何去解決這個問題呢,這裡我們解讀兩個對這方面的探索。一個是採取更低侵入高頻的釋出方式,我們反而利用更加高頻的釋出模式來將較大量的變更風險分散開來;另一個是對健康度的度量,這能讓我們對每個 MOSN 節點的健康情況都準確掌握,做到更加自動化的釋出和相應的技術風險決策。
低侵入高頻釋出
MOSN 是以 sidecar 形態與業務容器共同部署在 Pod 內。由於 MOSN 依賴與應用的業務程序互動獲取應用的服務資訊,與服務配置中心互動並建立連線。這些資訊在 MOSN 的釋出過程中都需要重建。因此 MOSN 的釋出過程也要求應用容器同時做重啟,以保證應用的服務資訊能完整的在 MOSN 重新被初始化。但由於應用的啟動速度通常較慢,這個方式也嚴重的降低了 MOSN 的釋出效率。在最初考慮這個問題時,我們做了一些熱升級的嘗試,但熱升級引入了多個 MOSN 容器的互動,大幅增加了運維複雜性,因此我們又探索了新的低侵入釋出模式,我們稱之為溫升級——僅關閉一切流量,不通知應用業務程序,接近業務無感方式的釋出。溫升級整體的流程如下。
流量管理與服務元資料的繼承
我們在原有持續整合 pipeline 流水線的基礎上,提出了更高的要求:每個程式碼 PR 合併後即達到可釋出的狀態。MOSN 接管了業務的所有進出流量,並直接與流量相關的中介軟體互動,因此 MOSN 具備從源頭關停流量的能力。當釋出開始後,MOSN 先從各中介軟體登出自身,確保不再承接流量,之後 MOSN 容器退出銷燬,kubelet 使用新的 image 重建新的 MOSN 容器並啟動 MOSN。由於此時無法從應用業務程序獲取服務的元資料資訊(業務程序不知道 MOSN 進行了更新),新啟動的 MOSN 只能從之前的 MOSN 繼承這部分資訊:MOSN 會準實時的將所有的動態配置資訊(含服務元資料)dump 到一個共享的掛載卷,當新的 MOSN 容器啟動時,也會掛載同一個卷,並在 MOSN 程序啟動時載入這些配置,從而最大程度繼承原來 MOSN 的狀態。由於依賴於舊版本動態配置資訊的載入,MOSN 也做了相應的向下相容,確保升級過程中的元資料都能正確載入。
連線重建
完成狀態繼承後,MOSN 將自身所在 Pod 重新發布到服務配置中心等中介軟體,並開啟與遠端建立多個主動或被動的連線。對於後端的服務連線來說,MOSN 的升級過程就是一次普通的連線中斷,通常的服務框架都能自動處理連線中斷並重試建立新的連線。
高頻釋出
當低侵入的釋出實現之後,更高頻的釋出也變得可能。我們基於溫升級的能力,建設了從研發迭代直通到生產的 nighly build 交付的 CI/CD 流程,讓新研發的功能程式碼在風險可控的前提下能夠以最快的速度接觸到生產流量,縮短了反饋迴環,極大加快了新能力的落地。
健康度度量
當新版本的 MOSN 釋出後,如何快速知道 MOSN 執行得是否健康。除了傳統的監控方式外,我們讓 MOSN 將自己的健康度主動透出,並配套監控平臺,釋出平臺和巡檢系統做到第一時間發現問題,自動暫停釋出和回滾。
靜態健康度
MOSN 定義了標準的元件框架,MOSN 中的每個元件都需要實現健康狀態的相關介面將自己的健康狀態細緻地透出。例如:註冊中心客戶端會透出自己和服務端的連線狀態、心跳狀態,透出自己每一個訂閱和釋出結果等;配置中心客戶端會透出自己和服務端的連線狀態,透出自己對每一個配置的拉取結果等。這些元件的狀態和結果會作為釋出後流量是否開啟的前置檢查項,如果有元件不健康則不會開啟流量。巡檢平臺也會在檢視 MOSN 執行時的健康狀態,如果是不健康的狀態則會阻斷繼續釋出。
動態健康度
靜態健康度側重在以元件的視角去觀察各個元件自身執行情況的健康情況,動態健康度則是側重在以全站流量的視角去觀察各個 MOSN 節點的健康情況。以 RPC 為例,每一筆呼叫其實都是 應用_A -> MOSN_A -> MOSN_B -> 應用_B 的模型,這個模型中可以從 6 個地方觀察到這筆呼叫的健康狀態:應用_A 的 Egress 視角,MOSN_A 的 Ingress 和 Egress 視角,MOSN_B 的 Ingress 和 Egress 視角,應用_B 的 Ingress 視角。每一個視角都可以做呼叫情況的統計,基於這些呼叫統計,最終可以聚合計算出每個 MOSN 節點的健康情況。
如圖 應用_B 的 Egress 成功率和 MOSN_B 的 Ingress 成功率下跌,由此可判斷 MOSN_B 節點為不健康狀態,而 MOSN_B 的 Egress 成功率未變,所以還能進一步判斷是 MOSN_B 的 Ingress 模組可能出現了問題。如果此時 成功率_7 也出現下跌,而 成功率_8 和 應用_A -> MOSN_A -> MOSN_C 鏈路上的成功率都未發生改變,則可判斷是 MOSN_B 的 Egress 模組出現了問題。實際上,我們的動態健康度的度量維度會更復雜一些,例如還會區分出該筆流量是在 MOSN 節點內部發生異常還是在發起呼叫之後發生異常,還會有 MOSN 的版本等資訊,由此可以更加精確的定位到出現異常的 MOSN 節點,並追溯到它的釋出單及迭代等資訊,幫助最終的自動化決策
問題診斷在質量保障和技術風險的強大保障下,每一次 MOSN 的升級已經達到了一個高水準的穩定性。但是 MOSN 升級的覆蓋範圍之大,場景之複雜,有的問題甚至可能在幾個月之後才會暴露出來,如何再去發現這些漏網之魚的 corner case 呢。其中有一些問題較難定位,這些問題的特點是:發生隨機,過程時間短,難以抓到現場。一些偶發的 CPU 使用飈升、OOM 的問題對穩定性是大風險,我們需要有辦法把這些問題儘快定位出來,不能讓其一直潛伏到業務的關鍵時間點再爆發。
我們選取程式所佔用的 CPU、RSS 和 goroutine 數來作為程序的執行時指標。分為兩種情況:
如果這些指標在較短時間內發生較大波動,那麼認為可能發生了瞬時抖動如果這些指標到達非預期的閾值,那麼認為可能發生了資源洩露或瞬時抖動對上述三個指標,每經過 5s 採集一次,儲存在相應的環形陣列中。每次採集新一輪指標時,均與之前十個週期的數值平均(可以認為是一種形式的 moving average)進行 diff,並判斷當前的值是否已到達預期外的閾值。
若 CPU 規則被觸發,自動啟動 Go 的 CPU Profile,並將檔案儲存在日誌目錄中若 RSS 規則被觸發,自動將 Go 的 heap Profile 儲存在日誌目錄中若 Goroutine 規則被觸發,自動將 Go 的 goroutine Profile 儲存左日誌目錄中這些功能上線之後,我們只要根據監控系統推下來的報警 host 找到相應的目錄位置,並把 profile 下載到本地慢慢分析就可以了。
診斷功能上線之後幫我們定位出了多個之前難以發現的漏洞/程式碼問題,我們將該功能沉澱為一個開源 lib:holmes,目前已開源在 MOSN 組織下。
總結及未來在過去一年中,得益於 Service Mesh 的落地,我們的基礎設施得到前所未有的演進速度,並在效能,效能,穩定性和可用率等各方面幫助業務得到提升。
在向前探索的過程中,我們越發看清 Service Mesh 真正價值的體現不在於它建設了多少能力,而是體現在它自身將這些基礎設施能力大規模應用於業務的週期和穩定性。
由此我們發現 Service Mesh 目前最大的挑戰其實是它自身的研發效能,只有一個高效和穩定的 Service Mesh 才能持續不斷地將基礎設施能力賦予業務並得以演進。因此我們在質量保障,技術風險和問題診斷上也在不斷突破創新,最終使得 Service Mesh 的核心價值真正發揮了出來。
螞蟻的 Service Mesh 一直走在這個領域的最前沿,接下來我們又會向什麼方向探索呢。我們會持續將更多的能力下沉至 MOSN ,接管所有的進出流量,讓任意技術棧的應用只需要接入 MOSN 就能擁有完整的基礎設施能力;結合螞蟻豐富的實踐經驗與社群的力量制定出一套 API 作為應用和控制面等同 Service Mesh 互動的標準,一起推進 Service Mesh 的發展;不斷結合業務挖掘出有價值的場景,透過 Service Mesh 的優勢為業務帶來幫助;我們也會透過技術創新讓 MOSN 具備被整合的能力(https://github.com/mosn/mosn/issues/1559),使得 MOSN 和更多優秀高效能閘道器生態打通,進一步發揮 MOSN 沉澱的能力, 從而釋放 1 + 1 > 2 的技術紅利;另外會持續投入開源,MOSN 的核心能力一直是開源共建的方式,不斷吸取社群良好的輸入,並與螞蟻內部大規模場景下的實踐相碰撞,最終又將這些優秀的能力貢獻於社群。