1. 程式人生 > 程式設計 >系統架構-Serverless(baas & faas)無伺服器計算

系統架構-Serverless(baas & faas)無伺服器計算

自從2014年AWS推出Lambda服務後,Serverless一詞越來越熱,已經成為一種新型的軟體設計架構,即Serverless Architecture。作為一種原生於公共雲的架構,Serverless有什麼優缺點?是否能應用於傳統企業程式?是否適合私有云場景?是否像很多文章宣稱的一樣,會成為未來改變雲端計算的中堅力量?作為一名雲端計算行業的老兵,作者想在此文中分享一些自己的觀點。

什麼是Serverless

Serverless並不神祕,用一個簡單的例子就可講明。我們設計了一個AI應用,可以識別出圖片中人物的人種,我們把它作為一種SaaS服務架設在公共雲上提供給客戶使用,其典型的後端架構設計如下:

      在該架構中,我們購買的雲主機上運行了Tomcat Web Server,用於承載Java編寫的AI應用。使用者通過API上傳圖片。受限於雲主機的本地儲存空間,為了滿足大量客戶同時上傳圖片,AI應用實現了一個儲存閘道器將圖片匯入公共雲的物件儲存。圖片匯入完成後,AI應用從物件儲存讀入圖片進行識別,並將結果存入公共雲的資料庫中(例如RDS),使用者使用API查詢結果。       AI應用上線一段時間後受到了使用者的歡迎,越來越多的公司開始使用該服務。根據統計資料,大多數公司在上午9點~11點、下午2點~5點集中上傳圖片,為了滿足該時間段的突發訪問量,我們設定了公共雲的      Auto-Scaling策略,在訪問增加時動態建立更多的雲主機來響應客戶。AI應用的架構演化成:

  • 在這個架構中,我們需要做如下事情:
  1. 管理雲主機。我們要關心CPU數量、記憶體大小、IP地址等等系統級的配置。同時還要關心雲主機的作業系統,為部署AI應用擬定策略。作業系統和Tomcat的安全補丁也不能忽視,否則競爭對手可能僱傭黑客來攻擊我們的系統。
  2. 配置公共雲的Auto-Scaling的策略,應對高峰期突發訪問量。
  3. 使用公共雲的物件儲存和資料庫。
  4. 編寫AI應用。       要完成這些工作,我們既要開發AI應用,又要營運支撐業務(例如管理雲主機生命週期、管理作業系統)。這是當前架構的現實:為20%的核心業務營運80%的支撐業務。
  • 下面用Serverless架構改寫AI應用:

  • 多個執行AI應用程式碼的程式被啟動,併發處理使用者上傳的圖片。

  • 在Serverless架構的AI應用中,我們只需要做兩件事情:

  1. 使用公共雲的物件儲存和資料庫。

  2. 用公共雲的Serverless框架編寫AI應用。

      與之前的架構相比,我們不再營運雲主機、作業系統、Tomcat,同時也不需要配置Auto-Scaling Group,公共雲的Serverless框架會在每個圖片上傳完成後啟動一個程式執行AI應用,自動實現水平擴充套件。我們終於只需要關心核心業務了,用Serverless框架支援的語言(例如AWS Lambda就支援Java,Python和JVM系語言)編寫AI應用,一切非核心業務都外包給了公共雲營運商。

      我們的Serverless AI應用用到了兩種技術。首先使用了公共雲提供的物件儲存和資料庫服務,統稱為BaaS(Backend as a Service,後端即服務)。其次用了Lambda框架,稱為FaaS(Functions as a Service,函式即服務)。

      使用BaaS和FaaS是Serverless應用的基本特徵,符合這兩個基本特徵的應用可稱為Serverless應用。

是BaaS,不是PaaS

      AI應用用到了物件儲存和資料庫,將來或許還會用到訊息佇列。直觀感覺是在使用PaaS,為什麼還要造一個新詞BaaS?技術圈有太多令人混淆的術語了。

      BaaS並非PaaS,它們的區別在於:PaaS需要參與應用的生命週期管理,BaaS則僅僅提供應用依賴的第三方服務。典型的PaaS平臺需要提供手段讓開發者部署和配置應用,例如自動將應用部署到Tomcat容器中,並管理應用的生命週期。BaaS不包含這些內容,BaaS只以API的方式提供應用依賴的後端服務,例如資料庫和物件儲存。BaaS可以是公共雲服務商提供的,也可以是第三方廠商提供的,例如Facebook收購的Parse就是著名的MBaaS提供商(Mobile Backend as a Service)。從功能上講,BaaS可以看作PaaS的一個子集,即提供第三方依賴元件的部分。

