|
總第447篇 2021年 第017篇
1 背景Serverless一詞于2012年被提出,2014年由于亞馬遜的AWS Lambda無服務(wù)器計算服務(wù)的興起,而被大家廣泛認知。Serverless通常被直譯成“無服務(wù)器”,無服務(wù)器計算是可以讓用戶在不考慮服務(wù)器的情況下構(gòu)建并運行應(yīng)用程序。使用無服務(wù)器計算,應(yīng)用程序仍在服務(wù)器上運行,但所有服務(wù)器管理工作均由Serverless平臺負責。如機器申請、代碼發(fā)布、機器宕機、實例擴縮容、機房容災(zāi)等都由平臺幫助自動完成,業(yè)務(wù)開發(fā)只需考慮業(yè)務(wù)邏輯的實現(xiàn)即可。 回顧計算行業(yè)的發(fā)展歷程,基礎(chǔ)設(shè)施從物理機到虛擬機,再從虛擬機到容器;服務(wù)架構(gòu)從傳統(tǒng)單體應(yīng)用架構(gòu)到SOA架構(gòu),再從SOA架構(gòu)到微服務(wù)架構(gòu)。從基礎(chǔ)設(shè)施和服務(wù)架構(gòu)兩條主線來看整體技術(shù)發(fā)展趨勢,大家可能會發(fā)現(xiàn),不論是基礎(chǔ)設(shè)施還是服務(wù)架構(gòu),都是從大往小或者由巨到微的方向上演進,這種演變的本質(zhì)原則無非是解決資源成本或者研發(fā)效率的問題。當然,Serverless也不例外,它也是用來解決這兩個方面的問題:
雖然AWS在2014年就推出了第一個Serverless產(chǎn)品Lambda,但Serverless技術(shù)在國內(nèi)的應(yīng)用一直不溫不火。不過近兩三年,在容器、Kubernetes以及云原生等技術(shù)的推動下,Serverless技術(shù)迅速發(fā)展,國內(nèi)各大互聯(lián)網(wǎng)公司都在積極建設(shè)Serverless相關(guān)產(chǎn)品,探索Serverless技術(shù)的落地。在這種背景下,美團也于2019年初開始了Serverless平臺的建設(shè),內(nèi)部項目名稱為Nest。 截止到目前,Nest平臺已經(jīng)過兩年的建設(shè),回顧整體的建設(shè)過程,主要經(jīng)歷了以下三個階段:
2 快速驗證,落地MVP版本2.1 技術(shù)選型建設(shè)Nest平臺,首要解決的就是技術(shù)選型問題,Nest主要涉及三個關(guān)鍵點的選型:演進路線、基礎(chǔ)設(shè)施、開發(fā)語言。 2.1.1 演進路線起初Serverless服務(wù)主要包含F(xiàn)aaS(Function as a Service)和BaaS(Backend as a Service),近幾年Serverless的產(chǎn)品領(lǐng)域有所擴張,它還包含面向應(yīng)用的Serverless服務(wù)。
面向應(yīng)用的Serverless服務(wù):如Knative,它提供了從代碼包到鏡像的構(gòu)建、部署以及實例彈性伸縮等全面的服務(wù)托管能力,公有云產(chǎn)品有Google Cloud Run(基于Knative)、阿里云的SAE(Serverless Application Engine)。 在美團內(nèi)部,BaaS產(chǎn)品其實就是內(nèi)部的中間件以及底層服務(wù)等,它們經(jīng)過多年的發(fā)展,已經(jīng)非常豐富且成熟了。因此,在美團的Serverless產(chǎn)品演進主要在函數(shù)計算服務(wù)和面向應(yīng)用的Serverless服務(wù)兩個方向上。那究竟該如何演進呢?當時主要考慮到在業(yè)界FaaS函數(shù)計算服務(wù)相對于面向應(yīng)用的Serverless服務(wù)來說,更加成熟且確定。因此,我們決定“先建設(shè)FaaS函數(shù)計算服務(wù),再建設(shè)面向應(yīng)用的Serverless服務(wù)”這樣一條演進路線。 2.1.2 基礎(chǔ)設(shè)施由于彈性伸縮是Serverless平臺必備的能力,因此Serverless必然涉及到底層資源的調(diào)度和管理。這也是為什么當前業(yè)界有很多開源的Serverless產(chǎn)品(如OpenFaaS、Fission、Nuclio、Knative等)是基于Kubernetes來實現(xiàn)的,因為這種選型能夠充分利用Kubernetes的基礎(chǔ)設(shè)施的管理能力。在美團內(nèi)部基礎(chǔ)設(shè)施產(chǎn)品是Hulk,雖然Hulk是基于Kubernetes封裝后的產(chǎn)品,但Hulk在落地之初考慮到落地難度以及各種原因,最終未按照原生的方式來使用Kubernetes,并且在容器層采用的也是富容器模式。 在這種歷史背景下,我們在做基礎(chǔ)設(shè)施選型時就面臨兩種選項:一是使用公司的Hulk來作為Nest的基礎(chǔ)設(shè)施(非原生Kubernetes),二是采用原生Kubernetes基礎(chǔ)設(shè)施。我們考慮到當前業(yè)界使用原生Kubernetes是主流趨勢并且使用原生Kubernetes還能充分利用Kubernetes原生能力,可以減少重復開發(fā)。因此,最終考量的結(jié)果是我們采用了原生Kubernetes作為我們的基礎(chǔ)設(shè)施。 2.1.3 開發(fā)語言雖然無論在云原生領(lǐng)域,還是Kubernetes的生態(tài)中,Golang都更加主流,但在美團Java才是使用最廣泛的語言,相比Golang,Java在公司內(nèi)部生態(tài)比較好。因此,在語言的選型上我們選擇了Java語言。在Nest產(chǎn)品開發(fā)之初,Kubernetes社區(qū)的Java客戶端還不夠完善,但隨著項目的推進,社區(qū)的Java客戶端也逐漸豐富了起來,目前已經(jīng)完全夠用了。另外,我們也在使用過程中,也貢獻了一些Pull Request,反哺了社區(qū)。 2.2 架構(gòu)設(shè)計基于以上的演進路線、基礎(chǔ)設(shè)施、開發(fā)語言的選型,我們進行了Nest產(chǎn)品的架構(gòu)設(shè)計。 在整體的架構(gòu)上,流量由EventTrigger(事件觸發(fā)源,如Nginx、應(yīng)用網(wǎng)關(guān)、定時任務(wù)、消息隊列、RPC調(diào)用等)觸發(fā)到Nest平臺,Nest平臺內(nèi)會根據(jù)流量的特征路由到具體函數(shù)實例,觸發(fā)函數(shù)執(zhí)行,而函數(shù)內(nèi)部代碼邏輯可以調(diào)用公司內(nèi)的各個BaaS服務(wù),最終完成函數(shù)的執(zhí)行,返回結(jié)果。 在技術(shù)實現(xiàn)上,Nest平臺使用Kubernetes作為基礎(chǔ)底座并適當參考了一些Knative的優(yōu)秀設(shè)計,在其架構(gòu)內(nèi)部主要由以下幾個核心部分組成:
2.3 流程設(shè)計在具體的CI/CD流程上,Nest又與傳統(tǒng)的模式有何區(qū)別呢?為了說明這個問題,我們先來看一看在Nest平臺上函數(shù)的整體生命周期怎樣的?具體有以下四個階段:構(gòu)建、版本、部署、伸縮。
就這四個階段來看,Nest與傳統(tǒng)的CI/CD流程本質(zhì)區(qū)別在于部署和伸縮:傳統(tǒng)的部署是感知機器的,一般是將代碼包發(fā)布到確定的機器上,但Serverless是要向用戶屏蔽機器的(在部署時,可能函數(shù)的實例數(shù)還是0);另外,傳統(tǒng)的模式一般是不具備動態(tài)擴縮容的,而Serverless則不同,Serverless平臺會根據(jù)業(yè)務(wù)的自身流量需要,進行動態(tài)擴縮容。后續(xù)章節(jié)會詳細講解彈性伸縮,因此這里我們只探討部署的設(shè)計。 部署的核心點在于如何向用戶屏蔽機器?對于這個問題,我們抽象了機器,提出了分組的概念,分組是由SET(單元化架構(gòu)的標識,機器上會帶有該標識)、泳道(測試環(huán)境隔離標識,機器上會帶有該標識)、區(qū)域(上海、北京等)三個信息組成。用戶部署只需在相應(yīng)的分組上進行操作,而不用涉及到具體機器。能夠做到這些的背后,是由Nest平臺幫助用戶管理了機器資源,每次部署會根據(jù)分組信息來實時初始化相應(yīng)的機器實例。 2.4 函數(shù)觸發(fā)函數(shù)的執(zhí)行是由事件觸發(fā)的。完成函數(shù)的觸發(fā),需要實現(xiàn)以下四個流程:
2.5 函數(shù)執(zhí)行函數(shù)不同于傳統(tǒng)的服務(wù),傳統(tǒng)的服務(wù)是個可執(zhí)行的程序,但函數(shù)不同,函數(shù)是代碼片段,自身是不能單獨執(zhí)行的。那流量觸發(fā)到函數(shù)實例后,函數(shù)是如何執(zhí)行的呢? 函數(shù)的執(zhí)行的首要問題是函數(shù)的運行環(huán)境:由于Nest平臺是基于Kubernetes實現(xiàn)的,因此函數(shù)一定是運行在Kubernetes的Pod(實例)內(nèi),Pod內(nèi)部是容器,容器的內(nèi)部是運行時,運行時是函數(shù)流量接收的入口,最終也是由運行時來觸發(fā)函數(shù)的執(zhí)行。一切看起來是那么的順利成章,但我們在落地時是還是遇到了一些困難,最主要的困難是讓開發(fā)同學可以在函數(shù)內(nèi)無縫的使用公司內(nèi)的組件,如OCTO(服務(wù)框架)、Celler(緩存系統(tǒng))、DB等。 在美團的技術(shù)體系中,由于多年的技術(shù)沉淀,很難在一個純粹的容器(沒有任何其他依賴)中運行公司的業(yè)務(wù)邏輯。因為公司的容器中沉淀了很多環(huán)境或服務(wù)治理等能力,如服務(wù)治理的Agent服務(wù)以及實例環(huán)境配置、網(wǎng)絡(luò)配置等。 因此,為了業(yè)務(wù)在函數(shù)內(nèi)無縫的使用公司內(nèi)的組件,我們復用公司的容器體系來降低業(yè)務(wù)編寫函數(shù)的成本。但復用公司的容器體系也沒那么簡單,因為在公司內(nèi)沒有人試過這條路,Nest是公司第一個基于原生Kubernetes建設(shè)的平臺,“第一個吃螃蟹的人”總會遇到一些坑。對于這些坑,我們只能在推進過程中“逢山開路,遇水搭橋”,遇到一個解決一個。總結(jié)下來,其中最核心的是在容器的啟動環(huán)節(jié)打通的CMDB等技術(shù)體系,讓運行函數(shù)的容器與開發(fā)同學平時申請的機器用起來沒有任何區(qū)別。 2.6 彈性伸縮彈性伸縮的核心問題主要有三個:什么時候伸縮,伸縮多少,伸縮的速度快不快?也就是伸縮時機、伸縮算法、伸縮速度的問題。
除了基本的擴縮容能力,我們還支持了伸縮到0,支持配置最大、最小實例數(shù)(最小實例即預(yù)留實例)。伸縮到0的具體實現(xiàn)是,我們在事件網(wǎng)關(guān)內(nèi)部增加了激活器模塊,當函數(shù)無實例時,會將函數(shù)的請求流量緩存在激活器內(nèi)部,然后立即通過流量的Metrics去驅(qū)動彈性伸縮組件進行擴容,等擴容的實例啟動完成后,激活器再將緩存的請求重試到擴容的實例上觸發(fā)函數(shù)執(zhí)行。 3 優(yōu)化核心技術(shù),保障業(yè)務(wù)穩(wěn)定性3.1 彈性伸縮優(yōu)化上面提到的伸縮時機、伸縮算法、伸縮速度這三要素都是理想情況下的模型,尤其是伸縮速度,當前技術(shù)根本做不到毫秒級別的擴縮容。因此,在線上實際場景中,彈性伸縮會存在一些不符合預(yù)期的情況,比如實例伸縮比較頻繁或者擴容來不及,導致服務(wù)不太穩(wěn)定的問題。
下圖展示的是線上彈性伸縮的真實案例(配置的最小實例數(shù)為4,單實例閾值100,閾值使用率0.7),其中上半部分是業(yè)務(wù)每秒的請求數(shù),下半部分是擴縮實例的決策圖,可以看到在成功率100%的情況下,業(yè)務(wù)完美應(yīng)對流量高峰。 3.2 冷啟動優(yōu)化冷啟動是指在函數(shù)調(diào)用鏈路中包含了資源調(diào)度、鏡像/代碼下載、啟動容器、運行時初始化、用戶代碼初始化等環(huán)節(jié)。當冷啟動完成后,函數(shù)實例就緒,后續(xù)請求就能直接被函數(shù)執(zhí)行。冷啟動在Serverless領(lǐng)域至關(guān)重要,它的耗時決定了彈性伸縮的速度。 所謂“天下武功,無堅不破,唯快不破”,這句話在Serverless領(lǐng)域也同樣受用。試想如果拉起一個實例足夠快,快到毫秒級別,那幾乎所有的函數(shù)實例都可以縮容到0,等有流量時,再擴容實例處理請求,這對于存在高低峰流量的業(yè)務(wù)將極大的節(jié)省機器資源成本。當然,理想很豐滿,現(xiàn)實很骨感。做到毫秒級別幾乎不可能。但只要冷啟動時間越來越短,成本自然就會越來越低,另外,極短的冷啟動時間對伸縮時函數(shù)的可用性以及穩(wěn)定性都有莫大的好處。 冷啟動優(yōu)化是個循序漸進的過程,我們對冷啟動優(yōu)化主要經(jīng)歷了三個階段:鏡像啟動優(yōu)化、資源池優(yōu)化、核心路徑優(yōu)化。
![]()
3.3 高可用保障說到高可用,對于一般的平臺,指的就是平臺自身的高可用,但Nest平臺有所不同,Nest的高可用還包含托管在Nest平臺上的函數(shù)。因此,Nest的高可用保障需要從平臺和業(yè)務(wù)函數(shù)兩個方面著手。 3.3.1 平臺高可用對平臺的高可用,Nest主要從架構(gòu)層、服務(wù)層、監(jiān)控運營層、業(yè)務(wù)視角層面都做了全面的保障。
![]()
3.3.2 業(yè)務(wù)高可用對于業(yè)務(wù)高可用,Nest主要從服務(wù)層、平臺層兩個層面做了相關(guān)的保障。
![]() 3.4 容器穩(wěn)定性優(yōu)化前文已提到,Serverless與傳統(tǒng)模式在CI/CD流程上是不同的,傳統(tǒng)模式都是事先準備好機器然后部署程序,而Serverless則是根據(jù)流量的高低峰實時彈性擴縮容實例。當新實例擴容出來后,會立即處理業(yè)務(wù)流量。這聽起來貌似沒什么毛病,但在富容器生態(tài)下是存在一些問題的:我們發(fā)現(xiàn)剛擴容的機器負載非常高,導致一些業(yè)務(wù)請求執(zhí)行失敗,影響業(yè)務(wù)可用性。 分析后發(fā)現(xiàn)主要是因為容器啟動后,運維工具會進行Agent升級、配置修改等操作,這些操作非常耗CPU。同在一個富容器中,自然就搶占了函數(shù)進程的資源,導致用戶進程不穩(wěn)定。另外,函數(shù)實例的資源配置一般比傳統(tǒng)服務(wù)的機器要小很多,這也加劇了該問題的嚴重性。基于此,我們參考業(yè)界,聯(lián)合容器設(shè)施團隊,落地了輕量級容器,將運維的所有Agent放到Sidecar容器中,而業(yè)務(wù)的進程單獨放到App容器中。采用這種容器的隔離機制,保障業(yè)務(wù)的穩(wěn)定性。同時,我們也推動了容器裁剪計劃,去掉一些不必要的Agent。 ![]() 4 完善生態(tài),落實收益Serverless是個系統(tǒng)工程,在技術(shù)上涉及到Kubernetes、容器、操作系統(tǒng)、JVM、運行時等各種技術(shù),在平臺能力上涉及到CI/CD各個流程的方方面面。 為了給用戶提供極致的開發(fā)體驗,我們?yōu)橛脩籼峁┝碎_發(fā)工具的支持,如CLI(Command Line Interface)、WebIDE等。為了解決現(xiàn)有上下游技術(shù)產(chǎn)品的交互的問題,我們與公司現(xiàn)有的技術(shù)生態(tài)做了融合打通,方便開發(fā)同學使用。為了方便下游的集成平臺對接,我們開放了平臺的API,實現(xiàn)Nest賦能各下游平臺。針對容器過重,系統(tǒng)開銷大,導致低頻業(yè)務(wù)函數(shù)自身資源利用率不高的問題,我們支持了函數(shù)合并部署,成倍提升資源利用率。 4.1 提供研發(fā)工具開發(fā)工具能夠降低平臺的使用成本,幫助開發(fā)同學快速的進行CI/CD流程。目前Nest提供了CLI工具,幫助開發(fā)同學快速完成創(chuàng)建應(yīng)用、本地構(gòu)建、本地測試、Debug、遠程發(fā)布等操作。Nest還提供了WebIDE,支持在線一站式完成代碼的修改、構(gòu)建、發(fā)布、測試。 4.2 融合技術(shù)生態(tài)僅支持這些研發(fā)工具還是不夠的,項目推廣使用后,我們很快就發(fā)現(xiàn)開發(fā)同學對平臺有了新的需求,如無法在Pipeline流水線、線下服務(wù)實例編排平臺上完成對函數(shù)的操作,這對我們項目的推廣也形成了一些阻礙。因此,我們?nèi)诤线@些公司的成熟技術(shù)生態(tài),打通了Pipeline流水線等平臺,融入到現(xiàn)有的上下游技術(shù)體系內(nèi),解決用戶的后顧之憂。 4.3 開放平臺能力有很多Nest的下游解決方案平臺,如SSR(Server Side Render)、服務(wù)編排平臺等,通過對接Nest的OpenAPI,實現(xiàn)了生產(chǎn)力的進一步解放。例如,不用讓開發(fā)同學自己去申請、管理和運維機器資源,就能夠讓用戶非常快速的實現(xiàn)一個SSR項目或者編排程序從0到1的創(chuàng)建、發(fā)布與托管。 Nest除了開放了平臺的API,還對用戶提供了自定義資源池的能力,擁有了該項能力,開發(fā)同學可以定制自己的資源池,定制自己的機器環(huán)境,甚至可以下沉一些通用的邏輯,實現(xiàn)冷啟動的進一步優(yōu)化。 4.4 支持合并部署合并部署指的是將多個函數(shù)部署在一個機器實例內(nèi)。合并部署的背景主要有兩個:
基于這兩個背景,我們考慮支持合并部署,將一些低頻的函數(shù)部署到同一個機器實例內(nèi),來提升預(yù)留實例中業(yè)務(wù)進程的資源利用率。 在具體實現(xiàn)上,我們參考Kubernetes的設(shè)計方案,設(shè)計了一套基于Sandbox的函數(shù)合并部署體系(每個Sandbox就是一個函數(shù)資源),將Pod類比成Kubernetes的Node資源,Sandbox類比成Kubernetes的Pod資源,Nest Sidecar類比成Kubelet。為了實現(xiàn)Sandbox特有的部署、調(diào)度等能力,我們還自定義了一些Kubernetes資源(如SandboxDeployment、SandboxReplicaSet、SandboxEndpoints等)來支持函數(shù)動態(tài)插拔到具體的Pod實例上。 ![]() 除此之外,在合并部署的形態(tài)下,函數(shù)之間的隔離性也是不可回避的問題。為了盡可能的解決函數(shù)(合并在同一個實例中)之間的互相干擾問題,在Runtime的實現(xiàn)上,我們針對Node.js和Java語言的特點采取了不同的策略:Node.js語言的函數(shù)使用不同的進程來實現(xiàn)隔離,而Java語言的函數(shù),我們采用類加載隔離。采用這種策略的主要原因是由于Java進程占用內(nèi)存空間相較于Node.js進程會大很多。 5 落地場景、收益目前Nest產(chǎn)品在美團前端Node.js領(lǐng)域非常受歡迎,也是落地最廣泛的技術(shù)棧。當前Nest產(chǎn)品在美團前端已實現(xiàn)了規(guī)?;涞?,幾乎涵蓋了所有業(yè)務(wù)線,接入了大量的B/C端的核心流量。 5.1 落地場景具體的落地前端場景有:BFF(Backend For Frontend)、CSR(Client Side Render)/SSR(Server Side Render)、后臺管理平臺、定時任務(wù)、數(shù)據(jù)處理等。
5.2 落地收益Serverless的收益是非常明顯的,尤其在前端領(lǐng)域,大量的業(yè)務(wù)接入已是最好的說明。具體收益,從以下兩個方面分別來看:
6 未來規(guī)劃
作者簡介
|
|
|