美團 Serverless 的探索與實踐
Serverless 是目前比較熱門的技術話題,各大雲平臺以及互聯網大廠內部都在積極建設 Serverless 產品。本文將介紹美團 Serverless 產品在落地過程中的一些實踐經驗,其中包括技術選型的考量、系統的詳細設計、系統穩定性優化、產品的周邊生態建設以及在美團的落地情況。雖然各個公司的背景不盡相同,但總有一些可以相互借鑑的思路或方法,希望能給大家帶來一些啓發或者幫助。
-
1 背景
-
2 快速驗證,落地 MVP 版本
-
2.1 技術選型
-
2.2 架構設計
-
2.3 流程設計
-
2.4 函數觸發
-
2.5 函數執行
-
2.6 彈性伸縮
-
3 優化核心技術,保障業務穩定性
-
3.1 彈性伸縮優化
-
3.2 冷啓動優化
-
3.3 高可用保障
-
3.4 容器穩定性優化
-
4 完善生態,落實收益
-
4.1 提供研發工具
-
4.2 融合技術生態
-
4.3 開放平臺能力
-
4.4 支持合併部署
-
5 落地場景、收益
-
5.1 落地場景
-
5.2 落地收益
-
6 未來規劃
-
作者簡介
-
招聘信息
1 背景
Serverless 一詞於 2012 年被提出,2014 年由於亞馬遜的 AWS Lambda 無服務器計算服務的興起,而被大家廣泛認知。Serverless 通常被直譯成 “無服務器”,無服務器計算是可以讓用戶在不考慮服務器的情況下構建並運行應用程序。使用無服務器計算,應用程序仍在服務器上運行,但所有服務器管理工作均由 Serverless 平臺負責。如機器申請、代碼發佈、機器宕機、實例擴縮容、機房容災等都由平臺幫助自動完成,業務開發只需考慮業務邏輯的實現即可。
回顧計算行業的發展歷程,基礎設施從物理機到虛擬機,再從虛擬機到容器;服務架構從傳統單體應用架構到 SOA 架構,再從 SOA 架構到微服務架構。從基礎設施和服務架構兩條主線來看整體技術發展趨勢,大家可能會發現,不論是基礎設施還是服務架構,都是從大往小或者由巨到微的方向上演進,這種演變的本質原則無非是解決資源成本或者研發效率的問題。當然,Serverless 也不例外,它也是用來解決這兩個方面的問題:
-
資源利用率:Serverless 產品支持快速彈性伸縮能力,能夠幫助業務提升資源利用率,在業務流量高峯時,業務的計算能力、容量自動擴容,承載更多的用戶請求,而在業務流量下降時,所使用的資源也會同時收縮,避免資源浪費。
-
研發運維效率:在 Serverless 上開發人員一般只需要填寫代碼路徑或者上傳代碼包,平臺能夠幫助完成構建、部署的工作。開發人員不直接面對機器,對於機器的管理,機器是否正常以及流量高低峯的是否需要擴縮容等問題,這些統統不需要去考慮,由 Serverless 產品幫助研發人員去完成。這樣就能使他們從繁瑣的運維工作中解放出來,從 DevOps 轉向 NoOps,更加專注於業務邏輯的實現。
雖然 AWS 在 2014 年就推出了第一個 Serverless 產品 Lambda,但 Serverless 技術在國內的應用一直不溫不火。不過近兩三年,在容器、Kubernetes 以及雲原生等技術的推動下,Serverless 技術迅速發展,國內各大互聯網公司都在積極建設 Serverless 相關產品,探索 Serverless 技術的落地。在這種背景下,美團也於 2019 年初開始了 Serverless 平臺的建設,內部項目名稱爲 Nest。
截止到目前,Nest 平臺已經過兩年的建設,回顧整體的建設過程,主要經歷了以下三個階段:
-
快速驗證,落地 MVP 版本:我們通過技術選型、產品與架構設計、開發迭代,快速落地了 Serverless 產品的基本的能力,如構建、發佈、彈性伸縮、對接觸發源、執行函數等。上線後,我們推進了一些業務的試點接入,幫助驗證打磨產品。
-
優化核心技術,保障業務穩定性:有了前期的試點業務驗證,我們很快發現產品的存在的一些穩定性相關的問題,主要有彈性伸縮的穩定性、冷啓動的速度、系統與業務的可用性、容器的穩定性。針對這些問題我們對各個問題涉及的技術點做了專項的優化改進。
-
完善技術生態,落實收益:優化了核心技術點後,產品逐漸成熟穩定,但依然面臨生態性問題,如研發工具欠缺,上下游產品沒有打通、平臺開放能力不足等問題,影響或阻礙了產品的推廣使用。因此,我們繼續完善產品的技術生態,掃清業務接入使用障礙,落實產品的業務收益。
2 快速驗證,落地 MVP 版本
2.1 技術選型
建設 Nest 平臺,首要解決的就是技術選型問題,Nest 主要涉及三個關鍵點的選型:演進路線、基礎設施、開發語言。
2.1.1 演進路線
起初 Serverless 服務主要包含 FaaS(Function as a Service)和 BaaS(Backend as a Service),近幾年 Serverless 的產品領域有所擴張,它還包含面向應用的 Serverless 服務。
-
FaaS:是運行在一個無狀態的計算容器中的函數服務,函數通常是事件驅動、生命週期很短(甚至只有一次調用)、完全由第三方管理的。業界相關 FaaS 產品有 AWS 的 Lambda、阿里雲的函數計算等。
-
BaaS:是建立在雲服務生態之上的後端服務。業界相關 BaaS 產品包括 AWS 的 S3、DynamoDB 等。
面向應用的 Serverless 服務:如 Knative,它提供了從代碼包到鏡像的構建、部署以及實例彈性伸縮等全面的服務託管能力,公有云產品有 Google Cloud Run(基於 Knative)、阿里雲的 SAE(Serverless Application Engine)。
在美團內部,BaaS 產品其實就是內部的中間件以及底層服務等,它們經過多年的發展,已經非常豐富且成熟了。因此,在美團的 Serverless 產品演進主要在函數計算服務和麪嚮應用的 Serverless 服務兩個方向上。那究竟該如何演進呢?當時主要考慮到在業界 FaaS 函數計算服務相對於面向應用的 Serverless 服務來說,更加成熟且確定。因此,我們決定 “先建設 FaaS 函數計算服務,再建設面向應用的 Serverless 服務” 這樣一條演進路線。
2.1.2 基礎設施
由於彈性伸縮是 Serverless 平臺必備的能力,因此 Serverless 必然涉及到底層資源的調度和管理。這也是爲什麼當前業界有很多開源的 Serverless 產品(如 OpenFaaS、Fission、Nuclio、Knative 等)是基於 Kubernetes 來實現的,因爲這種選型能夠充分利用 Kubernetes 的基礎設施的管理能力。在美團內部基礎設施產品是 Hulk,雖然 Hulk 是基於 Kubernetes 封裝後的產品,但 Hulk 在落地之初考慮到落地難度以及各種原因,最終未按照原生的方式來使用 Kubernetes,並且在容器層採用的也是富容器模式。
在這種歷史背景下,我們在做基礎設施選型時就面臨兩種選項:一是使用公司的 Hulk 來作爲 Nest 的基礎設施(非原生 Kubernetes),二是採用原生 Kubernetes 基礎設施。我們考慮到當前業界使用原生 Kubernetes 是主流趨勢並且使用原生 Kubernetes 還能充分利用 Kubernetes 原生能力,可以減少重複開發。因此,最終考量的結果是我們採用了原生 Kubernetes 作爲我們的基礎設施。
2.1.3 開發語言
雖然無論在雲原生領域,還是 Kubernetes 的生態中,Golang 都更加主流,但在美團 Java 纔是使用最廣泛的語言,相比 Golang,Java 在公司內部生態比較好。因此,在語言的選型上我們選擇了 Java 語言。在 Nest 產品開發之初,Kubernetes 社區的 Java 客戶端還不夠完善,但隨着項目的推進,社區的 Java 客戶端也逐漸豐富了起來,目前已經完全夠用了。另外,我們也在使用過程中,也貢獻了一些 Pull Request,反哺了社區。
2.2 架構設計
基於以上的演進路線、基礎設施、開發語言的選型,我們進行了 Nest 產品的架構設計。
在整體的架構上,流量由 EventTrigger(事件觸發源,如 Nginx、應用網關、定時任務、消息隊列、RPC 調用等)觸發到 Nest 平臺,Nest 平臺內會根據流量的特徵路由到具體函數實例,觸發函數執行,而函數內部代碼邏輯可以調用公司內的各個 BaaS 服務,最終完成函數的執行,返回結果。
圖 1 FaaS 架構圖
在技術實現上,Nest 平臺使用 Kubernetes 作爲基礎底座並適當參考了一些 Knative 的優秀設計,在其架構內部主要由以下幾個核心部分組成:
-
事件網關:核心能力是負責對接外部事件源的流量,然後路由到函數實例上;另外,網關還負責統計各個函數的進出流量信息,爲彈性伸縮模塊提供伸縮決策的數據支撐。
-
彈性伸縮:核心能力是負責函數實例的彈性伸縮,伸縮主要根據函數運行的流量數據以及實例閾值配置計算函數目標實例個數,然後藉助 Kubernetes 的資源控制能力,調整函數實例的個數。
-
控制器:核心能力是負責 Kubernetes CRD(Custom Resource Definition)的控制邏輯實現。
-
函數實例:函數的運行實例。當事件網關流量觸發過來,會在函數實例內執行相應的函數代碼邏輯。
-
治理平臺:面向用戶使用的平臺,負責函數的構建、版本、發佈以及一些函數元信息的管理等。
圖 2 Nest 架構圖
2.3 流程設計
在具體的 CI/CD 流程上,Nest 又與傳統的模式有何區別呢?爲了說明這個問題,我們先來看一看在 Nest 平臺上函數的整體生命週期怎樣的?具體有以下四個階段:構建、版本、部署、伸縮。
-
構建:開發的代碼和配置通過構建生成鏡像或可執行文件。
-
版本:構建生成的鏡像或可執行文件加上發佈配置形成一個不可變的版本。
-
部署:將版本發佈,即完成部署。
-
伸縮:根據函數實例的流量以及負載等信息,來進行實例的彈性擴縮容。
就這四個階段來看,Nest 與傳統的 CI/CD 流程本質區別在於部署和伸縮:傳統的部署是感知機器的,一般是將代碼包發佈到確定的機器上,但 Serverless 是要向用戶屏蔽機器的(在部署時,可能函數的實例數還是 0);另外,傳統的模式一般是不具備動態擴縮容的,而 Serverless 則不同,Serverless 平臺會根據業務的自身流量需要,進行動態擴縮容。後續章節會詳細講解彈性伸縮,因此這裏我們只探討部署的設計。
部署的核心點在於如何向用戶屏蔽機器?對於這個問題,我們抽象了機器,提出了分組的概念,分組是由 SET(單元化架構的標識,機器上會帶有該標識)、泳道(測試環境隔離標識,機器上會帶有該標識)、區域(上海、北京等)三個信息組成。用戶部署只需在相應的分組上進行操作,而不用涉及到具體機器。能夠做到這些的背後,是由 Nest 平臺幫助用戶管理了機器資源,每次部署會根據分組信息來實時初始化相應的機器實例。
圖 3 函數生命週期
2.4 函數觸發
函數的執行是由事件觸發的。完成函數的觸發,需要實現以下四個流程:
-
流量引入:向事件源註冊事件網關的信息,將流量引入到事件網關。如針對 MQ 事件源,通過註冊 MQ 的消費組,引入 MQ 的流量到事件網關。
-
流量適配:事件網關對事件源進入的流量進行適配對接。
-
函數發現:對函數元數據(函數實例信息、配置信息等)的獲取過程,類似微服務的服務發現過程。事件網關接受的事件流量需要發送到具體的函數實例,這就需要對函數進行發現。這裏發現實質是獲取 Kubernetes 中的內置資源或者 CRD 資源中存儲的信息。
-
函數路由:事件流量的路由過程,路由到特定的函數實例上。這裏爲了支持傳統路由邏輯(如 SET、泳道、區域路由等)以及版本路由能力,我們採用了多層路由,第一層路由到分組(SET、泳道、區域路由),第二層路由到具體版本。同版本內的實例,通過負載均衡器選擇出具體實例。另外,通過該版本路由,我們很輕鬆的支持了金絲雀、藍綠髮布。
圖 4 函數觸發
2.5 函數執行
函數不同於傳統的服務,傳統的服務是個可執行的程序,但函數不同,函數是代碼片段,自身是不能單獨執行的。那流量觸發到函數實例後,函數是如何執行的呢?
函數的執行的首要問題是函數的運行環境:由於 Nest 平臺是基於 Kubernetes 實現的,因此函數一定是運行在 Kubernetes 的 Pod(實例)內,Pod 內部是容器,容器的內部是運行時,運行時是函數流量接收的入口,最終也是由運行時來觸發函數的執行。一切看起來是那麼的順利成章,但我們在落地時是還是遇到了一些困難,最主要的困難是讓開發同學可以在函數內無縫的使用公司內的組件,如 OCTO(服務框架)、Celler(緩存系統)、DB 等。
在美團的技術體系中,由於多年的技術沉澱,很難在一個純粹的容器(沒有任何其他依賴)中運行公司的業務邏輯。因爲公司的容器中沉澱了很多環境或服務治理等能力,如服務治理的 Agent 服務以及實例環境配置、網絡配置等。
因此,爲了業務在函數內無縫的使用公司內的組件,我們複用公司的容器體系來降低業務編寫函數的成本。但複用公司的容器體系也沒那麼簡單,因爲在公司內沒有人試過這條路,Nest 是公司第一個基於原生 Kubernetes 建設的平臺,“第一個喫螃蟹的人” 總會遇到一些坑。對於這些坑,我們只能在推進過程中 “逢山開路,遇水搭橋”,遇到一個解決一個。總結下來,其中最核心的是在容器的啓動環節打通的 CMDB 等技術體系,讓運行函數的容器與開發同學平時申請的機器用起來沒有任何區別。
圖 5 函數執行
2.6 彈性伸縮
彈性伸縮的核心問題主要有三個:什麼時候伸縮,伸縮多少,伸縮的速度快不快?也就是伸縮時機、伸縮算法、伸縮速度的問題。
-
伸縮時機:根據流量 Metrics 實時計算函數期望實例數,進⾏擴縮。流量的 Metrics 數據來自於事件網關,這裏主要統計函數的併發度指標,彈性伸縮組件每秒中會主動從事件網關獲取一次 Metrics 數據。
-
伸縮算法:併發度 / 單實例閾值 = 期望實例數。根據收集的 Metrics 數據以及業務配置的閾值,通過算法計算出期望的實例數,然後通過 Kubernetes 接口設置具體實例數。整個算法看起來雖然簡單,但非常穩定、魯棒性好。
-
伸縮速度:主要取決於冷啓動時間,在下個章節會詳細講解這塊內容。
除了基本的擴縮容能力,我們還支持了伸縮到 0,支持配置最大、最小實例數(最小實例即預留實例)。伸縮到 0 的具體實現是,我們在事件網關內部增加了激活器模塊,當函數無實例時,會將函數的請求流量緩存在激活器內部,然後立即通過流量的 Metrics 去驅動彈性伸縮組件進行擴容,等擴容的實例啓動完成後,激活器再將緩存的請求重試到擴容的實例上觸發函數執行。
圖 6 彈性伸縮
3 優化核心技術,保障業務穩定性
3.1 彈性伸縮優化
上面提到的伸縮時機、伸縮算法、伸縮速度這三要素都是理想情況下的模型,尤其是伸縮速度,當前技術根本做不到毫秒級別的擴縮容。因此,在線上實際場景中,彈性伸縮會存在一些不符合預期的情況,比如實例伸縮比較頻繁或者擴容來不及,導致服務不太穩定的問題。
-
針對實例伸縮比較頻繁問題,我們在彈性伸縮組件內維護了統計數據的滑動窗⼝,通過計算均值來平滑指標,還通過延時縮容,實時擴容來緩解頻繁擴縮問題。另外,我們增加了基於 QPS 指標的伸縮策略,因爲 QPS 指標相對併發度指標會更加穩定。
-
針對擴容來不及問題,我們採取提前擴容的手段,當達到實例閾值的 70% 就擴容,能夠比較好的緩解這個問題。除此之外,我們還支持了多指標混合伸縮(併發度、QPS、CPU、Memory),定時伸縮等策略,滿足各種業務需求。
下圖展示的是線上彈性伸縮的真實案例(配置的最小實例數爲 4,單實例閾值 100,閾值使用率 0.7),其中上半部分是業務每秒的請求數,下半部分是擴縮實例的決策圖,可以看到在成功率 100% 的情況下,業務完美應對流量高峯。
圖 7 彈性伸縮案例
3.2 冷啓動優化
冷啓動是指在函數調用鏈路中包含了資源調度、鏡像 / 代碼下載、啓動容器、運行時初始化、用戶代碼初始化等環節。當冷啓動完成後,函數實例就緒,後續請求就能直接被函數執行。冷啓動在 Serverless 領域至關重要,它的耗時決定了彈性伸縮的速度。
所謂 “天下武功,無堅不破,唯快不破”,這句話在 Serverless 領域也同樣受用。試想如果拉起一個實例足夠快,快到毫秒級別,那幾乎所有的函數實例都可以縮容到 0,等有流量時,再擴容實例處理請求,這對於存在高低峯流量的業務將極大的節省機器資源成本。當然,理想很豐滿,現實很骨感。做到毫秒級別幾乎不可能。但只要冷啓動時間越來越短,成本自然就會越來越低,另外,極短的冷啓動時間對伸縮時函數的可用性以及穩定性都有莫大的好處。
圖 8 冷啓動的各個階段
冷啓動優化是個循序漸進的過程,我們對冷啓動優化主要經歷了三個階段:鏡像啓動優化、資源池優化、核心路徑優化。
- 鏡像啓動優化:我們對鏡像啓動過程中的耗時環節(啓動容器和運行時初始化)進行了針對性優化,主要對容器 IO 限速、一些特殊 Agent 啓動耗時、啓動盤與數據盤數據拷貝等關鍵點的優化,最終將啓動過程中的系統耗時從 42s 優化到 12s 左右。
圖 9 鏡像啓動優化成果
- 資源池優化:鏡像啓動耗時優化到 12s,基本已經快達到瓶頸點,再繼續優化空間不大。因此,我們想能否繞開鏡像啓動的耗時環節?最終,我們採用了一個比較簡單思路 “空間換時間”,用資源池方案:緩存一些已啓動的實例,當需要擴容時,直接從資源池獲取實例,繞開鏡像啓動容器的環節,最終效果很明顯,將啓動的系統耗時從 12s 優化到 3s。這裏需要說明的是資源池自身也是通過 Kubernetes 的 Depolyment 進行管理,池中實例被取走會立即自動補充。
圖 10 資源池優化成果
- 核心路徑優化:在資源池優化的基礎上,我們再次精益求精,針對啓動流程中的下載與解壓代碼兩個耗時環節進行優化,過程中我們採用了高性能的壓縮解壓算法(LZ4 與 Zstd)以及並行下載和解壓技術,效果非常好。另外,我們還支持了通用邏輯(中間件、依賴包等)下沉,通過預加載的方式,最終將函數端到端的啓動耗時優化到 2s,這就意味着擴容一個函數實例只需要 2s(包含函數啓動)。如果排除掉函數自身的初始化啓動耗時,平臺側的耗時已在毫秒級別。
3.3 高可用保障
說到高可用,對於一般的平臺,指的就是平臺自身的高可用,但 Nest 平臺有所不同,Nest 的高可用還包含託管在 Nest 平臺上的函數。因此,Nest 的高可用保障需要從平臺和業務函數兩個方面着手。
3.3.1 平臺高可用
對平臺的高可用,Nest 主要從架構層、服務層、監控運營層、業務視角層面都做了全面的保障。
- 架構層:我們針對有狀態服務,如彈性伸縮模塊,採用了主從架構,當主節點異常時從節點會立即替換。另外,我們還實現了架構上的多層隔離。橫向地域隔離:Kubernetes 兩地兩集羣強隔離、服務(事件網關、彈性伸縮)集羣內兩地弱隔離(上海的彈性伸縮只負責上海 Kubernetes 集羣內的業務伸縮,事件網關存在兩地調用需求,需訪問兩地 Kubernetes)。縱向業務線隔離:服務業務線強隔離,不同業務線使用不同集羣服務;在 Kubernetes 層的資源用 namespace 實現業務線弱隔離。
圖 11 部署架構
-
服務層:主要指的是事件網關服務,由於所有的函數流量都經過事件網關,因此事件網關的可用性尤爲重要,這層我們支持了限流和異步化,保障服務的穩定性。
-
監控運營層:主要通過完善系統監控告警、梳理核心鏈路並推動相關依賴方進行治理。另外,我們會定期梳理 SOP 並通過故障演練平臺實施故障注入演練,發現系統隱患問題。
-
業務視角層:我們開發了在線不間斷實時巡檢服務,通過模擬用戶函數的請求流量,實時檢測系統的核心鏈路是否正常。
3.3.2 業務高可用
對於業務高可用,Nest 主要從服務層、平臺層兩個層面做了相關的保障。
-
服務層:支持了業務降級、限流能力:當後端函數故障時,可通過降級配置,返回降級結果。針對異常的函數流量,平臺支持限制其流量,防止後端函數實例的被異常流量打垮。
-
平臺層:支持了實例保活、多層級容災以及豐富的監控告警能力:當函數實例異常時,平臺會自動隔離該實例並立即擴容新實例。平臺支持業務多地區部署,在同地區將函數實例儘可能打散不同機房。當宿主機、機房、地區故障時,會立即在可用宿主機、可用機房或可用區重建新實例。另外,平臺自動幫業務提供了函數在時延、成功率、實例伸縮、請求數等多種指標的監控,當在這些指標不符合預期時,自動觸發告警,通知業務開發和管理員。
圖 12 業務監控
3.4 容器穩定性優化
前文已提到,Serverless 與傳統模式在 CI/CD 流程上是不同的,傳統模式都是事先準備好機器然後部署程序,而 Serverless 則是根據流量的高低峯實時彈性擴縮容實例。當新實例擴容出來後,會立即處理業務流量。這聽起來貌似沒什麼毛病,但在富容器生態下是存在一些問題的:我們發現剛擴容的機器負載非常高,導致一些業務請求執行失敗,影響業務可用性。
分析後發現主要是因爲容器啓動後,運維工具會進行 Agent 升級、配置修改等操作,這些操作非常耗 CPU。同在一個富容器中,自然就搶佔了函數進程的資源,導致用戶進程不穩定。另外,函數實例的資源配置一般比傳統服務的機器要小很多,這也加劇了該問題的嚴重性。基於此,我們參考業界,聯合容器設施團隊,落地了輕量級容器,將運維的所有 Agent 放到 Sidecar 容器中,而業務的進程單獨放到 App 容器中。採用這種容器的隔離機制,保障業務的穩定性。同時,我們也推動了容器裁剪計劃,去掉一些不必要的 Agent。
圖 13 輕量級容器
4 完善生態,落實收益
Serverless 是個系統工程,在技術上涉及到 Kubernetes、容器、操作系統、JVM、運行時等各種技術,在平臺能力上涉及到 CI/CD 各個流程的方方面面。
爲了給用戶提供極致的開發體驗,我們爲用戶提供了開發工具的支持,如 CLI(Command Line Interface)、WebIDE 等。爲了解決現有上下游技術產品的交互的問題,我們與公司現有的技術生態做了融合打通,方便開發同學使用。爲了方便下游的集成平臺對接,我們開放了平臺的 API,實現 Nest 賦能各下游平臺。針對容器過重,系統開銷大,導致低頻業務函數自身資源利用率不高的問題,我們支持了函數合併部署,成倍提升資源利用率。
4.1 提供研發工具
開發工具能夠降低平臺的使用成本,幫助開發同學快速的進行 CI/CD 流程。目前 Nest 提供了 CLI 工具,幫助開發同學快速完成創建應用、本地構建、本地測試、Debug、遠程發佈等操作。Nest 還提供了 WebIDE,支持在線一站式完成代碼的修改、構建、發佈、測試。
4.2 融合技術生態
僅支持這些研發工具還是不夠的,項目推廣使用後,我們很快就發現開發同學對平臺有了新的需求,如無法在 Pipeline 流水線、線下服務實例編排平臺上完成對函數的操作,這對我們項目的推廣也形成了一些阻礙。因此,我們融合這些公司的成熟技術生態,打通了 Pipeline 流水線等平臺,融入到現有的上下游技術體系內,解決用戶的後顧之憂。
4.3 開放平臺能力
有很多 Nest 的下游解決方案平臺,如 SSR(Server Side Render)、服務編排平臺等,通過對接 Nest 的 OpenAPI,實現了生產力的進一步解放。例如,不用讓開發同學自己去申請、管理和運維機器資源,就能夠讓用戶非常快速的實現一個 SSR 項目或者編排程序從 0 到 1 的創建、發佈與託管。
Nest 除了開放了平臺的 API,還對用戶提供了自定義資源池的能力,擁有了該項能力,開發同學可以定製自己的資源池,定製自己的機器環境,甚至可以下沉一些通用的邏輯,實現冷啓動的進一步優化。
4.4 支持合併部署
合併部署指的是將多個函數部署在一個機器實例內。合併部署的背景主要有兩個:
-
當前的容器較重,容器自身的系統開銷較大,導致業務進程資源利用率不高(尤其是低頻業務)。
-
在冷啓動耗時不能滿足業務對時延的要求的情況下,我們通過預留實例來解決業務的需求。
基於這兩個背景,我們考慮支持合併部署,將一些低頻的函數部署到同一個機器實例內,來提升預留實例中業務進程的資源利用率。
在具體實現上,我們參考 Kubernetes 的設計方案,設計了一套基於 Sandbox 的函數合併部署體系(每個 Sandbox 就是一個函數資源),將 Pod 類比成 Kubernetes 的 Node 資源,Sandbox 類比成 Kubernetes 的 Pod 資源,Nest Sidecar 類比成 Kubelet。爲了實現 Sandbox 特有的部署、調度等能力,我們還自定義了一些 Kubernetes 資源(如 SandboxDeployment、SandboxReplicaSet、SandboxEndpoints 等)來支持函數動態插拔到具體的 Pod 實例上。
圖 14 合併部署架構
除此之外,在合併部署的形態下,函數之間的隔離性也是不可迴避的問題。爲了儘可能的解決函數(合併在同一個實例中)之間的互相干擾問題,在 Runtime 的實現上,我們針對 Node.js 和 Java 語言的特點採取了不同的策略:Node.js 語言的函數使用不同的進程來實現隔離,而 Java 語言的函數,我們採用類加載隔離。採用這種策略的主要原因是由於 Java 進程佔用內存空間相較於 Node.js 進程會大很多。
5 落地場景、收益
目前 Nest 產品在美團前端 Node.js 領域非常受歡迎,也是落地最廣泛的技術棧。當前 Nest 產品在美團前端已實現了規模化落地,幾乎涵蓋了所有業務線,接入了大量的 B/C 端的核心流量。
5.1 落地場景
具體的落地前端場景有:BFF(Backend For Frontend)、CSR(Client Side Render)/SSR(Server Side Render)、後臺管理平臺、定時任務、數據處理等。
-
BFF 場景:BFF 層主要爲前端頁面提供數據,採用 Serverless 模式,前端同學不需要考慮不擅長的運維環節,輕鬆實現了 BFF 向 SFF(Serverless For Frontend)模式的轉變。
-
CSR/SSR 場景:CSR/SSR 指的是客戶端渲染和服務端渲染,有了 Serverless 平臺,不用考慮運維環節,更多的前端業務來嘗試使用 SSR 來實現前端首屏的快速展現。
-
後臺管理平臺場景:公司有很多的後臺管理平臺的 Web 服務,它們雖然相較於函數是比較重的,但完全可以直接託管 Serverless 平臺,充分享受 Serverless 平臺極致的發佈和運維效率。
-
定時任務場景:公司存在很多週期性任務,如每隔幾秒拉取數據,每天 0 點清理日誌,每小時收集全量數據並生成報表等,Serverless 平臺直接與任務調度系統打通,只需寫好任務的處理邏輯並在平臺上配置定時觸發器,即完成定時任務的接入,完全不用管理機器資源。
-
數據處理場景:將 MQ Topic 作爲事件源接入 Serverless 平臺,平臺會自動訂閱 Topic 的消息,當有消息消費時,觸發函數執行,類似定時任務場景,作爲用戶也只需寫好數據處理的邏輯並在平臺上配置好 MQ 觸發器,即完成 MQ 消費端的接入,完全不用管理機器資源。
5.2 落地收益
Serverless 的收益是非常明顯的,尤其在前端領域,大量的業務接入已是最好的說明。具體收益,從以下兩個方面分別來看:
-
降成本:通過 Serverless 的彈性伸縮能力,高頻業務資源利用率能提升到 40%~50%;低頻業務函數通過合併部署,也能極大降低函數運行成本。
-
提效率:整體研發研發效率提升約 40%。
-
從代碼開發來看,提供完備的 CLI、WebIDE 等研發工具,能夠幫助開發同學生成代碼腳手架,聚焦編寫業務邏輯,快速完成本地測試;另外,讓業務服務零成本具備在線查看日誌與監控的能力。
-
從發佈來看,通過雲原生的模式,業務無需申請機器,發佈、回滾都是秒級別的體驗。另外,還能利用平臺天然能力,配合事件網關,實現切流、完成金絲雀測試等。
-
從日常運維來看,業務無需關注機器故障、資源不足、機房容災等傳統模式該考慮的問題,另外,當業務進程異常時,Nest 能夠自動完成異常實例的隔離,迅速拉起新實例實現替換,降低業務影響。
6 未來規劃
-
場景化解決方案:接入 Serverless 的場景衆多,如 SSR、後臺管理端、BFF 等,不同的場景有不同的項目模板、場景配置,如伸縮配置、觸發器配置等,另外,不同的語言,配置也有所不同。這無形中增加了業務的使用成本,給新業務的接入帶來了阻礙。因此,我們考慮場景化的思路來建設平臺,將平臺的能力與場景強關聯起來,平臺深度沉澱各場景的基本配置和資源,這樣不同的場景,業務只需要簡單的配置就可以將 Serverless 玩轉起來。
-
**傳統微服務 Serverless 化:**即是路線選型中提到的面向應用的 Serverless 服務。在美團使用最廣的開發語言是 Java,公司內部存在大量的傳統的微服務項目,這些項目如果都遷移到函數模式,顯然是不現實的。試想如果這些傳統的微服務項目不用改造,也能直接享受 Serverless 的技術紅利,其業務價值不言而喻。因此,傳統微服務的 Serverless 化是我們未來拓展業務的一個重要方向。在實施路徑上,我們會考慮將服務治理體系(如 ServiceMesh)與 Serverless 做技術融合,服務治理組件爲 Serverless 提供伸縮指標支持並在伸縮過程中實現精準的流量調配。
-
**冷啓動優化:**當前雖然函數的冷啓動優化已經取得了較好的成績,尤其是平臺側的系統啓動耗時,提升空間已經非常有限,但業務代碼自身的啓動耗時還是非常突出,尤其是傳統 Java 微服務,基本是分鐘級別的啓動耗時。因此,後續我們的冷啓動優化會重點關注業務自身的啓動耗時,爭取極大降低業務自身的啓動時間。在具體優化方法上,我們會考慮採用 AppCDS、GraalVM 等技術,降低業務自身啓動耗時。
-
其他規劃
-
豐富完善研發工具,提升研發效率,如 IDE 插件等。
-
**打通上下游技術生態,**深度融入公司現有技術體系,減少因上下游平臺帶來使用障礙。
-
**容器輕量化,**輕量化的容器能夠帶來更優的啓動耗時以及更佳的資源利用率,因此,容器輕量化一直是 Serverless 的不懈追求。在具體落地上,準備聯合容器設施團隊一起推進容器中的一些 Agent 採用 DaemonSet 方式部署,下沉到宿主機,提升容器的有效載荷。
作者簡介
-
殷琦、華珅、飛飛、志洋、奕錕等,來自基礎架構部應用中間件團隊。
-
佳文、凱鑫,亞輝等,來自金融技術平臺大前端團隊。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/n5JVLJ4Vf5stWR7SjVchLw