FaaS是Serverless的核心

      AI應用最初是一個典型Java程式,它可能使用Spring這樣的技術,因為我們需要一個框架確保程式的各個元件能夠被正確載入,需要MVC來保證REST API被正確的Controller處理。AI應用部署在Tomcat容器中,執行在雲主機上,7 x 24小時執行,我們提供不間斷的服務。在夜裡12點到早晨8點,幾乎沒有使用者使用,但我們還得讓它待在那裡,防止深夜偶爾使用的使用者得到一個503錯誤而誤會AI服務不穩定。我們為購買的雲主機付錢,儘管一半的時間它的CPU使用率幾乎為0,但沒有公共雲是按CPU使用率計費的,不工作的時間也得付錢。我們必須關心Auto-Scaling Group的配置,如何準確的配置Auto-Scaling策略是一個技術活,需要長期的經驗積累,在早期我們不得不多部署一些空閒的雲主機以保證服務不會因Auto-Scaling的配置不當而擁塞。

      用Serverless架構改寫了AI應用後,這些痛苦就通通消失了。Spring框架和Tomcat去掉了,用Lambda的Java SDK,只需要實現一個Function Handler處理圖片上傳完成這個事件,這跟寫一個Callback一樣簡單。在Function Handler中呼叫圖片識別的相關邏輯,然後呼叫資料庫的REST API儲存結果。也不用構建MVC,不用配置Tomcat的XML檔案,我們將儲存閘道器這個功能完全去除掉了,因為使用者可以直接上傳圖片到物件儲存。

      AI應用不用7 x 24小時運行了,沒有使用者上傳圖片時它只是一份編譯好的程式碼。當使用者圖片上傳完成時,FaaS會為AI應用啟動一個新的程式執行程式碼。該程式在程式碼執行完成後自動銷燬。我們只需為程式碼執行的這幾十秒鐘付錢,節省了很多開支。

      最後我們無需操心Auto-Scaling的問題,FaaS會在需要的時候自動擴充套件。

  • 這些就是FaaS的核心,從上面的例子裡面可以歸納出它的特點:
  1. FaaS執行的是後端程式碼而不是整個後端程式。例如AI應用僅僅包含處理圖片上傳完成這個事件的邏輯,並不是一個完整的後端程式,而是一段後端程式碼。

  2. 程式碼通過事件觸發。由於不再有一個長期執行的程式等待或輪詢使用者請求,程式碼只能通過特殊的事件觸發。這些事件由FaaS框架定義,例如上傳檔案到物件儲存、訊息佇列收到一條新的訊息、API Gateway收到一個新的API請求等。

  3. 程式碼的生命週期很短。例如我們的AI應用,從收到事件後Function Handler被呼叫開始,到呼叫返回結束,不會有常駐記憶體的程式執行。此外公共雲提供商還會限制程式碼執行的時間,超出時間後執行程式碼的程式會被強行銷燬。例如AWS的Lambda可執行的最長時間為5分鐘。

  4. 程式碼必須做到徹底無狀態,兩次呼叫間不能共享記憶體狀態。我們的AI應用最早使用了一個全域性變數統計處理的圖片數,每處理完一張圖片該計數器就加一。使用FaaS後我們不能再用任何全域性變數或記憶體資料結構(例如Hashmap)在呼叫間共享資料,因為程式碼執行在獨立的程式中,無法訪問對方的記憶體地址空間。於是我們對程式碼進行了改造,將全域性計數器放到了公共雲的Redis服務中,這為程式碼增加了額外的複雜性。

  5. 水平擴充套件不再是需要擔心的問題,FaaS會為每個事件和請求執行一份新的程式碼。

  6. 應用的部署方式從上傳、配置整個程式變成上傳一份打包程式碼的檔案(例如Jar檔案或一個Zip檔案)。

