在前一部分我們提到使用Docker部署應(yīng)用同時(shí)采用12factors原則時(shí)攒钳,服務(wù)器對(duì)于容器來(lái)講,只依賴(lài)它的計(jì)算資源雷滋,那么如果有一個(gè)提供運(yùn)行時(shí)環(huán)境的黑盒功能不撑,讓我們可以直接部署代碼去運(yùn)行文兢,那敢情真的太棒了。其次焕檬,從資源利用率的角度來(lái)說(shuō)姆坚,在單塊架構(gòu)下,如果應(yīng)用的某個(gè)功能模塊需要水平擴(kuò)展实愚,那么整個(gè)應(yīng)用都得和它一起水平擴(kuò)展兼呵,這是一種資源的浪費(fèi)。微服務(wù)架構(gòu)各個(gè)功能模塊分解成單獨(dú)的微服務(wù)腊敲,每個(gè)服務(wù)獨(dú)立部署击喂,獨(dú)立擴(kuò)展,一定程度上降低了資源的浪費(fèi)碰辅。但是我們仔細(xì)看一下懂昂,假設(shè)一個(gè)用戶(hù)管理的微服務(wù),請(qǐng)求/login
的endpoint要比請(qǐng)求register
的endpoint多的多没宾,如果因?yàn)?code>/login的請(qǐng)求數(shù)量多需要擴(kuò)展凌彬,那么/register
得和它一起擴(kuò)展,那/login
不禁就要發(fā)問(wèn)榕吼,你/register
有何德何能去承受這些資源饿序。同樣的道理可以延伸到Restful endpoint的每個(gè)函數(shù)。
|Restful API | RPM |
|---|---|---|
| /login
| 1000 |
| /register
| 100 |
如果你的微服務(wù)粒度很小羹蚣,容器和PaaS一定程度上可以解決上面的問(wèn)題,如果我完全不想關(guān)心應(yīng)用服務(wù)器或者容器乱凿,怎么辦顽素。Here comes Serverless。
什么是Serverless
鑒于這是一個(gè)比較新鮮的事物徒蟆,喜歡定義的我司給出了Serverless的概念胁出,Serverless原本有兩個(gè)概念:
- 描述嚴(yán)重或完全依賴(lài)第三方應(yīng)用程序/服務(wù)(比如在云平臺(tái))管理服務(wù)器端邏輯和狀態(tài)的應(yīng)用程序。舉個(gè)例子段审,對(duì)于Single Page的應(yīng)用全蝶,我們一般都是使用前后端分離的架構(gòu),前端部署在AWS S3上面寺枉,你可以把S3看成一個(gè)文件存儲(chǔ)服務(wù)抑淫,對(duì)于前端應(yīng)用的部署,只是上傳新的文件而已姥闪,同時(shí)S3的服務(wù)器對(duì)我們來(lái)說(shuō)都是不可見(jiàn)的始苇,我們也不用擔(dān)心任何的維護(hù)壓力,也不用擔(dān)心如何擴(kuò)展(大多數(shù)情況下)筐喳。
- 開(kāi)發(fā)者實(shí)現(xiàn)的服務(wù)器端的應(yīng)用邏輯(微服務(wù)甚至粒度更小的服務(wù))以event trigger的方式運(yùn)行在無(wú)狀態(tài)的臨時(shí)的容器中催式,并且這些容器函喉、計(jì)算資源都是由第三方去管理。你也可以稱(chēng)之為FaaS(函數(shù)即服務(wù))荣月,AWS的Lambda服務(wù)就是目前最好的一個(gè)實(shí)現(xiàn)管呵。
所以可以看到核心的點(diǎn)在于
- 除了代碼外全托管,無(wú)任何服務(wù)器維護(hù)哺窄、擴(kuò)展的負(fù)擔(dān)
- 更小的實(shí)現(xiàn)粒度撇寞,微服務(wù)粒度可以小到一個(gè)Restful的endpoint,這里可以小到一個(gè)函數(shù)
Serverless的使用場(chǎng)景
Serverless的使用場(chǎng)景堂氯,目前能看到的有三種:
- 傳統(tǒng)的MVC架構(gòu)的應(yīng)用
- 事件驅(qū)動(dòng)的應(yīng)用
- 定時(shí)任務(wù)
其中我們目前主要在2蔑担、3的業(yè)務(wù)場(chǎng)景下使用Serverless也就是AWS Lambda。而對(duì)于1咽白,我們很早就進(jìn)行了嘗試啤握,但是更多的是純技術(shù)性質(zhì)、或者是hackday的spike晶框,并沒(méi)有在核心業(yè)務(wù)上去使用排抬。最主要的考慮是它耦合了太多的AWS服務(wù),失去了可移植性授段,而非不能做蹲蒲。
MVC架構(gòu)的應(yīng)用
假設(shè)我們有一個(gè)MVC架構(gòu)的單頁(yè)面應(yīng)用寵物店,那么當(dāng)瀏覽器請(qǐng)求服務(wù)器時(shí)侵贵,它會(huì)按照請(qǐng)求届搁,從數(shù)據(jù)庫(kù)讀取具體的數(shù)據(jù),render html窍育,然后返回給瀏覽器卡睦。
在這個(gè)例子中,我們需要維護(hù)寵物店應(yīng)用的服務(wù)器以及數(shù)據(jù)庫(kù)服務(wù)器漱抓,以及其對(duì)應(yīng)代碼表锻、schema等,還有如何部署乞娄、擴(kuò)展的問(wèn)題瞬逊。但是如果切換到Serverless架構(gòu)的話,在AWS上看起來(lái)會(huì)是這樣的:
- 對(duì)寵物店應(yīng)用做前后端分離仪或,前端部署在S3上面确镊,后端的邏輯拆分到函數(shù)級(jí)別,部署在Lambda上溶其,狀態(tài)和數(shù)據(jù)保存在Dynamodb中(Dynamodb是一個(gè)全托管的NoSQL數(shù)據(jù)庫(kù))骚腥,API-Gateway可以作為http proxy以及single sign-on驗(yàn)證入口
- 有請(qǐng)求到來(lái)時(shí),首先返回前端的靜態(tài)頁(yè)面瓶逃,然后根據(jù)其中請(qǐng)求的API束铭,由API-Gateway經(jīng)過(guò)驗(yàn)證(Oauth或者SAML)后trigger對(duì)應(yīng)的Lambda函數(shù)廓块,比如
/search
對(duì)應(yīng)的是Search Function
。在函數(shù)中它會(huì)訪問(wèn)Dynamodb契沫,獲取數(shù)據(jù)并返回带猴。
我們?cè)谶@里面用到的服務(wù)API-Gateway
、S3
懈万、Lambda
以及Dynamodb
都是全托管的服務(wù)拴清,他們基本上都可以通過(guò)Cloudformation
實(shí)現(xiàn)基礎(chǔ)設(shè)施即代碼以及部署(某些region不能使用cloudformation去管理API-Gateway,S3的部署不需要使用cloudformation会通,同樣Dynamodb
也不存在部署的問(wèn)題)口予,維護(hù)的壓力很小,水平擴(kuò)展由這些服務(wù)自己實(shí)現(xiàn)涕侈,比如Dynamodb沪停、以及Lambda,S3可以通過(guò)CDN去提升性能裳涛。同時(shí)我們可以發(fā)現(xiàn)木张,原本單塊架構(gòu)下部署在一起的三層結(jié)構(gòu),現(xiàn)在被徹底的分開(kāi)部署了端三。是不是非常的amazing(Amazoning)舷礼。
這樣的好處在于:
- 不需要為一個(gè)一直在線的服務(wù)器付費(fèi),以及維護(hù)郊闯、升級(jí)
- 按照請(qǐng)求的數(shù)量去付費(fèi)妻献,而不是服務(wù)器類(lèi)型、運(yùn)行的數(shù)量去付費(fèi)
- 完全不用去關(guān)心水平擴(kuò)展的問(wèn)題
- 部署非常簡(jiǎn)單虚婿,而且速度都比較快
使用這樣的架構(gòu)對(duì)于我們這種長(zhǎng)期堅(jiān)持微服務(wù)(粒度到一個(gè)Restful endpoint一個(gè)API)旋奢、前后端分離、持續(xù)交付等實(shí)踐的公司來(lái)講是比較容易的然痊,到現(xiàn)在還沒(méi)有在核心業(yè)務(wù)上使用主要是個(gè)政治問(wèn)題,也許過(guò)幾個(gè)月CTO覺(jué)得這個(gè)也不錯(cuò)屉符,可能立馬就采用了剧浸。
同時(shí),我們可以發(fā)現(xiàn)矗钟,傳統(tǒng)的只做部署或者服務(wù)器維護(hù)的運(yùn)維人員在這個(gè)架構(gòu)下已經(jīng)沒(méi)有什么位置了……唆香。所以也許DevOps的下一步就真的是NoOps了……。
事件驅(qū)動(dòng)的架構(gòu)(Event-Driven Architecture)
事件驅(qū)動(dòng)的架構(gòu)比如Serverless Architectures提到的這個(gè)應(yīng)用場(chǎng)景吨艇,廣告服務(wù)收集到信息發(fā)布到消息系統(tǒng)中牵舱,然后通知注冊(cè)到具體topic下的click processor
服務(wù)著觉,進(jìn)行處理,保存到數(shù)據(jù)庫(kù)校坑。
其中,
click processor
服務(wù)可能得運(yùn)行在單獨(dú)的服務(wù)器上谒撼,需要一直在線,同時(shí),如果系統(tǒng)中的消息/事件數(shù)量突然增多凸舵,對(duì)于click processor
的水平擴(kuò)展,只能是基于容器甚至于服務(wù)器級(jí)別失尖,雖然這是可以做到的啊奄,但是對(duì)于整個(gè)架構(gòu)的設(shè)計(jì)和使用的服務(wù)要求比較高,同時(shí)掀潮,資源浪費(fèi)也是不可避免的(比如菇夸,可以通過(guò)ASG去監(jiān)控click processor
的服務(wù)器,如果它的CPU使用率過(guò)高仪吧,就啟動(dòng)新的click processor
服務(wù)器去處理庄新,使用容器的方式同理)。使用無(wú)服務(wù)架構(gòu)的方式的話就變成下面這樣:
- 用戶(hù)點(diǎn)擊廣告服務(wù)時(shí)邑商,其請(qǐng)求通過(guò)http proxy寫(xiě)入到Kinesis中
- 在
click processor
的Lambda函數(shù)上可以綁定要處理的Kinesis Stream摄咆,同時(shí)指定每次處理的batch大小 - Lambda輪詢(xún)Kinesis Stream,有新的事件則立即啟動(dòng)
click processor
函數(shù)去處理人断。
這樣就不需要一個(gè)一直在線的服務(wù)去poll消息系統(tǒng)吭从,同樣如果需要擴(kuò)展的話可以通過(guò)在click processor
函數(shù)中去invoke其它的click process
函數(shù)來(lái)實(shí)現(xiàn)。
Lambda也可以用push模型恶迈,比如涩金,它可以將事件源設(shè)定為S3的某個(gè)bucket中文件上傳,當(dāng)有新的文件上傳暇仲,它會(huì)觸發(fā)該Lambda函數(shù)來(lái)處理新上傳的文件步做。除了S3之外,SNS奈附、Congnito等服務(wù)也可以以push的方式去觸發(fā)Lambda函數(shù)全度。
定時(shí)任務(wù)(Scheduled Job)
定時(shí)任務(wù)在企業(yè)里面是一個(gè)很常見(jiàn)的場(chǎng)景,我見(jiàn)過(guò)有一些把定時(shí)任務(wù)和應(yīng)用放在一個(gè)服務(wù)器上跑的斥滤,恕我直言這種方式不可取将鸵,首先它無(wú)法自己scale,處理的不好還會(huì)導(dǎo)致服務(wù)器的性能問(wèn)題佑颇,同時(shí)你在部署的時(shí)候可能還得考慮它顶掉。比如每周一企業(yè)都會(huì)給用戶(hù)發(fā)一個(gè)簡(jiǎn)報(bào)郵件,如果你用定時(shí)任務(wù)來(lái)跑挑胸,可能得像下面這樣:
- Cron服務(wù)器上郵件發(fā)送cron定時(shí)運(yùn)行痒筒,首先從數(shù)據(jù)庫(kù)獲取要發(fā)送的郵箱的list
- 該cron將郵箱發(fā)送給郵件發(fā)送服務(wù)集群,集群前面的loadbalancer可以將請(qǐng)求平均到每個(gè)服務(wù)器上
這種方式除了資源的浪費(fèi)還有水平擴(kuò)展成本高之外,還有就是得維護(hù)額外的cron服務(wù)器以及代碼簿透。以前的慣例是把所有的cron任務(wù)放在專(zhuān)門(mén)的一臺(tái)/多臺(tái)機(jī)器上運(yùn)行移袍,維護(hù)的成本很高,而且服務(wù)器出問(wèn)題萎战,很難定位到原因咐容,即便你把它們的運(yùn)行日志都上傳到中心化服務(wù)器中。
如果使用Lambda的話就簡(jiǎn)單了很多:
- 首先
email function
將CloudWatchCloudWatch Events - Schedule
作為事件源蚂维,并選定執(zhí)行該函數(shù)的時(shí)間戳粒,可以用cron的標(biāo)準(zhǔn)格式去定義 - 其次在
email function
運(yùn)行時(shí),它會(huì)從數(shù)據(jù)庫(kù)中獲取列表虫啥,從水平擴(kuò)展的角度考慮蔚约,你可以把列表分為n份,在這個(gè)函數(shù)中去invoke多個(gè)(成千上萬(wàn)個(gè))真正的郵件發(fā)送函數(shù)去處理涂籽。實(shí)際上要做的事情很簡(jiǎn)單苹祟,像下面這樣:
var aws = require('aws-sdk');
var lambda = new aws.Lambda({
region: 'ap-southeast-2'
});
#fake the mail list
var mailers = {mailer1: "someone@gmail.com", mailer2: "somebody@qq.com"};
console.log("call email sending function");
lambda.invoke({
FunctionName: 'arn:aws:lambda:ap-southeast-2:AWS_ACCOUNT_ID:function:email-sending-function',
Payload: JSON.stringify(mailers)
},
function(error, data) {
if (error) {
context.done('error', error);
console.log("invoke failed");
}
if(data.Payload) {
context.succeed(data.Payload);
console.log("invoke success");
}
});
這樣前面所說(shuō)的痛點(diǎn)基本都解決了,沒(méi)記錯(cuò)的話我們目前使用Lambda最多的場(chǎng)景就是定時(shí)任務(wù)评雌,比如ETL树枫。
關(guān)于AWS Lambda
目前Lambda只支持三種語(yǔ)言的運(yùn)行時(shí)環(huán)境:
- java
- nodejs
- python
其中nodejs和python可以直接部署代碼,而java是部署編譯后的jar包景东。雖然看上去支持的語(yǔ)言不多砂轻,但實(shí)際上如Ruby語(yǔ)言,可以通過(guò)JRuby去開(kāi)發(fā)部署斤吐,同理還有基于JVM的scala等語(yǔ)言搔涝。
Lambda函數(shù)運(yùn)行的容器最多只能保留5分鐘,所以一定要保證函數(shù)在5分鐘內(nèi)執(zhí)行完畢和措。同時(shí)不要在上保存狀態(tài)庄呈,狀態(tài)可以保存在S3或者數(shù)據(jù)庫(kù)中。
Lambda的部署方式有三種
- 如果沒(méi)有很多外部依賴(lài)派阱,直接上傳代碼即可
- 如果有外部依賴(lài)诬留,可以打包成zip文件上傳
- 將打包后的文件上傳到s3上,通過(guò)cloudformation去做部署
同時(shí)贫母,Lambda支持的Alias
功能可以很容易讓我們實(shí)現(xiàn)藍(lán)綠部署故响。
Lambda的監(jiān)控比較粗粒度,AWS沒(méi)有提供它在容器中運(yùn)行的狀況監(jiān)控颁独,雖然這點(diǎn)沒(méi)有太大的必要,你也可以通過(guò)比較hack的方式伪冰,比如用newrelic來(lái)實(shí)現(xiàn)對(duì)它的監(jiān)控誓酒。
出錯(cuò)報(bào)警也是需要注意的地方,可以通過(guò)創(chuàng)建cloudwatch alarm監(jiān)控錯(cuò)誤數(shù)量,出問(wèn)題時(shí)報(bào)警靠柑,當(dāng)然對(duì)應(yīng)的你代碼得在錯(cuò)誤時(shí)拋出異常寨辩。
Lambda的日志是傳到cloudwatch中的,想要傳到日志服務(wù)器中得需要些額外的步驟歼冰。還有一種方式就是將日志作為時(shí)間寫(xiě)入到Kinesis Stream中靡狞,以統(tǒng)一的方式寫(xiě)入日志服務(wù)器,同時(shí)可以在檢測(cè)到很多錯(cuò)誤返回時(shí)報(bào)警隔嫡。
目前Lambda最大的問(wèn)題在于測(cè)試甸怕,對(duì)業(yè)務(wù)邏輯做單元測(cè)試自然是沒(méi)有問(wèn)題,但是集成的話會(huì)稍微比較麻煩腮恩,每個(gè)函數(shù)都得有一套對(duì)應(yīng)的測(cè)試環(huán)境梢杭,比如關(guān)聯(lián)的數(shù)據(jù)庫(kù)表、S3 bucket秸滴、Kinesis Stream等武契,維護(hù)的成本比較高。調(diào)試也只能部署上去后在線調(diào)試荡含,想想都覺(jué)得刺激咒唆。有了Lambda,線上改代碼不是夢(mèng)释液。突然間就想起來(lái)P神以前和我結(jié)對(duì)調(diào)試問(wèn)題的時(shí)候說(shuō)的一句話全释,"whenever I debug, I debug in production"。現(xiàn)在想想還是P神高均澳。當(dāng)然這些都是trade off
恨溜,看技術(shù)層面的選擇了。
在有一次的AWS Summit上找前,有一位臺(tái)灣的朋友提到說(shuō)Lambda背后實(shí)現(xiàn)的原理主要有兩點(diǎn):
- 啟動(dòng)艸快的定制化容器,并且可能會(huì)有部署你代碼的standby的容器
- 高速網(wǎng)絡(luò)
實(shí)際上我們回頭看它的使用場(chǎng)景糟袁,用現(xiàn)有的工具,比如Mesos等躺盛,我們?cè)诒镜鼐涂梢詫?shí)現(xiàn)event driven或者定時(shí)任務(wù)的場(chǎng)景项戴,因?yàn)樗鼈儗?duì)于時(shí)效性的要求不高。但是對(duì)于實(shí)時(shí)的業(yè)務(wù)場(chǎng)景槽惫,需要立刻返回的就不好做了周叮。不過(guò)也許等Unikernel成熟了,這點(diǎn)就不再是問(wèn)題了界斜。
總結(jié)
不說(shuō)了仿耽,得趕緊把我創(chuàng)建的lambda函數(shù)刪掉,免的下個(gè)月賬單??各薇。