做者簡介:
馬振軍,花名古今,在基礎架構領域耕耘多年,對 Service Mesh 有深度實踐經驗,目前在螞蟻集團中間件團隊負責 MOSN、Layotto 等項目的開發工做。
Layotto 官方 GitHub 地址:https://github.com/mosn/layottogit
點擊連接便可觀看現場視頻:https://www.bilibili.com/video/BV1hq4y1L7FY/github
Service Mesh 在微服務領域已經很是流行,愈來愈多的公司開始在內部落地,螞蟻從 Service Mesh 剛出現的時候開始,就一直在這個方向上大力投入,到目前爲止,內部的 Mesh 方案已經覆蓋數千個應用、數十萬容器而且通過了屢次大促考驗,Service Mesh 帶來的業務解耦,平滑升級等優點大大提升了中間件的迭代效率。docker
在大規模落地之後,咱們又遇到了新的問題,本文主要對 Service Mesh 在螞蟻內部落地狀況進行回顧總結,並分享對 Service Mesh 落地後遇到的新問題的解決方案。編程
在微服務架構下,基礎架構團隊通常會爲應用提供一個封裝了各類服務治理能力的 SDK,這種作法雖然保障了應用的正常運行,但缺點也很是明顯,每次基礎架構團隊迭代一個新功能都須要業務方參與升級才能使用,尤爲是 bugfix 版本,每每須要強推業務方升級,這裏面的痛苦程度每個基礎架構團隊成員都深有體會。瀏覽器
伴隨着升級的困難,隨之而來的就是應用使用的 SDK 版本差異很是大,生產環境同時跑着各類版本的 SDK,這種現象又會讓新功能的迭代必須考慮各類兼容,就好像帶着枷鎖前進通常,這樣隨着不斷迭代,會讓代碼維護很是困難,有些祖傳邏輯更是一不當心就會掉坑裏。緩存
同時這種「重」SDK 的開發模式,致使異構語言的治理能力很是薄弱,若是想爲各類編程語言都提供一個功能完整且能持續迭代的 SDK 其中的成本可想而知。安全
18 年的時候,Service Mesh 在國內持續火爆,這種架構理念旨在把服務治理能力跟業務解耦,讓二者經過進程級別的通訊方式進行交互。在這種架構模式下,服務治理能力從應用中剝離,運行在獨立的進程中,迭代升級跟業務進程無關,這就可讓各類服務治理能力快速迭代,而且因爲升級成本低,所以每一個版本均可以所有升級,解決了歷史包袱問題,同時 SDK 變「輕」直接下降了異構語言的治理門檻,不再用爲須要給各個語言開發相同服務治理能力的 SDK 頭疼了。網絡
螞蟻很快意識到了 Service Mesh 的價值,全力投入到這個方向,用 Go 語言開發了 MOSN 這樣能夠對標 envoy 的優秀數據面,全權負責服務路由,負載均衡,熔斷限流等能力的建設,大大加快了公司內部落地 Service Mesh 的進度。架構
如今 MOSN 在螞蟻內部已經覆蓋了數千個應用、數十萬容器,新建立的應用默認接入 MOSN,造成閉環。並且在你們最關心的資源佔用、性能損耗方面 MOSN 也交出了一份讓人滿意的答卷:app
因爲 Service Mesh 下降了異構語言的服務治理門檻,NodeJS、C++等異構技術棧也在持續接入到 MOSN 中。
在看到 RPC 能力 Mesh 化帶來的巨大收益以後,螞蟻內部還把 MQ,Cache,Config 等中間件能力都進行了 Mesh 化改造,下沉到 MOSN,提升了中間件產品總體的迭代效率。
一個現代分佈式應用,每每會同時依賴 RPC、Cache、MQ、Config 等各類分佈式能力來完成業務邏輯的處理。
當初看到 RPC 下沉的紅利之後,其餘各類能力也都快速下沉。初期,你們都會以本身最熟悉的方式來開發,這就致使沒有統一的規劃管理,如上圖所示,應用依賴了各類基礎設施的 SDK,而每種 SDK 又以本身特有的方式跟 MOSN 進行交互,使用的每每都是由原生基礎設施提供的私有協議,這直接致使了複雜的中間件能力雖然下沉,但應用本質上仍是被綁定到了基礎設施,好比想把緩存從 Redis 遷移到 Memcache 的話,仍舊須要業務方升級 SDK,這種問題在應用上雲的大趨勢下表現的更爲突出,試想一下,若是一個應用要部署在雲上,因爲該應用依賴了各類基礎設施,勢必要先把整個基礎設施搬到雲上才能讓應用順利部署,這其中的成本可想而知。
所以如何讓應用跟基礎設施解綁,使其具有可移植能力,可以無感知跨平臺部署是咱們面臨的第一個問題。
事實證實 Service Mesh 確實下降了異構語言的接入門檻,但在愈來愈多的基礎能力下沉到 MOSN 之後,咱們逐漸意識到爲了讓應用跟 MOSN 交互,各類 SDK 裏都須要對通訊協議,序列化協議進行開發,若是再加上須要對各類異構語言都提供相同的功能,那維護難度就會成倍上漲,
Service Mesh 讓重 SDK 成爲了歷史,但對於如今各類編程語言百花齊放、各類應用又強依賴基礎設施的場景來講,咱們發現現有的 SDK 還不夠薄,異構語言接入的門檻還不夠低,如何進一步下降異構語言的接入門檻是咱們面臨的第二個問題。
20 年初的時候,Bilgin lbryam 發表了一篇名爲
Multi-Runtime Microservices Architecture
的文章,裏面對微服務架構下一階段的形態進行了討論。
如上圖所示,做者把分佈式服務的需求進行了抽象,總共分爲了四大類:
明確了需求之後,借鑑了 Service Mesh 的思路,做者對分佈式服務的架構演進進行了以下總結:
第一階段就是把各類基礎設施能力從應用中剝離解耦,統統變成獨立 sidecar 模型伴隨着應用一塊兒運行。
第二階段是把各類 sidecar 提供的能力統一抽象成若干個 Runtime,這樣應用從面向基礎組件開發就演變成了面向各類分佈式能力開發,完全屏蔽掉了底層實現細節,並且因爲是面向能力,除了調用提供各類能力的 API 以外,應用不再須要依賴各類各樣基礎設施提供的 SDK 了。
做者的思路跟咱們但願解決的問題一致,咱們決定使用 Runtime 的理念來解決 Service Mesh 發展到如今所遇到的新問題。
爲了讓你們對 Runtime 有一個更加清晰的認識,上圖針對 Service Mesh 跟 Runtime 兩種理念的定位、交互方式、通訊協議以及能力豐富度進行了總結,能夠看到相比 Service Mesh 而言,Runtime 提供了語義明確、能力豐富的 API,可讓應用跟它的交互變得更加簡單直接。
dapr 是社區中一款知名的 Runtime 實現產品,活躍度也比較高,所以咱們首先調研了 dapr 的狀況,發現 dapr 具備以下優點:
當考慮如何在公司內部落地 dapr 時,咱們提出了兩種方案,如上圖所示:
a. dapr 雖然提供了不少分佈式能力,但目前並不具有 Service Mesh 包含的豐富的服務治理能力。
b. MOSN 在公司內部已經大規模落地,而且通過了屢次大促考驗,直接用 dapr 來替換 MOSN 穩定性有待驗證。
a. 引入一個新的 sidecar,咱們就須要考慮它配套的升級、監控、注入等等事情,運維成本飆升。
b. 多維護一個容器意味着多了一層掛掉的風險,這會下降如今的系統可用性。
一樣的,若是你目前正在使用 envoy 做爲數據面,也會面臨上述問題。
所以咱們但願把 Runtime 跟 Service Mesh 二者結合起來,經過一個完整的 sidecar 進行部署,在保證穩定性、運維成本不變的前提下,最大程度複用現有的各類 Mesh 能力。此外咱們還但願這部分 Runtime 能力除了跟 MOSN 結合起來以外,將來也能夠跟 envoy 結合起來,解決更多場景中的問題,Layotto 就是在這樣的背景下誕生。
如上圖所示,Layotto 是構建在 MOSN 之上,在下層對接了各類基礎設施,向上層應用提供了統一的,具備各類各樣分佈式能力的標準 API。對於接入 Layotto 的應用來講,開發者再也不須要關心底層各類組件的實現差別,只須要關注應用須要什麼樣的能力,而後調用對應能力的 API 便可,這樣能夠完全跟底層基礎設施解綁。
對應用來講,交互分爲兩塊,一個是做爲 gRPC Client 調用 Layotto 的標準 API,一個是做爲 gRPC Server 來實現 Layotto 的回調,得利於gRPC 優秀的跨語言支持能力,應用再也不須要關心通訊、序列化等細節問題,進一步下降了異構技術棧的使用門檻。
除了面向應用,Layotto 也向運維平臺提供了統一的接口,這些接口能夠把應用跟 sidecar 的運行狀態反饋給運維平臺,方便 SRE 同窗及時瞭解應用的運行狀態並針對不一樣狀態作出不一樣的舉措,該功能考慮到跟 k8s 等已有的平臺集成,所以咱們提供了 HTTP 協議的訪問方式。
除了 Layotto 自己設計之外,項目還涉及兩塊標準化建設,首先想要制定一套語義明確,適用場景普遍的 API 並非一件容易的事情,爲此咱們跟阿里、 dapr 社區進行了合做,但願可以推動 Runtime API 標準化的建設,其次對於 dapr 社區已經實現的各類能力的 Components 來講,咱們的原則是優先複用、其次開發,儘可能不把精力浪費在已有的組件上面,重複造輪子。
最後 Layotto 目前雖然是構建在 MOSN 之上,將來咱們但願 Layotto 能夠跑在 envoy 上,這樣只要應用接入了 Service Mesh,不管數據面使用的是 MOSN 仍是 envoy,均可以在上面增長 Runtime能力。
如上圖所示,一旦完成 Runtime API 的標準化建設,接入 Layotto 的應用自然具有了可移植性,應用不須要任何改造就能夠在私有云以及各類公有云上部署,而且因爲使用的是標準 API,應用也能夠無需任何改造就在 Layotto 跟 dapr 之間自由切換。
從上面的架構圖能夠看出,Layotto 項目自己是但願屏蔽基礎設施的實現細節,向上層應用統一提供各類分佈式能力,這種作法就好像是在應用跟基礎設施之間加了一層抽象,所以咱們借鑑了 OSI 對網絡定義七層模型的思路,但願 Layotto 能夠做爲第八層對應用提供服務,otto 是意大利語中8的意思,Layer otto 就是第八層的意思,簡化了一下變成了 Layotto,同時項目代號 L8,也是第八層的意思,這個代號也是設計咱們項目 LOGO 時靈感的來源。
介紹完項目的總體狀況,下面對其中四個主要功能的實現細節進行說明。
首先是分佈式系統中常用的配置功能,應用通常使用配置中心來作開關或者動態調整應用的運行狀態。Layotto 中配置模塊的實現包括兩部分,一個是對如何定義配置這種能力的 API 的思考,一個是具體的實現,下面逐個來看。
想要定義一個能知足大部分實際生產訴求的配置 API 並非一件容易的事,dapr 目前也缺失這個能力,所以咱們跟阿里以及 dapr 社區一塊兒合做,爲如何定義一版合理的配置 API 進行了激烈討論。
目前討論結果尚未最終肯定,所以 Layotto 是基於咱們提給社區的初版草案進行實現,下面對咱們的草案進行簡要說明。
咱們先定義了通常配置所需的基本元素:
此外咱們追加了兩種高級特性,用來適配更加複雜的配置使用場景:
對於上述定義的配置 API 的具體實現,目前支持查詢、訂閱、刪除、建立、修改五種操做,其中訂閱配置變動後的推送使用的是 gRPC 的 stream 特性,而底層實現這些配置能力的組件,咱們選擇了國內流行的 apollo,後面也會根據需求增長其餘實現。
對於 Pub/Sub 能力的支持,咱們調研了 dapr 如今的實現,發現基本上已經能夠知足咱們的需求,所以咱們直接複用了 dapr 的 API 以及 components,只是在 Layotto 裏面作了適配,這爲咱們節省了大量的重複勞動,咱們但願跟 dapr 社區保持一種合做共建的思路,而不是重複造輪子。
其中 Pub 功能是 App 調用 Layotto 提供的 PublishEvent 接口,而 Sub 功能則是應用經過 gRPC Server 的形式實現了 ListTopicSubscriptions 跟 OnTopicEvent 兩個接口,一個用來告訴 Layotto 應用須要訂閱哪些 topic,一個用於接收 topic 變化時 Layotto 的回調事件。
dapr 對於 Pub/Sub 的定義基本知足咱們的需求,但在某些場景下仍有不足,dapr 採用了 CloudEvent 標準,所以 Pub 接口沒有返回值,這沒法知足咱們生產場景中要求 Pub 消息之後服務端返回對應的 messageID 的需求,這一點咱們已經把需求提交給了 dapr 社區,還在等待反饋,考慮到社區異步協做的機制,咱們可能會先社區一步增長返回結果,而後再跟社區探討一種更好的兼容方案。
RPC 的能力你們不會陌生,這多是微服務架構下最最基礎的需求,對於 RPC 接口的定義,咱們一樣參考了 dapr 社區的定義,發現徹底能夠知足咱們的需求,所以接口定義就直接複用 dapr 的,但目前 dapr 提供的 RPC 實現方案還比較薄弱,而 MOSN 通過多年迭代,能力已經很是成熟完善,所以咱們大膽把 Runtime 跟 Service Mesh 兩種思路結合在一塊兒,把 MOSN 自己做爲咱們實現 RPC 能力的一個 Component,這樣 Layotto 在收到 RPC 請求之後交給 MOSN 進行實際數據傳輸,這種方案能夠經過 istio 動態改變路由規則,降級限流等等設置,至關於直接複用了 Service Mesh 的各類能力,這也說明 Runtime 不是要推翻 Service Mesh,而是要在此基礎上繼續向前邁一步。
具體實現細節上,爲了更好的跟 MOSN 融合,咱們在 RPC 的實現上面加了一層 Channel,默認支持dubbo,bolt,http 三種常見的 RPC 協議,若是仍然不能知足用戶場景,咱們還追加了 Before/After 兩種 Filter,可讓用戶作自定義擴展,實現協議轉換等需求。
在實際生產環境中,除了應用所須要的各類分佈式能力之外,PaaS 等運維平臺每每須要瞭解應用的運行狀態,基於這種需求,咱們抽象了一套 Actuator 接口,目前 dapr 尚未提供這方面的能力,所以咱們根據內部的需求場景進行了設計,旨在把應用在啓動期、運行期等階段各類各樣的信息暴露出去,方便 PaaS 瞭解應用的運行狀況。
Layotto 把暴露信息分爲兩大類:
a. Readiness:表示應用啓動完成,能夠開始處理請求。
b. Liveness:表示應用存活狀態,若是不存活則須要切流等。
Health 對外暴露的健康狀態分爲如下三種:
到這裏關於 Layotto 目前在 Runtime 方向上的探索基本講完了,咱們經過定義明確語義的 API,使用 gRPC 這種標準的交互協議解決了目前面臨的基礎設施強綁定、異構語言接入成本高兩大問題。隨着將來 API 標準化的建設,一方面可讓接入 Layotto 的應用無感知的在各類私有云、公有云上面部署,另外一方面也能讓應用在 Layotto,dapr 之間自由切換,提升研發效率。
目前 Serverless 領域也是百花齊放,沒有一種統一的解決方案,所以 Layotto 除了在上述 Runtime 方向上的投入之外,還在 Serverless 方向上也進行了一些嘗試,下面就嘗試方案進行介紹。
WebAssembly,簡稱 WASM,是一個二進制指令集,最初是跑在瀏覽器上來解決 JavaScript 的性能問題,但因爲它良好的安全性,隔離性以及語言無關性等優秀特性,很快人們便開始讓它跑在瀏覽器以外的地方,隨着 WASI 定義的出現,只須要一個 WASM 運行時,就可讓 WASM 文件隨處執行。
既然 WebAssembly 能夠在瀏覽器之外的地方運行,那麼咱們是否能把它用在 Serverless 領域?目前已經有人在這方面作了一些嘗試,不過若是這種方案真的想落地的話,首先要考慮的就是如何解決運行中的 WebAssembly 對各類基礎設施的依賴問題。
目前 MOSN 經過集成 WASM Runtime 的方式讓 WASM 跑在 MOSN 上面,以此來知足對 MOSN 作自定義擴展的需求。同時,Layotto 也是構建在 MOSN 之上,所以咱們考慮把兩者結合在一塊兒,實現方案以下圖所示:
開發者可使用 Go/C++/Rust 等各類各樣本身喜歡的語言來開發應用代碼,而後把它們編譯成 WASM 文件跑在 MOSN 上面,當 WASM 形態的應用在處理請求的過程當中須要依賴各類分佈式能力時就能夠經過本地函數調用的方式調用 Layotto 提供的標準 API,這樣直接解決了 WASM 形態應用的依賴問題。
目前 Layotto 提供了 Go 跟 Rust 版 WASM 的實現,雖然只支持 demo 級功能,但已經足夠讓咱們看到這種方案的潛在價值。
此外,WASM 社區目前還處於初期階段,有不少地方須要完善,咱們也給社區提交了一些 PR共同建設,爲 WASM 技術的落地添磚加瓦。
雖然如今 Layotto 中對 WASM 的使用還處於試驗階段,但咱們但願它最終能夠成爲 Serverless 的一種實現形態,如上圖所示,應用經過各類編程語言開發,而後統一編譯成 WASM 文件,最後跑在 Layotto+MOSN 上面,而對於應用的運維管理統一由 k8s、docker、prometheus 等產品負責。
最後來看下 Layotto 在社區的作的一些事情。
上圖列出了 Layotto 跟 dapr 現有的能力對比,在 Layotto 的開發過程當中,咱們借鑑 dapr 的思路,始終以優先複用、其次開發爲原則,旨在達成共建的目標,而對於正在建設或者將來要建設的能力來講,咱們計劃優先在 Layotto 上落地,而後再提給社區,合併到標準 API,鑑於社區異步協做的機制,溝通成本較高,所以短時間內可能 Layotto 的 API 會先於社區,但長期來看必定會統一。
關於如何定義一套標準的 API 以及如何讓 Layotto 能夠跑在 envoy 上等等事項,咱們已經在各個社區進行了深刻討論,而且之後也還會繼續推動。
Layotto 在目前主要支持 RPC、Config、Pub/Sub、Actuator 四大功能,預計在九月會把精力投入到分佈式鎖、State、可觀測性上面,十二月份會支持 Layotto 插件化,也就是讓它能夠跑在 envoy 上,同時但願對 WebAssembly 的探索會有進一步的產出。
前面詳細介紹了 Layotto 項目,最重要的仍是該項目今天做爲 MOSN 的子項目正式開源,咱們提供了詳細的文檔以及 demo 示例方便你們快速上手體驗。
對於 API 標準化的建設是一件須要長期推進的事情,同時標準化意味着不是知足一兩種場景,而是儘量的適配大多數使用場景,爲此咱們但願更多的人能夠參與到 Layotto 項目中,描述你的使用場景,討論 API 的定義方案,一塊兒提交給社區,最終達成 Write once, Run anywhere 的終極目標!
更多文章請掃碼關注「金融級分佈式架構」公衆號