Serverless為我們帶來了什麼

      對比傳統架構,用Serverless架構改寫的AI應用具有顯著的優勢。我們不再運維任何雲主機和作業系統,甚至不再運維Tomcat這樣的Web容器,只需要專注於程式碼本身,所有配置、應用生命週期管理的工作都由FaaS框架負責。公共雲的出現讓我們從物理硬體管理中解放出來,Serverless架構讓我們進一步從作業系統管理中解放出來,第一次真正專注於核心業務。

      業務也變得更加敏捷了。我們只需要編寫核心業務相關的程式碼,例如AI應用中影象識別的部分。無需編寫任何載入、部署、配置應用的程式碼,例如不再需要配置systemd在系統啟動時載入應用。

      水平擴充套件也不是問題。正如前面反覆提及的,FaaS框架會為每一個事件、每一個API請求都啟動一份新的程式執行程式碼。這跟傳統應用的執行緒池方式類似,每個請求都在一個單獨的執行緒中執行,區別在於執行緒之間共享同一記憶體地址空間,FaaS的程式間不共享任何記憶體。與執行緒池有最大執行緒數限制類似,FaaS框架通常也限制了最大程式數,例如AWS Lambda在一個Region預設能執行的最大併發呼叫是600,也就是說我們的AI應用最多能在600個程式中同時執行。

      最後,也是最重要的,Serverless架構為我們節省了大量開支。我們只需為AI應用執行的時間付錢,無需為應用等待請求的時間付錢。水平擴充套件的粒度從原來的雲主機細化到程式,節省了額外的開支,不用再購買閒置的雲主機來抵消Auto-Scaling的配置不精確帶來的影響。業務的敏捷性提高也降低了營運成本,我們不再需要精通作業系統配置和管理的營運人員,不僅節省了人力成本,也節省了應用從開發到上線的時間。

Serverless不是銀子彈,是後端小程式的未來

      serverless架構在某些應用場景的優勢如此明顯,有些支持者已經開始炒作它會成為顛覆性的雲端計算新架構了。技術圈向來如此,一些人總在孜孜不倦的尋找包治百病的靈藥,和解決一切問題的銀子彈。“All design is about tradeoff”,Serverless也不是銀子彈,它有獨特的優勢,而這些優勢也帶來了不可避免的侷限。

      為每個事件/請求啟動一個全新的程式執行程式碼是FaaS的核心,程式的啟動延時是Serverless面臨的第一個問題。取決於編寫應用的語言,啟動延時可以是10毫秒(如簡單的Python應用),也可以是1分鐘(複雜的Java應用)。這樣的延時對於realtime的程式是難以接受的。目前Serverless應用通常執行在公共雲的多租戶環境中,啟動延時還受系統負載影響,很難保證應用在規定時間內被執行。公共雲提供商目前沒有對Serverless提供相應的SLA保證,筆者寫這篇文章的時候,AWS Lambda還沒有相關的SLA條款。

      Serverless無法用於高併發應用,為每個請求啟動一個程式開銷太高。例如雙十一支付寶高峰期每秒處理的交易數為8.59萬筆,如果使用Serverless架構,意味著我們的系統內每秒有8.59萬個程式被建立又被銷燬,這是難以負擔的開銷。

      Serverless應用無法常駐記憶體,執行的時間是受限的。如果你的應用無法在數分鐘內完成的工作,那Serverless不是你的選擇,例如AWS Lambda給予程式的最長執行時間是5分鐘,超時後程式將被強制終止。這對程式設計提出了挑戰,例如我們的AI應用必須優化到在5分鐘內完成複雜影象的識別。我們也不能編寫執行長時間IO操作的應用,例如對物件儲存中1T的資料進行復雜編碼。

      Serverless呼叫之間不能共享狀態讓編寫複雜程式變得極度困難。無狀態是互連網應用追求的目標,例如滿足“12要素”的應用。但Serverless將無狀態進行的更加徹底,在不同的呼叫之間無法共享記憶體狀態,例如使用hashmap。我們的AI應用中統計已處理圖片總數的全域性計數器在傳統架構中只是一個全域性變數,但在Serverless架構中它變成儲存在記憶體資料庫(Redis)中的一條記錄,更新成本、保證原子性等因素讓我們的編碼變得數倍複雜。對於大多雲原生的網際網路應用來說,這種徹底的無狀態架構是一個巨大的挑戰,而對於動輒有幾十萬、上百萬行程式碼的、充滿了狀態的企業應用來說,Serverless的無狀態改造幾乎是一個無法完成的任務。

      熟練的微服務的架構師,對將業務拆分成一個個單獨的服務非常熟悉,也有不少的經典書籍(例如《Building Microservices: Designing Fine-Grained Systems》)指導我們如何做。但即使是他們,在面對Serverless架構時也會感到頭痛,如何將業務拆分成成百上千個執行在獨立程式、執行時間受限的函式是巨大的挑戰。而是否需要如此細粒度的拆分是需要回答的第一個問題。有些問題或許變成無解難題又或成本極高,例如分散式資料庫事務。

      上面都是Serverless架構的一些固有侷限,它們源於Serverless架構的特點,很難隨著時間的推移、技術的完善而解決。除此之外,作為一個新的技術,Serverless還面臨著整合測試困難、Vendor Lock-in、除錯監控困難、版本控制等諸多不足,每一項都會成為採用Serverless架構的阻礙。

      由於這些侷限性,Serverless架構不會成為複雜應用的架構首選,相反,它應該是後端小程式的未來。

      雲端的應用有大量的小程式場景,例如識別一張圖片、對一段音訊/視訊進行編解碼、對IOT裝置的請求返回一小段資料、將客戶提交的工單通過郵件通知客服人員等等。這些基於事件觸發的小程式在傳統架構中實現起來是相對複雜的,你往往需要為20%的核心業務運營80%的支撐業務。Serverless完美的解決了這些問題,它可以成為複雜應用的一種補充架構。我們可以將無狀態的、事件觸發的業務拆分成Serverless應用,讓整個架構變得更加的簡潔和高效。

      Serverless也在不斷演變,例如AWS最近引入的Step Functions就嘗試解決呼叫間共享狀態的問題,其效果有待觀察。

