前言 云計(jì)算時(shí)代出現(xiàn)了大量XaaS形式的概念,從IaaS,、PaaS,、SaaS到容器云引領(lǐng)的CaaS,再到火熱的微服務(wù)架構(gòu),以及現(xiàn)在越來越多被談起的Serverless和FaaS,我們正在經(jīng)歷一個(gè)技術(shù)飛速變革的時(shí)代。 一,、什么是Faas 云計(jì)算時(shí)代出現(xiàn)了大量XaaS形式的概念,,從IaaS (Infrastructure as a Service)、PaaS (Platform as a Service),、SaaS (Software as a Service) 到容器云引領(lǐng)的CaaS (Containers as a Service),,再到火熱的微服務(wù)架構(gòu),它們都在試著將各種軟,、硬件資源等抽象為一種服務(wù)提供給開發(fā)者使用,,讓他們不再擔(dān)心基礎(chǔ)設(shè)施、資源需求,、中間件等等,,在減輕心智負(fù)擔(dān)的同時(shí)更好地專注于業(yè)務(wù)。FaaS是Functions as a Service的簡(jiǎn)稱,,它往往和無服務(wù)架構(gòu)(Serverless Architecture)一同被提起,。 Serverless的概念剛剛出現(xiàn)在HackerNews時(shí)并不為大眾所接受。后來隨著微服務(wù)和事件驅(qū)動(dòng)架構(gòu)的發(fā)展才慢慢引起關(guān)注,。Serverless并不是說沒有服務(wù)器參與,,它通過將復(fù)雜的服務(wù)器架構(gòu)透明化,使開發(fā)者專注于“要做什么”,,從而強(qiáng)調(diào)了減少開發(fā)者對(duì)服務(wù)器等計(jì)算資源的關(guān)注,、工作粒度從服務(wù)器切換到任務(wù)的思想。2006年第一個(gè)支持“隨用隨付”的代碼執(zhí)行平臺(tái)Zimki問世,。2014年亞馬遜AWS推出了Lambda成為最主要的無服務(wù)架構(gòu)的代表,。接著Google、IBM和Microsoft也紛紛推出了各自支持Serverless的平臺(tái),。 微服務(wù)架構(gòu)近年來是一個(gè)非?;鸨脑掝},大大小小的公司都開始逐步分拆原來的單體應(yīng)用,,試著轉(zhuǎn)換到由各個(gè)模塊服務(wù)組合成大型的復(fù)雜應(yīng)用,。Serverless可以看作是比微服務(wù)架構(gòu)更細(xì)粒度的架構(gòu)模式,即FaaS,。Lambda也是FaaS的典型代表,,它允許用戶僅僅上傳代碼而無需提供和管理服務(wù)器,由它負(fù)責(zé)代碼的執(zhí)行、高可用擴(kuò)展,,支持從別的AWS服務(wù)或其他Web應(yīng)用直接調(diào)用等,。以電子商務(wù)應(yīng)用為例,微服務(wù)中可以將瀏覽商品,、添加購(gòu)物車,、下單、支付,、查看物流等拆分為解耦的微服務(wù)。在FaaS里,,它可以拆分到用戶的所有CRUD操作代碼,。當(dāng)發(fā)生“下單”事件時(shí),將觸發(fā)相應(yīng)的Functions,,交由Lambda執(zhí)行,。人們?cè)谠絹碓蕉嗟膱?chǎng)景里將Serverless和FaaS等同起來。 假設(shè)現(xiàn)在有下面的JavaScript代碼: module.exports = function(context, callback) { callback(200, 'Hello, world! 顯然它是一個(gè)函數(shù),,通過FaaS的方式,,我們可以通過訪問一個(gè)URL的方式調(diào)用這個(gè)函數(shù)。 $ curl -XGET localhost:8080 Hello, world! FaaS擁有下面的特點(diǎn): 1,、FaaS里的應(yīng)用邏輯單元都可以看作是一個(gè)函數(shù),,開發(fā)人員只關(guān)注如何實(shí)現(xiàn)這些邏輯,而不用提前考慮性能優(yōu)化,,讓工作聚焦在這個(gè)函數(shù)里,,而非應(yīng)用整體。 2,、FaaS是無狀態(tài)的,,天生滿足云原生(Cloud Native App)應(yīng)用該滿足的12因子(12 Factors)中對(duì)狀態(tài)的要求。無狀態(tài)意味著本地內(nèi)存,、磁盤里的數(shù)據(jù)無法被后續(xù)的操作所使用,。大部分的狀態(tài)需要依賴于外部存儲(chǔ),比如數(shù)據(jù)庫(kù),、網(wǎng)絡(luò)存儲(chǔ)等,。 3、FaaS的函數(shù)應(yīng)當(dāng)可以快速啟動(dòng)執(zhí)行,,并擁有短暫的生命周期,。函數(shù)在有限的時(shí)間里啟動(dòng)并處理任務(wù),并在返回執(zhí)行結(jié)果后終止,。如果它執(zhí)行時(shí)間超過了某個(gè)閾值,,也應(yīng)該被終止。 4、FaaS函數(shù)啟動(dòng)延時(shí)受很多因素的干擾,。以AWS Lambda為例,,如果采用了JS或Python實(shí)現(xiàn)了函數(shù),它的啟動(dòng)時(shí)間一般不會(huì)超過10~100毫秒,。但如果是實(shí)現(xiàn)在JVM上的函數(shù),,當(dāng)遇到突發(fā)的大流量或者調(diào)用間隔過長(zhǎng)的情況,啟動(dòng)時(shí)間會(huì)顯著變長(zhǎng),。 5,、FaaS需要借助于API Gateway將請(qǐng)求的路由和對(duì)應(yīng)的處理函數(shù)進(jìn)行映射,并將響應(yīng)結(jié)果代理返回給調(diào)用方,。 比如對(duì)于一個(gè)簡(jiǎn)單的3層Web應(yīng)用,,在這里后端系統(tǒng)實(shí)現(xiàn)了大部分業(yè)務(wù)邏輯:認(rèn)證、搜索,、事務(wù)等,,它的架構(gòu)如下: 如果采用Serverless架構(gòu),將認(rèn)證,、數(shù)據(jù)庫(kù)等采用第三方的服務(wù),,從原來的單體后端里分拆出來(可能需要在原來的客戶端里加入一些業(yè)務(wù)邏輯)。對(duì)于大部分的任務(wù),,通過函數(shù)的形式進(jìn)行執(zhí)行,,而不再使用一直在線的服務(wù)器進(jìn)行支持,如此一來它的架構(gòu)看起來就清晰多了: 這樣的拆分除了讓各個(gè)組件(函數(shù))間充分解耦,,每個(gè)都很好地實(shí)現(xiàn)了單一職責(zé)原則(SRP, Single Responsibility Principle)以外,,它的好處還有: 減少開支 減輕負(fù)擔(dān) 不再需要重復(fù)造輪子,需要什么功能直接集成調(diào)用即可,,也無需考慮整體的性能,,只專注于業(yè)務(wù)代碼的實(shí)現(xiàn)。 易于擴(kuò)展 云上提供了自動(dòng)的彈性擴(kuò)展,,用了多少計(jì)算資源,,就購(gòu)買多少,完全按需付費(fèi),。 簡(jiǎn)化管理 環(huán)保計(jì)算 在Martin Flower的專欄文章Serverless Architectures曾這樣定義Serverless架構(gòu): 'Serverless architectures refer to applications that significantly depend on third-party services(AKA Backend as a Service or 'BaaS') or on custom code that is run ephmemeral containers (Function as a Service or 'FaaS')' 正如前面提到了FaaS的每個(gè)函數(shù)都擁有快速啟動(dòng)和短暫生命周期的特性,讓容器作為任務(wù)函數(shù)運(yùn)行的基本單位,,是不是非常適合FaaS的場(chǎng)景,?同樣,作為最熱門的容器編排工具的Kubernetes又該怎樣應(yīng)對(duì)FaaS呢? 二,、Kubernetes 與 FaaS Fission是一款基于Kubernetes的FaaS框架,。通過Fission可以輕而易舉地將函數(shù)發(fā)布成HTTP服務(wù)。它通過讀取用戶的源代碼,,抽象出容器鏡像并執(zhí)行,。同時(shí)它幫助減輕了Kubernetes的學(xué)習(xí)負(fù)擔(dān),開發(fā)者無需了解太多K8s也可以搭建出實(shí)用的服務(wù),。Fission目前主要支持NodeJS和Python,預(yù)支持C# .NET,,對(duì)Golang的支持也在進(jìn)行中,。Fission可以與HTTP路由、Kubernetes Events和其他的事件觸發(fā)器結(jié)合,,所有這些函數(shù)都只有在運(yùn)行的時(shí)候才會(huì)消耗CPU和內(nèi)存,。 Kubernetes提供了強(qiáng)大的彈性編排系統(tǒng),并且擁有易于理解的后端API和不斷發(fā)展壯大的社區(qū),。所以Fission將容器編排功能交給了K8s,,讓自己專注于FaaS的特性。 對(duì)于FaaS來說,,它最重要的兩個(gè)特性是將函數(shù)轉(zhuǎn)換為服務(wù),,同時(shí)管理服務(wù)的生命周期。有很多辦法可以實(shí)現(xiàn)這兩個(gè)特性,,但需要考慮一些問題,,比如“框架運(yùn)行在源碼級(jí)?還是Docker鏡像,?”,,“第一次運(yùn)行的負(fù)載多少能接受”,不同的選擇會(huì)影響到平臺(tái)的擴(kuò)展性,、易用性,、資源使用以及性能等問題。 為了使Fission足夠易用,,它選擇在源碼級(jí)工作,。用戶不再參與鏡像構(gòu)建,、推倉(cāng)庫(kù)、鏡像認(rèn)證,、鏡像版本等過程,。但源碼級(jí)的接口不允許用戶打包二進(jìn)制依賴。Fission采用的方式是在鏡像內(nèi)部放置動(dòng)態(tài)的函數(shù)加載工具,,讓用戶可以在源碼層操作,,同時(shí)在需要的時(shí)候可以定制鏡像。這些鏡像在Fission里叫做“環(huán)境鏡像”,,它包含了特定語言的運(yùn)行時(shí),、一組常用的依賴和函數(shù)的動(dòng)態(tài)加載工具。如果這些依賴已經(jīng)足夠滿足需求,,就直接使用這個(gè)鏡像,,否則的話需要重新導(dǎo)入依賴并構(gòu)建鏡像。環(huán)境鏡像是Fission中唯一與語言相關(guān)的部分,??梢园阉醋鍪强蚣芾锲溆嗖糠值慕y(tǒng)一接口。所以Fission可以更加容易擴(kuò)展(這看起來就像VFS一樣),。 FaaS優(yōu)化了函數(shù)運(yùn)行時(shí)的資源使用,,它的目標(biāo)是在運(yùn)行的時(shí)候才消費(fèi)資源。但在冷啟動(dòng)的時(shí)候可能會(huì)有些資源使用過載,,比如對(duì)于用戶登錄的過程,,無論多等幾秒都是不可接受的。為了改變這個(gè)問題,,F(xiàn)ission維持了一個(gè)面向任何環(huán)境容器池,。當(dāng)有函數(shù)進(jìn)來時(shí),F(xiàn)ission無需啟動(dòng)新容器,,直接從池里取一個(gè),,將函數(shù)拷貝到容器里,執(zhí)行動(dòng)態(tài)加載,,并將請(qǐng)求路由到對(duì)應(yīng)的實(shí)例,。 除了安裝在本地的Fission主程序外,F(xiàn)ission-bundle設(shè)計(jì)為一組微服務(wù)構(gòu)成:
在Kubernetes上,,這些組件都以Deployment的方式運(yùn)行,,并對(duì)外暴露Service,。除了這三個(gè)Fission特有的組件外,還用了Etcd作為資源和映射的存儲(chǔ),,同樣也以Deployment的方式啟動(dòng),。Controller支持Fission的API,其他的組件監(jiān)視controller的更新,。Router暴露為K8s里的LoadBalancer或NodePort類型的服務(wù)(這取決于K8s集群放在哪里),。 目前,F(xiàn)ission將一個(gè)函數(shù)映射為一個(gè)容器,,對(duì)于自動(dòng)擴(kuò)展為多個(gè)實(shí)例的特性在后續(xù)版本里,。以及重用函數(shù)Pods來支持多個(gè)函數(shù)也在計(jì)劃中(在這種情況下隔離不是必須的)。Fission文檔簡(jiǎn)單介紹了它的工作原理: '當(dāng)Router收到外部請(qǐng)求,,它先去緩存Cache里查看是否在請(qǐng)求一個(gè)已經(jīng)存在的服務(wù),。如果沒有,要訪問請(qǐng)求映射的服務(wù)函數(shù),,需要向Pool Manager申請(qǐng)一個(gè)容器實(shí)例執(zhí)行函數(shù),。Pool Manager擁有一個(gè)空閑Pod池。它選擇一個(gè)Pod,,并把函數(shù)加載到里面(通過向容器里的Sidecar發(fā)送請(qǐng)求實(shí)現(xiàn)),,并且把Pod的地址返回給Router。Router將外部請(qǐng)求代理轉(zhuǎn)發(fā)到該P(yáng)od,,并將響應(yīng)結(jié)果返回。Pod會(huì)被緩存起來以應(yīng)對(duì)后續(xù)的請(qǐng)求,。如果空閑了幾分鐘,,它就會(huì)被殺死' 對(duì)于較小的REST API來說,F(xiàn)ission是個(gè)很好的選擇,,通過實(shí)現(xiàn)webhooks,,可以為Slack或其他服務(wù)編寫chatbots。 Fission同時(shí)還支持根據(jù)Kubernetes的Watch機(jī)制來出發(fā)函數(shù)的執(zhí)行,。例如你可以設(shè)置一個(gè)函數(shù)來watch某個(gè)命名空間下所有滿足某個(gè)標(biāo)簽的pod,,這個(gè)函數(shù)將得到序列化的對(duì)象和這個(gè)上下文的Watch Event類型(added/removed/updated)。又如通過設(shè)置事件處理函數(shù)可以將它應(yīng)用于簡(jiǎn)單的監(jiān)控,,指定當(dāng)任意一個(gè)服務(wù)添加到集群時(shí)向Slack發(fā)送一條消息,。當(dāng)然也有更復(fù)雜的應(yīng)用,例如編寫一個(gè)watching Kubernetes第三方資源(Third Party Resource)的自定義controller,。 在Fission的官網(wǎng)上有個(gè)入門的使用示例: $ cat hello.jsmodule.exports = function(context, callback) { 如果是第一次運(yùn)行,,需要先準(zhǔn)備NodeJS的運(yùn)行環(huán)境: # Add the stock NodeJS env to your Fission deployment$ fission env create --name nodejs --image fission/node-env 通過閱讀Fission的源碼,可以很清晰地看到它的執(zhí)行過程: 1. fission env create --name nodejs --image fission/node-env 2. fission function create --name hello --env nodejs --code hello.js 同樣,,由fission主程序執(zhí)行命令function和子命令create,,通過--name參數(shù)指定函數(shù)名為hello,,--env參數(shù)確定環(huán)境,--code參數(shù)確定要執(zhí)行的函數(shù)代碼,。通過POST向/v1/functions發(fā)出請(qǐng)求,,攜帶函數(shù)信息的JSON。controller拿到JSON后進(jìn)行函數(shù)資源的存儲(chǔ),。首先將拿到UUID,,然后寫到文件名為該UUID的文件里。接著向ETCD的API發(fā)送HTTP請(qǐng)求,,在file/name路徑下有序存放UUID,。最后類似上面env命令,將UUID和序列化后的JSON數(shù)據(jù)寫到ETCD里,。 3. fission route create --method GET --url /hello --function hello fission通過參數(shù)--method指定請(qǐng)求所需方法為GET,,--url指定API路由為hello,--function指定對(duì)應(yīng)執(zhí)行的函數(shù)為hello,。通過POST向/v1/triggers/http發(fā)出請(qǐng)求,,將路由和函數(shù)的映射關(guān)系信息發(fā)送到controller。controller會(huì)在已有的trigger列表里進(jìn)行重名檢查,,如果不重復(fù),,才會(huì)獲取UUID并將序列化后的JSON數(shù)據(jù)寫到etcd里。 前面的都是由本地的fission程序完成的,。我們已經(jīng)預(yù)先創(chuàng)建了fission-bundle的Deployment和Service,。它創(chuàng)建了名為fission的命名空間,并在里面啟動(dòng)4個(gè)Deployment,,分別是controller, router, poolMgr, etcd,,并創(chuàng)建NodePort類型的Service: controller和router,分別監(jiān)聽端口31313和31314,。同時(shí)創(chuàng)建另一個(gè)名為fission-function的命名空間用來運(yùn)行執(zhí)行函數(shù)的Pod. router使用Cache維護(hù)著一份function到service的映射,,同時(shí)還有trigger集合(有個(gè)goroutine通過controller保持對(duì)這個(gè)trigger集合的更新),在啟動(dòng)時(shí)按照添加trigger里的url和針對(duì)對(duì)應(yīng)函數(shù)的handler初始化路由,。 4. curl http://$FISSION_ROUTER/hello 當(dāng)執(zhí)行該curl時(shí),,請(qǐng)求發(fā)送至router容器。收到請(qǐng)求后會(huì)轉(zhuǎn)發(fā)到兩個(gè)對(duì)應(yīng)的handler,。一個(gè)是用戶定義的面向外部的,,一個(gè)是內(nèi)部的。實(shí)際上它們執(zhí)行的是同一個(gè)handler,。任何handler都會(huì)先根據(jù)funtion名去Cache里查找對(duì)應(yīng)的service名,。如果沒有命中,將通過poolmgr為函數(shù)創(chuàng)建新的Service,,并把記錄添加到Cache,。然后生成一個(gè)反向代理,,接收外部請(qǐng)求,然后轉(zhuǎn)發(fā)至Kubernetes Service,。 Poolmgr在創(chuàng)建新的service時(shí),,會(huì)根據(jù)env創(chuàng)建Pod pool(初始大小為3個(gè)副本的deployment),然后從中隨機(jī)選擇一個(gè)Ready的Pod,。接著為此建立對(duì)應(yīng)的Service,。 Fission是一個(gè)開源項(xiàng)目,由Platform 9和社區(qū)進(jìn)行開發(fā),。社區(qū)正在努力讓Kubernetes上的FaaS更加易用和輕松集成,。在未來幾個(gè)月將添加單元測(cè)試、與Git集成,、函數(shù)監(jiān)控和日志聚合等特性,,同時(shí)也會(huì)跟其他的Events進(jìn)行集成,對(duì)了,,還有為更多的語言創(chuàng)建環(huán)境,。在今年1月份,F(xiàn)ission發(fā)布了alpha版,。 三,、后記 容器技術(shù)的出現(xiàn)改變了軟件交付的思維,微服務(wù)和Serverless雖然沒有減少軟件生命周期中的環(huán)節(jié),,但確實(shí)改變了下游軟件部署和維護(hù)的理念,,提高了軟件開發(fā)人員的效率。FaaS是未來的一種可能的走勢(shì),,但一定不會(huì)是最終的未來,。總有一天FaaS又會(huì)被其他技術(shù)所代替,。生活在這個(gè)信息爆炸、技術(shù)飛速更迭的時(shí)代很煩惱也很幸福,。這就是我們所在的時(shí)代,,我們正在親身經(jīng)歷著未來。 |
|