在基礎架構領域耕耘多年
目前在螞蟻集團中介軟體團隊
負責 MOSN、Layotto 等專案的開發工作
校對|卓與、齊天
本文 9053 字 閱讀 18 分鐘
▼
|前言|
在過去幾年裡,螞蟻集團的基礎設施進行了廣受矚目的大規模 Mesh 化改造,成為業內服務網格化的一個標杆。在收穫了基礎架構團隊掌握資料平面帶來業務敏捷性的同時,也享受到了將基礎設施 SDK 從應用中剝離出來後,帶來的應用和基礎設施雙方更高的易維護性。
然而服務網格並不是銀彈,大規模落地後也面臨著新的問題。
適逢其時,微軟牽頭的 Dapr 橫空出世,把分散式應用執行時的概念帶入了大眾視野,我們也嘗試使用這種思路來解決 Mesh 化後遺留的問題。
本文透過回顧螞蟻集團一路從微服務架構到 Service Mesh 再到分散式應用執行時的整個演進歷程,結合生產落地過程中遇到的各種問題及思考,嘗試探討一下未來五年,雲原生執行時可能的發展方向。
PART. 1
從 Service Mesh 到應用執行時
2018 年,螞蟻集團在 Service Mesh 剛剛開始流行的時候,就在這個方向上大力投入,如今已三年有餘。Sevice Mesh 早已在公司內大規模落地,支援了生產環境數十萬容器的日常執行。
19 年下半年,Dapr 專案正式開源並持續火爆,應用執行時的概念引起了人們的關注,螞蟻集團也踏上了從 Service Mesh 到應用執行時的演進道路。
A、螞蟻 Service Mesh 實踐的收穫與遺留問題
在傳統的微服務架構下,基礎架構團隊一般會為應用提供一個封裝了各種服務治理能力的 SDK,這種做法雖然保障了應用的正常執行。但缺點也非常明顯,每次基礎架構團隊迭代一個新功能都需要業務方參與升級才能使用,而遇到 bugfix 版本,往往需要強推業務方升級,這裡面的痛苦程度基礎架構團隊的每一個成員都深有體會。
伴隨著升級的困難,隨之而來的就是應用使用的 SDK 版本差別非常大。生產環境同時跑著各種版本的 SDK ,這種現象又會讓新功能的迭代必須考慮各種相容,時間一長就會讓程式碼維護非常困難,有些祖傳邏輯更是不敢改也不敢刪。
同時這種“重” SDK 的開發模式,導致異構語言的治理能力始終很難對標主語言,各種保障高可用的能力都無法作用於異構語言應用。
後來有人提出了 Service Mesh 的理念,這種理念旨在把服務治理能力跟業務解耦,讓兩者透過程序級別的通訊方式進行互動。在這種架構下,各種服務治理能力從應用中剝離,執行在獨立的程序中,讓業務團隊跟基礎架構團隊可以各自進行迭代更新,大幅度提升效率。
同時 SDK 因為功能減少而變“輕”則降低了異構語言的接入門檻,讓這些語言開發的應用有機會對標主語言的治理能力。
在看到 Service Mesh 理念的巨大潛力之後,螞蟻集團很快就在這個方向上進行了大力投入,如上圖所示,首先是使用 Go 語言自研了可以對標 envoy 的資料面。然後把 RPC 中各種治理能力下沉了到 MOSN 中,這樣 RPC 的 SDK 變“輕”,而其他基礎設施的 SDK 則依舊維持現狀。
在 RPC 能力完成 Mesh 化改造之後,我們進行了快速推廣,如今達到了數千個應用,數十萬個容器的生產規模。與此同時,全站升級頻率最快可以達到 1~2 次/月,這跟傳統微服務架構下 1~2 次/年的升級頻率相比達到了一個質的提升。
B、螞蟻初步泛 Mesh 化的探索
在 RPC 能力完成 Mesh 化改造並且驗證了這種架構的可行性以及體驗到 Mesh 化帶來的迭代效率大幅度提升的收益以後,我們正式走上了整個基礎設施泛 Mesh 化改造的道路。
如上圖所示,在泛 Mesh 化改造的大趨勢下,除了 RPC 以外,快取、訊息、配置等一些常用的基礎設施能力迅速從應用中剝離,下沉到 MOSN 中,這套架構極大的提高了整個基礎架構團隊的迭代效率。
正如軟體工程沒有銀彈說的那樣,隨著泛 Mesh 化落地規模逐漸擴大,我們逐漸意識到它遺留的問題,如上圖所示。
在這種架構下,雖然應用跟基礎設施之間加了一層網路代理,但對於基礎設施協議部分的處理依然保留在 SDK 中,這就導致應用本質上還是要面向某個基礎設施做開發,比如想使用 Redis 作為快取實現,那麼應用需要引入 Redis 的 SDK,未來如果想切換到 Memcache 等其他快取實現,則必須對應用進行改造。
除了替換 SDK 之外,甚至還涉及到呼叫 API 的調整,因此這種架構完全無法滿足當前公司面臨的同一個應用在多個平臺部署的需求。
與上述問題類似,泛 Mesh 化改造後,“輕” SDK 的低開發成本讓各種異構語言都有機會接入到整個基礎設施體系中來,享受多年基礎設施建設的紅利。
但由於 SDK 裡仍然保留了通訊、序列化等協議的處理邏輯,因此隨著接入的語言越來越多樣化,這裡依然存在不能忽視的開發成本。換句話說,泛 Mesh 化改造帶來的“輕” SDK 跟傳統微服務架構相比雖然降低了異構語言接入基礎設施的門檻,但是隨著接入語言越來越多樣,依賴的中介軟體能力越來越豐富,我們還需要嘗試進一步降低這種門檻。
如果對上述兩個問題做一層抽象,本質上都可以歸結為應用跟基礎設施之間的邊界不夠清晰,或者說應用中始終嵌入了某種基礎設施實現中特有的處理邏輯,導致兩者一直耦合在一起。
因此如何定義應用跟基礎設施之間的邊界,讓兩者徹底解綁是我們當下必須要思考解決的問題。
PART. 2
重新定義基礎設施邊界
A、如何看待 Dapr
Dapr 專案由微軟牽頭,於 19 年下半年正式開源,作為分散式應用執行時的一種實現方案登上舞臺,引起了廣泛關注,它向我們展示瞭如何定義應用跟基礎設施之間的邊界。
上圖是 Dapr 官方提供的架構圖,跟 Service Mesh 架構類似,Dapr 採用 Sidecar 的模型部署在應用跟基礎設施之間。但與之不同的是,Dapr 基於 HTTP/gRPC 這類標準協議嚮應用提供了一套語義明確、面向能力的 API ,讓應用可以不再關心基礎設施的實現細節,只需專注業務本身依賴哪些能力即可。
目前 Dapr 已經提供了較為豐富的 API,包括狀態管理、釋出訂閱、服務呼叫等常見基礎設施能力,基本能夠覆蓋業務日常開發的需求,並且每種能力都對應了多種具體的基礎設施實現,開發者可以按需自由切換且這種切換對應用完全透明。
除了能力之外,Dapr 官方也給出了 Dapr 跟 Service Mesh 之間的異同點,如上圖所示。
兩者雖然有一些交集,但本質不同,Service Mesh 強調的是透明的網路代理,它並不關心資料本身,而 Dapr 強調的是提供能力,是真正站在應用的角度來思考如何降低應用的開發成本。
Dapr 本身的優勢非常明顯,不過 Service Mesh 所提供的豐富的網路治理能力,也是保障應用生產穩定性的關鍵點。
與此同時 Dapr 跟基礎設施的互動也無法離不開網路,因此有沒有一種解決方案可以讓應用執行時跟 Service Mesh 雙劍合璧,降低應用開發成本的同時保留豐富的網路治理能力?
B、Layotto:
Servcie Mesh & 應用執行時雙劍合璧
Layotto 作為 Dapr 之外的一個應用執行時實現方案,目的就是希望把應用執行時跟 Service Mesh 兩者的優勢結合起來。因此 Layotto 是建立在 MOSN 之上,分工上希望讓 MOSN 來處理網路部分,而自己負責嚮應用提供各種中介軟體能力。
此外基於螞蟻集團內部的生產運維經驗,Layotto 還抽象了一套面向 PaaS 的 API,主要目的是希望把應用跟 Layotto 本身的執行狀態透出給 PaaS 平臺,讓 SRE 可以快速瞭解應用的執行狀態,降低日常運維的成本。
C、API 標準化:跨平臺部署利器
對於跟應用互動所使用的這套 API,Layotto 希望在 Dapr 的基礎上結合實際生產使用的場景進行擴充套件改造,同時也會跟阿里、Dapr 一起合作,爭取定義一套能力通用,覆蓋場景廣的標準 API 。
而一旦完成標準化建設,對於所有基於這套 API 開發的應用來說,它們不僅不需要為適配各種平臺之間的差異而煩惱,甚至也可以在 Layotto 跟 Dapr 之間無縫切換,徹底打消商業化使用者對產品繫結帶來的顧慮。
D、為什麼不在一個 Sidecar 裡解決所有?
Dapr 專案給我們最大的啟示在於,它定義了應用跟基礎設施之間的邊界,但應用需要的不僅僅是這些。Dapr 為我們提供了很好的思路,是一個好的開端,但還不能夠完全覆蓋我們想要的東西,我們希望可以完全定義應用跟依賴資源之間的邊界,可以覆蓋系統資源,基礎設施,資源限制等多個環節。成為應用的“真”執行時,應用除了業務邏輯之外無需關注任何其他資源。
以當前 Sidecar 思路的落地情況來看,無論是 Dapr,MOSN 還是 Envoy,解決的都是應用到基礎設施的問題。而對於系統呼叫,資源限制等方面仍舊由應用自己完成,這部分操作不需要經過任何中間環節,而沒有被接管就意味著很難統一治理,類似網路流量如果沒有統一的出入口,治理起來自然會困難重重。同時如果不能對應用可訪問的系資源進行精細化控制,那始終會存在安全隱患。
E、統一的邊界:Layotto 的野望
雖然 Layotto 的初始階段跟 Dapr 類似,是作為應用執行時的形態存在。
但一個更大的目標是嘗試定義應用跟所有依賴資源之間的邊界,我們統稱為安全、服務、資源三大邊界。未來希望可以演進到應用的“真”執行時這一形態。
定義清楚邊界帶來的直接收益是可以徹底解放業務的開發者,讓他們可以專注於業務本身。
現在一名業務開發人員想要上手寫程式碼,不僅要熟悉本身的業務邏輯,還需要熟悉快取、訊息、配置等各種各樣基礎設施的實現細節,成本非常高,而一旦把邊界定義清楚以後,會降低業務開發人員的上手門檻,進而降低整體的開發成本。
目標雖然已經明確,但是 Layotto 以什麼樣的存在形式來達到這個目標是我們首先面臨的問題。
在 Service Mesh 的推動下,大家已經逐漸接受應用跟基礎設施之間藉助 Sidecar 互動帶來的收益,但要想繼續透過 Sidecar 的形式來對應用跟作業系統之間的互動以及應用可使用的最大資源進行限制恐怕就沒那麼簡單了。因此我們迫切需要一種全新的部署模型來達成目標,經過反覆討論以後,函式計算這種研發模式進入我們的視野。
PART. 3
未來五年:函式是不是下一站?
A、Layotto 與螞蟻函式化的明天
相信大家對函式計算並不陌生,但函式本身除了作為獨立程序執行以外還有沒有其他更好的方式可以嘗試?為了回答這個問題,我們首先回顧一下虛擬化技術的發展。
如上圖所示,以前的虛擬機器時代,人們在一套硬體上面獨立執行多個作業系統。這種模式可以抽象為對硬體進行了虛擬化,而現在大火的容器技術則是在一個作業系統上透過 namespace、cgroup 等技術手段來執行多個容器,這種模式相比於虛擬機器來說,可以看作是對作業系統進行了虛擬化,但因為容器技術採用的是共享核心的方式,因此在安全方面一直被人詬病,這也是 Kata 之類的安全容器誕生的一個背景。
此外,在社群中還有一種 Unikernel 的技術在發展,它的一個主體思路是應用可以獨佔核心,但這部分核心不是一個完整的作業系統,而是僅僅包含了應用執行所需要的部分,在應用開發完成後會跟核心一起編譯成映象直接在硬體上面執行。
之所以會有多種虛擬化方案,其實是因為對不同的資源進行虛擬化帶來的收益不相同,比如容器跟虛擬機器相比就有更快的啟動速度以及更高的資源利用率。
對比上述三種技術,我們可以得出技術人員一直嘗試在隔離性、安全性、輕量化三個方向上尋找一個平衡點,希望可以最大程度的整合它們各自的優勢。因此我們期望的函式模型也能夠整合這三者各自的優勢。
B、跳過,再跳過,函式能不能成為雲原生時代的一等公民?
向上來說:
1.函式本身可以使用任何語言進行開發,這樣才能更好的滿足越來越多樣的業務訴求。
2.多個函式執行在一個執行時基座上面,它們都跑在一個程序裡,在這種模型下函式之間的隔離性勢必也是重點考慮的因素。
向下來說:
1.函式執行過程中不能直接訪問下層資源,必須藉助基座發起請求,包括系統呼叫、基礎設施等。
2.執行時基座可以對函式執行過程中可使用的資源進行精細化的控制,保證按需使用。
為了實現上述目標,我們需要找到一種技術來作為函式的載體,讓一個程序中的不同函式之間具有良好的隔離性、移植性、安全性。正因為如此,當下越來越火的 WebAssembly 技術成為了我們重點考慮的物件。
C、風口浪尖上的 WebAssembly(wasm)
雖然最初定位是希望讓服務端程式語言執行在瀏覽器中來解決 JavaScript 的效能問題,但由於這項技術具備了各種優秀的特性,因此人們迫切的希望它可以在瀏覽器之外的環境中執行,類似於 Node.js 可以讓 JavaScript 跑在伺服器上一樣,WebAssembly 社群也提供了多種執行時來支援在伺服器上執行 *.wasm 檔案。
WebAssembly 作為當前一項炙手可熱的技術寵兒,與生俱來就有其他技術不可能替代的優勢:
1.語言無關,跨平臺
- WebAssembly 作為一套指令集,理論上可以支援從任意語言編譯過來,同時在設計之初就把在不同 CPU 架構上執行作為基本目標。
2.安全性,體積小
- wasm 模組在執行時可以執行的系統呼叫、可訪問的磁碟檔案都是需要宿主明確授權才行,這帶來了良好的安全性。
- 編譯成的 wasm 檔案本身體積很少,這就帶來了更快的傳輸、載入速度。
3.沙箱執行環境
- 多個 wasm 模組都執行在自己的沙箱環境中,它們之間具有良好的隔離性,互不影響。
這項技術雖然有巨大的發展潛力,不過就目前而言,對於實際在後端生產環境落地來說還有很多的不足:
1.多語言的支援程度
- WebAssembly 目標是可以支援從各種語言編譯得到,但就目前來說各種主流語言對它的支援程度大不相同,支援的比較好是 c/c++/rust 等編譯語言,可惜這些語言對於開發普通的業務邏輯來說,上手成本是個很大問題。而對於業務場景中主流的 Java,Go 來說,它們對 WebAssembly 的支援程度非常有限,並不足以支撐這項技術在生產環境中落地。
2.生態建設
- 在實際生產環境中,定位線上問題是我們日常都會面對的,Java 有自帶的各種命令及 arthas 等三方工具,Go 的 pprof 也是很優秀的效能分析利器,但執行中的 wasm 如何進行排查,比如優雅的列印錯誤堆疊或者 debug 目前還處於早期階段。
3.各種執行時能力參差不齊
- 正如前面“統一邊界”中提到的,執行中的函式需要讓它進行安全的系統呼叫,並且限制可以使用的最大資源,目前幾款主流的 wasm 執行時對這些能力的支援層次不齊,有的只支援部分功能,這些都是在真實的生產場景落地之前必須要解決的問題。
雖然 WebAssembly 在大火的同時也有很多不足,但隨著社群的發展,相信上述提到問題都會逐步解決,重要的是我們相信這項技術的前景,我們也會參與到整個 WebAssembly 社群的推廣建設中。
D、Layotto 與螞蟻函式化應用的明天
如果未來以函式作為跟當前微服務架構具有同等地位的另一種基礎研發模型,我們就需要考慮整個函式模式的生態建設問題,而這個建設其實就是圍繞極致的迭代效率來打造,包括但不限於下面幾點:
1.基礎框架
- 得益於 WebAssembly 這項技術的支援,函式本身是可以使用多種主流語言進行開發, 但為了更好的管理每個函式,仍舊需要讓業務同學在開發過程中遵循一定的模板,如函式載入時會執行一個 start 方法,可以做一些初始化工作,解除安裝時會執行一個 destroy 方法,這可以做一些清理工作。
2.開發除錯
- 現在多數的開發除錯工作大家仍舊習慣在本地 IDE 進行,但本地開發其實有很多不便之處,比如需要進行各種配置,或者當我們需要協作時,往往需要以投屏的方式讓別人參與進來,現在 Cloud IDE 日漸成熟,相信隨著發展可以更好的解決上述問題。
3.打包部署
- 現在主流的應用在部署時會打包成 war,jar 或者直接編譯成目標作業系統的可執行檔案,在函式體系中,應用則是編譯成 *.wasm 檔案,預設就可以在各種作業系統上面執行。
4.生命週期管理 + 資源排程
- 現在 K8s 已經成為了容器管理排程的事實標準,函式排程如何融入到 K8s 生態也是我們探索的一大重點。
在執行模型上,如上圖所示,Layotto 除了支援 Sidecar 模式之外,藉助於 WebAssembly 技術可以讓 wasm 形態的函式直接跑在 Layotto 上,這種模式下,函式跟 Layotto 之間的互動是採用本地呼叫的方式來完成,我們把這一層 API 稱為 Runtime ABI,它是由 Runtime API 演變而來,比如函式想要從快取中查詢某個 key ,只需呼叫一個 proxy_get_state 的本地方法即可完成。
關於排程,目前 K8s 已經成為了事實標準,因此需要解決的其實是 wasm 形態的函式如何融入 K8s 的生態。這裡我們需要重點考慮兩個問題:
1.wasm 跟映象之間的關係是什麼?
K8s 是以映象為基礎來建立 Pod,而函式編譯的產物是 wasm 檔案,我們需要有把兩者融合在一起的妥善方案。
2.如何讓 K8s 管理部署 wasm ?
K8s 的排程單位是 Pod,如何優雅的把排程 Pod 橋接到排程 wasm 上面並且讓多個 wasm 函式執行在一個程序裡也是一個棘手的問題。
在調研過社群的一些探索方案之後,我們給出了一套自己的實現方案。整體來說 K8s 支援開發者基於 Containerd 的 OCI 規範對容器執行時進行擴充套件,目前基於這套規範已經有了 Kata,gVisor 等知名的安全容器實現方案,因此在 Layotto 中我們也同樣採用了基於 Containerd 的擴充套件實現容器執行時方案。整體方案上有兩個關鍵點:
1.映象構建階段
對於編譯好的 *.wasm 檔案,我們把它打在一個映象裡,然後 push 到映象倉庫用於後續排程使用。
2.排程部署階段
我們自己實現了一個叫做 containerd-shim-layotto-v2 的外掛,在 K8s 收到排程 Pod 的請求以後它會把真正的處理邏輯交給 Kubelet,然後再經過 Containerd 轉交給我們的自定義外掛,該外掛會從目標映象中提取出 *.wasm 檔案讓 Layotto 載入執行。目前 Layotto 集成了 wasmer 作為 wasm 的執行時。
整套排程方案最終的使用效果如上圖所示,對於一個開發好的函式來說,首先把它編譯成 *.wasm 檔案,然後再構建成映象,部署過程中只需要在 yaml 檔案中指定 runtimeClassName 為 Layotto 即可。後續如建立容器、檢視容器狀態、刪除容器等操作都保留了 K8s 的語義,這對於 SRE 同學來說完全沒有額外的學習成本。
目前整套流程已經開源在 Layotto 社群,感興趣的同學可以參考我們的 QuickStart【1】文件進行體驗。
最後,我們來暢想一下未來可能的研發模型,首先在研發階段,開發人員可以自由選擇適合業務場景的語言編寫程式碼。
而對於開發工具來說,除了本地 IDE 以外可能越來越多的人會選擇 Cloud IDE 來開發,這將很大的提高開發人員的協作效率。然後是部署階段,對於一些輕量的業務場景,可能會按照函式模型進行部署,而對於傳統的業務,可能會保留 BaaS 模型,同時如果有更高的安全性訴求,一種可行方案是把業務部署在 Kata 之類的安全容器中。
最後,隨著 Unikernel 技術的成熟,可能會有越來越多的人在這個方向上進行嘗試,比如把 Layotto 打在 kernel 中跟應用一起編譯部署。
更重要的一點是,不管未來使用哪種模型部署,基於 Layotto 與生俱來的移植性,運維人員可以把應用隨意部署在任何平臺上,而這種切換對於開發人員來說完全透明!
最後在向用戶提供服務的階段,隨著函式服務啟動的速度越來越快,可以做到收到請求以後再載入執行函式,並且對它們可使用的資源進行嚴格精確的控制,真正做到按需計費。
PART. 4
開源與共贏
前文中提到的未來研發模式中其實依賴很多技術領域的發展,這些技術的成熟依賴整個技術社群的發展,這也是 Layotto 選擇開源的一個重要背景,因此我們跟多個社群都進行了交流,希望一起推動未來研發模型依賴的各項技術走向成熟。
A、Dapr 社群:API 標準化
Dapr 除了定義應用跟基礎設施之間的服務邊界以外,它還有一套 Runtime API 被人們廣為接受,上圖是我們在內部實際落地過程中針對這些 API 提出的各種修正建議,我們希望跟 Dapr 社群、阿里巴巴一起對這套 API 進行標準化建設。
B、WebAssembly 社群:生態建設
對於 WebAssembly 社群來說,我們會持續關注這項技術的整個生態發展,大體上分為以下幾類:
1.多語言支援
前面提到現在對 WebAssembly 支援較好的語言對於開發業務邏輯來說成本較高,因此希望隨著社群的發展可以更好的支援如 Java、Go、JS 等常見的業務開發語言。
2.WASM ABI
這裡主要是定義 wasm 函式跟 Layotto 之間進行互動所使用的 API 。社群中已有了一些嘗試,我們希望在此基礎上增加 Runtime ABI 的定義,讓函式可以更方便的呼叫基礎設施。
3.生態建設
我們希望 WebAssembly 技術具備更好的排查定位問題的能力,更精細的可使用資源控制手段,更多實用的高階特性。
C、Layotto 社群:微服務 & 函式的探索
Layotto 社群則會聚焦於未來研發模式的探索,主要分為以下兩類:
1.Sidecar 模型
- 在這種模型下,應用跟 Layotto 之間透過基於 gRPC 協議的 Runtime API 進行互動,這也是當下最容易落地的一種模型。
2.FaaS 模型
- 在這種模型下,Layotto 會藉助 WebAssembly 技術讓多個函式在同一個程序中執行,在此基礎上嘗試定義函式執行過程中所依賴的安全、服務、資源三大邊界。
|後記|
我們嘗試基於現階段微服務架構理論的發展,以及實際生產落地中解決各種問題所積累的寶貴經驗,來思考雲原生執行時的下一個五年會如何發展。
但這只是我們目前探索的一個方向,我們也相信存在其他的可能性,但有一點是很明確的,那就是雲原生執行時的下一個五年不是等來的,是眾多技術人員一起努力探索出來的。
「參 考」
【1】QuickStart
https://mosn.io/layotto/#/zh/start/faas/start
- 網易雲音樂 DBA 談 TiDB 選型:效率的選擇
- 分散式定時任務排程框架實踐
- 鏈路追蹤(Tracing)的前世今生(上)
- 基於 GraphQL 平臺化 BFF 構建及微服務治理
- 石墨文件Websocket百萬長連線技術實踐
技術原創及架構實踐文章,歡迎透過公眾號選單「聯絡我們」進行投稿。
高可用架構
改變網際網路的構建方式