Serverless不是傳統的PaaS

      Serverless跟PaaS之間的界線比較模糊,很多人認為Serverless是PaaS的一種,筆者也傾向於認為Serverless是特殊的PaaS形態。

      Serverless由BaaS和FaaS兩部分構成,BaaS負責提供業務的依賴服務,FaaS負責業務的部署和生命週期管理,從這個意義上來看,Serverless的角色跟PaaS一樣。與傳統PaaS的區別在於,傳統PaaS是以程式為粒度管理應用的生命週期,而Serverless是以函式粒度管理應用生命週期。傳統PaaS中的應用為常駐記憶體的程式,而Serverless應用執行完即銷燬。此外,使用傳統PaaS,使用者仍需要關心水平擴充套件,例如如何配置Auto-Scaling Group,但Serverless沒有這個問題,水平擴充套件是架構天然自帶的功能。

Serverless和微服務

      Serverless和微服務沒有直接關係,但兩者有相似之處,例如都需要做業務拆分、強調無狀態、具有敏捷特性等。Serverless在很多方面比微服務粒度更細,要求也更嚴格。例如微服務以服務為邊界拆分業務,Serverless以函式為邊界拆分業務;微服務可以有跨呼叫的記憶體狀態共享,Serverless要求呼叫徹底無狀態。此外,Serverless依賴BaaS提供第三方依賴,而微服務可以自由選擇第三方依賴來源,例如使用本地搭建的傳統中介軟體棧(如本地MySql和訊息匯流排)。

Serverless和容器

      Serverless和容器是蘋果和桔子的比較,不在一個平面上。Serverless是一種軟體設計架構,容器是軟體架構的承載者。雖然沒有公開資料,但我們可以推測類似於AWS Lambda這樣的Serverless框架使用了某種程度的容器技術,否者難以實現語言無關和毫秒級的啟動。儘管已經有一些開源專案使用Docker實現Serverless中的FaaS部分,筆者不認為AWS Lambda這樣的公共Serverless框架直接使用了Docker,一定是一種更為輕量級、體積更小的容器技術,我們或許可以將它稱為Nano-Container。

Serverless對私有云有意義嗎?

      對於私有云來說,現在將業務遷往Serverless架構還為時過早。首先Serverless是從公共雲中演化出來的新型架構,適用於執行在公共雲上的小程式。而私有云更多承載的是老而笨重的傳統業務,難以用Serverless架構改造。其次Serverless依賴BaaS,在私有云中搭建和運維BaaS成本都不低,使用公共BaaS服務又受限於網路頻寬和延時,容易導致系統不穩定。

      隨著企業應用的進一步雲化、開源Serverless框架的成熟,私有云的Devops場景也可以採用Serverless作CI/CD,例如目前Jenkins承擔的大部分工作都可以用Serverless替代,如用FaaS框架對應Jenkins本身,上傳的程式碼對應Jenkins Job中的Bash指令碼,將原來的Jenkins API觸發Job改為觸發FaaS中的程式碼。

總結

      Serverless作為一種全新的架構,是雲端計算髮展演化的必然結果。追求更細粒度的計費單元,更加專注於核心業務、將支撐業務外包給基礎設施提供商是雲端計算的趨勢。Serverless架構的特點,讓編寫事件觸發的後端小程式變得更加容易。同時它也有自身內在的侷限性,並不適合複雜的應用架構。從目前的情況看,部分採用Serverless的混合架構對公共雲應用是個不錯的選擇,私有應用採用Serverless還為時過早。雲端計算技術正在飛速發展,未來還有無限可能。