Peter Wayner 范范
在AWS Lambda、谷歌云函數(shù)(Cloud Functions)和微軟Azure Functions的幫助下,一點點業(yè)務邏輯就能夠完成許多工作
如果你曾有過因為服務器故障在凌晨3點鐘被叫醒過的經(jīng)歷,就會明白“無服務器”這個熱門術(shù)詞的吸引力之所在。機器需要花上數(shù)小時、數(shù)天有時甚至是數(shù)周時間進行配置,同時它們還需要經(jīng)常更新以解決BUG和安全漏洞。這些更新通常會帶來麻煩,因為新的更新會與其他的更新不兼容,這種情況似乎永無休止。
由于運行服務器而產(chǎn)生的這種令人頭疼的死循環(huán)也是許多大型云公司選擇“無服務器”架構(gòu)的原因之一。他們知道,老板長期以來一直聽到的借口都是“服務器這樣啦,服務器那樣啦”。老板肯定在考慮我們是否能夠擺脫服務器。
這是一個很好的營銷口號,唯一的問題是它在嚴格意義上并不正確。這些應用沒有了服務器就如同飯店沒有了廚房。如果你想點東西都在菜單上,并且你喜歡廚師準備它們的方式,那么坐在餐廳里是很棒的。但是如果你想吃一道與眾不同的菜,或是吃到與眾不同的口味,那么你最好有自己的廚房。
亞馬遜、谷歌和微軟這三家巨頭目前正在激烈地爭奪主機應用的未來,希望將它們寫入自己的無服務器API并通過自己的自動化層進行管理。如果平臺達到了我們的要求,同時新的模式普及起來,那么對于創(chuàng)建數(shù)十億美元的獨角獸級網(wǎng)絡應用來說,它們無疑是最簡單快捷的方式。我們只需要寫少量的關(guān)鍵邏輯,平臺會處理所有的細節(jié)。
無服務器函數(shù)正在成為能夠?qū)⑺性乒δ苓B接起來的膠合或腳本語言。曾經(jīng)相對獨立的映射或AI工具如今也通過由事件驅(qū)動的無服務器函數(shù)連接到了一起。如今我們的許多工作都可以通過云端各個部分的響應和事件觸發(fā)來完成。如果我們希望嘗試機器學習并通過它們分析數(shù)據(jù),那么最快的方式是創(chuàng)建一個無服務器應用,然后將事件發(fā)送至云端的機器學習部分。
這其中的關(guān)鍵是將所有東西進一步細化,讓它們更容易共享云端上的資源。過去,所有的人都在瘋狂地利用運行在自己的虛擬機上的Ubuntu服務器創(chuàng)建新實例。所有的人都在使用相同的操作系統(tǒng),而這個操作系統(tǒng)會在被劃分為多個虛擬Ubuntu服務器的真實服務器上大量復制。無服務器操作避免了這種復制,從而大幅降低了云計算的成本,尤其是對于那些只是零星運行,甚至從未讓那些位于機房中的老服務器發(fā)生擁堵的工作。
當然所有的便利背后都有隱藏的成本。如果你想將代碼遷移到另一個站點,你可能會為需要重寫堆棧中的絕大部分而感到怵頭。API則不同,雖然像JavaScript等流行的語言都進行了標準化,但是它們幾乎已經(jīng)變?yōu)榱藢@摹_@使得用戶極有可能遇到廠商鎖定的情況。
為了介紹無服務器選項的吸引力,我花費了一些時間創(chuàng)建了一些函數(shù)并將它們放在堆棧上。我沒有編寫太多的代碼,不過重點就在于此。我將更多的時間花在了點擊按鍵和填寫web表單以配置所有的東西。你是否還記得我們使用XML和JSON配置所有東西的時候?現(xiàn)在我只需要填寫一個web表單,云會為我們完成剩下的工作。盡管如此,我們?nèi)匀灰癯绦騿T那樣考慮問題,搞清楚云端上發(fā)生了什么以及哪些不在自己的控制之中。
AWS Lambda
AWS Lambda正在成長為亞馬遜整個云的shell腳本層。作為一個基礎系統(tǒng),其可讓嵌入的函數(shù)對由亞馬遜云基礎設施的任意部分生成的事件做出響應。如果新的文件上傳至S3,我們可以讓其觸發(fā)一個函數(shù),讓函數(shù)利用新文件做有趣的事情。如果某個視頻正在使用亞馬遜Elastic Transcoder(彈性轉(zhuǎn)碼器)進行轉(zhuǎn)碼,那么我們可以讓Lambda函數(shù)等待至轉(zhuǎn)碼完成后再依次被觸發(fā)。這些函數(shù)能夠觸發(fā)其他的Lambda操作,或是僅向某人發(fā)送更新。
你可以用JavaScript (Node.js)、Python、Java、C#和Go等編寫Lambda函數(shù)。鑒于上述這些語言可以嵌入許多其他語言,這使得運行Haskell、Lisp甚至是 C++等其他代碼成為了可能。
由于亞馬遜為配置和優(yōu)化提供了許多選項,因此我們會覺得編寫Lambda函數(shù)會比預期的要復雜。盡管在技術(shù)上我們確實可以只編寫幾行代碼就能夠完成許多事情,但是我認為自己必須要分配更多的時間去配置代碼的運行方式。許多工作是通過瀏覽器填寫表單完成的,而不再是通過鍵入文本文件完成。有時我會覺得我們只是將文本編輯器換成了一個基于瀏覽器的表單。不過,這也是為了保留所有的靈活性所付出的代價,亞馬遜希望將這些靈活性也提供給Lambda用戶。
在額外的步驟中,有一些是由于亞馬遜向用戶提供了更多選擇并期待更多的人初次嘗試函數(shù)編寫而導致的。一旦我在谷歌或微軟上編寫完成了一個函數(shù),那么我能夠?qū)⒆约旱臑g覽器指出正確的URL并立即測試它們。亞馬遜會讓我點擊配置API網(wǎng)關(guān)并正確設置防火墻。
最后,所有的這些點擊都添加了一個輔助層,這比使用文本文件要更容易上手。在我創(chuàng)建了一個函數(shù)后,瀏覽器會發(fā)出 “該函數(shù)包含了一個外部庫”的警告。若是回到純Node的時代,這正是我希望知道的東西,我會通過谷歌搜索這一錯誤信息提示來找尋答案,期間我會雙手交叉,期待著答案能夠出現(xiàn)在搜索結(jié)果頁面上?,F(xiàn)在云會為我們提供幫助。
如果無服務器意味著可以將我們從服務器管理中解放出來,那么亞馬遜還有許多與AWS Lambda一樣的 “無服務器”選項。與此同時,亞馬遜還有EC2 Auto Scaling、AWS Fargate、AWS Elastic Beanstalk等彈性工具,其中EC2 Auto Scaling和AWS Fargate可啟動和關(guān)閉服務器,AWS Elastic Beanstalk可處理上傳的代碼,將其部署至web服務器上并進行負載平衡和擴展。當然,通過這些自動化工具,我們?nèi)匀豢梢詣?chuàng)建服務器鏡像。
AWS Step Functions是一個非常有幫助的解決方案。作為無代碼流程圖工具,它們可為軟件架構(gòu)師稱之為工作流的模型創(chuàng)建狀態(tài)機。部分問題在于所有的無服務器函數(shù)都是無狀態(tài)的,在執(zhí)行一些最基礎的業(yè)務邏輯時它們才工作,然而當通過檢查單或流程圖瀏覽客戶端時這就會帶來麻煩。你需要不斷地去數(shù)據(jù)庫重新加載關(guān)于客戶端的信息。Step Functions則將Lambda函數(shù)和狀態(tài)連接在了一起。
谷歌云函數(shù)與Firebase
如果你的目標是擺脫配置服務器的工作,那么谷歌云有許多服務可以將你從提供根密碼、甚至是使用命令行的工作中解放出來。
從2008年的谷歌App Engine開始,谷歌一直在慢慢地添加不同的“無服務器”選項,并且提供各種消息傳遞和數(shù)據(jù)透明組合。谷歌云Pub/Sub可以為我們屏蔽消息隊列,因此我們只需為數(shù)據(jù)生產(chǎn)者和消費者編寫代碼即可。谷歌云函數(shù)為許多重要的產(chǎn)品提供了由事件驅(qū)動的計算,包括選框工具和API。與此同時,谷歌的Firebase可讓我們將JavaScript代碼混合到向客戶端傳遞數(shù)據(jù)的數(shù)據(jù)存儲層。
在這些當中,F(xiàn)irebase是最讓我感興趣的。有人認為數(shù)據(jù)庫是原始的無服務器應用,其抽象掉了數(shù)據(jù)結(jié)構(gòu)和磁盤存儲,然后通過TCP/IP端口傳遞所有的信息。通過添加JavaScript代碼和通知完成所有工作(你可能會希望通過服務器架構(gòu)完成,如認證),F(xiàn)irebase將抽象化運用到了極致。在技術(shù)上,它們只是一個數(shù)據(jù)庫,但是它們可以處理許多業(yè)務邏輯和堆棧通知。我們真的可以擺脫一些客戶端的HTML、CSS、JavaScript和Firebase。
你可能傾向于將Firebase的JavaScript層稱之為“存儲過程”,就像甲骨文的做法一樣,但是這將導致錯失一個良機。Firebase代碼是由JavaScript編寫的,因此它們可以在本地版本的Node.js上運行。你可以在這個層嵌入許多業(yè)務邏輯,因為Node已經(jīng)帶有處理這一工作流的庫。此外,你還可以享受到運行在客戶端、服務器和數(shù)據(jù)庫上的同構(gòu)代碼的便利。
吸引我的另一個原因是Firebase中的同步層,它們會在網(wǎng)絡中從數(shù)據(jù)庫同步項目的副本。通過這個功能,我們可以將自己的客戶端應用設置成另一個數(shù)據(jù)庫節(jié)點并訂閱針對相關(guān)數(shù)據(jù)的所有變化(或僅僅是相關(guān)的數(shù)據(jù))。如果某個地方的數(shù)據(jù)發(fā)生了變化,它們會修改所有的地方。我們不再需要發(fā)布通知,只需要為Firebase編寫一個信息,因為Firebase會在需要的地方復制它們。
我們不需要將關(guān)注點僅僅放在Firebase上。更為基礎的谷歌云函數(shù)則是一個更為簡單的解決方案,其可將定制的代碼嵌入至整個谷歌云中。此時,對于編寫將在預先配置的Node環(huán)境中運行的Node.js代碼來說,云函數(shù)一個很好選擇。盡管谷歌云平臺支持Java、C#、Go、Python和PHP等多種語言,但云函數(shù)僅限于支持JavaScript 和Node。目前已經(jīng)有跡象顯示它們將支持其他的語言,如果這些真的變成了現(xiàn)實,那么我對此一點也不感到意外。
谷歌云函數(shù)在谷歌云中融合程度還沒有達到AWS Lambda在AWS中的融合程度,至少目前是這樣。當我嘗試著創(chuàng)建一個函數(shù)與谷歌Docs交互,我發(fā)現(xiàn)自己可能需要REST API并用Apps Script編寫代碼。換句話說,谷歌Docs有著自己的REST API,在“無服務器”這個詞被造出來之前的很長一段時間里它一直在嘗試這一概念。
值得關(guān)注的是谷歌App Engine一直保持著良好的發(fā)展勢頭。在一開始,它們僅提供了啟用Python應用以滿足網(wǎng)站訪問者的需求。經(jīng)過幾年的發(fā)展,如今它們已經(jīng)可以處理許多不同的語言運行環(huán)境。一旦將代碼捆綁到可執(zhí)行文件中,在用戶發(fā)送請求時,App Engine將進入啟動足夠節(jié)點的程序以處理流量,并進行相應的擴展和收縮。
盡管如此,目前仍然有一些障礙需要克服。與云函數(shù)一樣,我們的代碼必須要以一種相對無狀態(tài)的方式編寫,同時還必須要在限定的時間內(nèi)完成每個請求。App Engine不會扔掉所有的輔助工具,或是忘掉這些請求之間的東西。雖然App Engine是無服務器革命中的一個重要組成部分,但是對于那些依舊使用老方法通過Python、PHP、Java、C#或Go創(chuàng)建堆棧的人來說,App Engine仍然是最好用的。
微軟Azure Functions
當然,微軟也正在和他的競爭對手一樣努力的讓用戶也能夠通過Azure云享受無服務器的便利。他們創(chuàng)建了自己的基礎函數(shù)Azure Functions以及設計精巧的工具,這些工具甚至可以抵得上半個程序員。
微軟最大的優(yōu)勢可能在于其Office應用。Office應用以前為桌面執(zhí)行文件,如今它們正逐步遷移至云端。實際上,微軟云營收超過亞馬遜在很大程度上得益于其將Office的營收也計入到云營收當中。
Azure Functions文檔中列出的一個最佳范例為我們展示了當用戶將一個電子表格存儲在OneDrive后云函數(shù)是如何被觸發(fā)的。云端上的小精靈們會突然活躍起來幫助處理該電子表格。對于鐘愛Excel 電子表格(或其他Office文檔)的IT商店支持團隊來說,這無疑是天賜的利器。他們可以通過編寫Azure Functions做幾乎所有的事情。雖然我們經(jīng)常會認為HTML和web是云唯一的界面,然而它們沒有理由不能通過像微軟Word或Excel這樣的文檔格式完成。
Azure的Logic Apps吸引我的原因是其中的一個工具可以讓我們只填寫表格而無疑擔心語義和語法的問題。雖然我們?nèi)匀恍枰癯绦騿T那樣思考問題并對抽象和數(shù)據(jù)做出明智的決策,但是我們可以確信自己編寫的“代碼”沒有填寫的表格那么多。
與亞馬遜的Step Functions一樣,Logic Apps有意編碼“工作流”。得益于一些狀態(tài)的可獲得性,這里的“工作流”要比普通意義上的“函數(shù)”稍微復雜些。我們?nèi)匀灰帉戇壿?,以類似流程圖的方式將不同的函數(shù)和連接器連接來,但是我們不再需要使用正式的計算機語言拼寫它們。
Logic Apps最大的優(yōu)勢是預建了“連接器”,這使得它們能夠深入到更多的微軟和第三方應用中。我們可以高效地向Logic Apps推送或從Logic Apps中拉出數(shù)據(jù),就像Salesforce、Twitter和Office 365那樣。這些連接對于公司的IT員工來說極具價值,他們?nèi)缃窨梢酝ㄟ^編寫Logic Apps將外部工具連接起來,就像他們過去創(chuàng)建shell腳本一樣。
Azure另一個有趣之處是Azure Cosmos DB。該數(shù)據(jù)庫同時既是NoSQL數(shù)據(jù)庫也是SQL數(shù)據(jù)庫。微軟復制了針對Cassandra和MongoDB的API,因此我們可以在不重寫Cassandra或MongoDB代碼的情況下推入和推出信息。如果我們想寫SQL,那么我們也可以做到。Cosmos DB更為直接,它們?yōu)樗袞|西都創(chuàng)建了索引以更快的運行。如果我們有許多SQL和NoSQL代碼,同時我們希望它們能夠共同工作,那么Azure Cosmos DB會創(chuàng)建一個中央結(jié)點?;蛟S我們還希望在未來為其他不同的解決方案留下一扇敞開的門。
三個無服務器云之間的比較
哪種無服務器平臺最適合我們呢?雖然編寫基礎函數(shù)的工作量在這三個獨立的平臺上都相差無幾,但是還是存在著區(qū)別。最明顯的區(qū)別可能是可以使用的語言,因為每個平臺在完成了對Node.js和JavaScript的支持之后都有自己偏愛的語言。我們對在微軟Azure上能夠使用C#語言一點也不意外,意外的是它們是唯一支持F#和TypeScript語言的平臺。亞馬遜支持的語言是Java、C#和Python。盡管谷歌的App Engine支持許多語言,但是目前谷歌的基礎函數(shù)僅限于JavaScript。
在比較這些無服務器云時,最困難的工作是對價格和速度進行比較,因為它們更多地被隱藏在后臺。當我啟用VM實例后,我會有種自己在揮金如土的感覺,因為它們的定價是按小時收費的。如今,這些提供商正在將香腸切的越來越薄,我們可以以不到一美元的價格獲得幾十萬次的函數(shù)調(diào)用。我們將會像《王牌大賤諜》中的邪惡博士那樣將“百萬”這個詞掛在嘴邊。
當然,這些表面上的低價很快就會讓我們的頭腦喪失理智和預算意識,這就如同我們?nèi)ヒ粋€使用不同貨幣的陌生國家度假一樣。我們不久就會進行另一個數(shù)百萬次的數(shù)據(jù)庫調(diào)用。這很像我們在墨西哥的療養(yǎng)觀光勝地坎庫恩的酒吧喝酒一樣,我們無法迅速地計算出它們的真正價格。
當云向我們銷售原始的虛擬機時,我們可能會根據(jù)RAM 和CPU進行評估,但是在無服務器平臺,我們根本沒有什么能夠用于評估的依據(jù)。
值得關(guān)注的是,無服務器模式將會迫使我們將數(shù)據(jù)存放在本地的云數(shù)據(jù)庫上,因為我們不可能真正被允許通過代碼保持任意狀態(tài)。我們不得不信任這些后端。函數(shù)必須在沒有任何緩存或配置的情況下運行,因為總是有許多版本在不斷的出現(xiàn)和消失。數(shù)據(jù)庫膠水代碼會填滿我們的代碼,這一切就如同美劇《怪奇物語》中“倒置”世界里的那些藤蔓一樣。
對比它們的成本唯一可行的辦法是在所有的平臺上都創(chuàng)建一個應用,不過這是一件非常具有挑戰(zhàn)性的工作。由于它們都可運行Node.js,因此在三個平臺之間移動一些代碼是可行的。即便如此,我們還是會遇到一些不得不隱忍的差異(例如,我們在微軟和谷歌的平臺上可直接處理HTTP請求,但是在AWS上就需要通過API網(wǎng)關(guān))。
好消息是我們不需要如此偏執(zhí)。在我的體驗中,許多基礎應用幾乎用不上什么資源,我們可以在三個平臺為兜里沒錢的開發(fā)者提供的免費層上干許多事情。無服務器模式確實可以為我們在總開支方面省下一大筆資金。除非我們屬于全天候滿負荷運行服務器的類型并且有免費的空調(diào),否則采用無服務器解決方案可以為我們節(jié)約很多費用。無論價格是每百萬次調(diào)用1美元還是1.5美元,它們將能夠為我們節(jié)省很多錢,這一點是無可爭辯的。
這里還有一個更深層次的問題。如果我們對這三個云平臺都不滿意,那么我們就遇到了很大麻煩。將代碼從這些平臺中抽出來讓它們運行在商用服務器上,就像我們在Docker容器那樣幾乎是不可能的。如果幸運的話,我們可以復制相同的原始架構(gòu)和基礎的JavaScript函數(shù),但是在此之后,我們需要重寫所有地方的數(shù)據(jù)庫膠水代碼。因為這三家公司都有自己專利的數(shù)據(jù)存儲層。
還有一點不明確的地方是如果出現(xiàn)了運行故障會發(fā)生什么情況。在運行自己的服務器時,如果出現(xiàn)了故障,老板會狠掐我們的脖子。當無服務器平臺出現(xiàn)了故障,會發(fā)生什么并不明確。谷歌的頁面上有這樣一句警告“這是Google云函數(shù)的測試版。 此API可能會以不兼容的方式進行更改,并且不受任何SLA或棄用策略的約束。”
與剛剛涉及這一領域時相比,亞馬遜的服務條款已經(jīng)變得好多了。但是這些條款中仍然包含了需要我們牢牢記住的警告,“如果您的內(nèi)容運行未超過三(3)個月,我們可能會在30天內(nèi)通知您并且在沒有任何責任的情況下刪除您上傳到AWS Lambda的任何內(nèi)容。”如果我們想一直使用它們,那么就要確保自己的代碼處于運行狀態(tài)。這種警告是無疑正確的(因為我知道自己的老的Lambda函數(shù)將永遠不會再使用),但是這表明我們將放棄一些控制權(quán)。
微軟則為Azure服務提供了服務級協(xié)議,承諾將依據(jù)服務信用對宕機進行經(jīng)濟補償。那么當我們的函數(shù)不工作了,這些承諾仍然適用嗎?或許吧,前提是我們不要嘗試該服務的一些測試功能。如果我們將要創(chuàng)建一些對任務起著關(guān)鍵作用的東西,而非為小朋友創(chuàng)建一個聊天室,那么花些時間研究這些條款是值得的。
在許多情況下,我們所做的僅僅限于對亞馬遜、谷歌和微軟的服務和功能進行比較,而忽略了函數(shù)層。如果我們支持的用戶對Office應用情有獨鐘,那么通過OneDrive上的Office文件觸發(fā)Azure Functions的能力將具有很大的吸引力。谷歌Firebase使得通過函數(shù)為web應用提供通知和認證等支持服務變得更加容易。AWS Lambda則引入了許多亞馬遜服務,但是這似乎也限制了它們的發(fā)展前景。
將這些云和函數(shù)進行混搭在技術(shù)上是可行的,因為它們在HTTP API調(diào)用方面都使用相同的PUT和GET語言。無法在一個應用中使用匯聚了三個云服務優(yōu)勢的微服務在說法上是不成立的。但是嚴重的延時會讓我們放棄嘗試,因為數(shù)據(jù)包離開了本地云,轉(zhuǎn)而在開放而廣闊的互聯(lián)網(wǎng)上傳輸。此外,由于在語法分析和結(jié)構(gòu)上還存在稍許差異,這也使得我們只會選擇一家公司的平臺。
只選擇一個云在安全方面可能更具意義。你真的喜歡谷歌Maps并希望將它們應用到自己的項目中嗎?那么即便內(nèi)心深處更希望通過微軟的Azure Functions使用F#,你可能還是會選擇使用谷歌云函數(shù)。亞馬遜的語音識別、谷歌的圖像分析API以及其他不同的服務和機器學習API也面臨同樣的道理。這些函數(shù)并不重要,重要的是它們與哪些實際的事情有著密切的聯(lián)系。