深入理解 Serverless 計算的併發度

背景

2019 年 Berkeley 預測 Serverless 將取代 Serverful 計算[1],成爲雲計算的計算新範式。Serverless 爲應用程序開發提供了一種全新的系統架構,其憑藉着彈性伸縮省事省心,按需付費更低成本、聚焦業務降低 OPS 這三大核心價值,將開發人員從繁重的手動資源管理和性能成本優化中解放出來,讓工程師的生產力再次發生變革。

根據 CNCF 官方定義[2]:

Serverless is a cloud native development model that allows developers to build and run applications without having to manage servers. There are still servers in serverless, but they are abstracted away from app development. A cloud provider handles the routine work of provisioning, maintaining, and scaling the server infrastructure. Developers can simply package their code in containers for deployment. Once deployed, serverless apps respond to demand and automatically scale up and down as needed. Serverless offerings from public cloud providers are usually metered on-demand through an event-driven execution model. As a result, when a serverless function is sitting idle, it doesn’t cost anything.

從上面的定義可以看出, Severless != No Server, 只是對於開發者來說,沒有了 Server 去管理。而在雲廠商提供的服務中, Serverless 架構應該是採用 FaaS(Function as a service, 函數即服務)和 BaaS(後端服務)服務來解決問題的一種設計。

FaaS 服務的典型代表: AWS lambda、 阿里雲函數計算 FC、Azure Functions、Google Cloud Functions 等
BaaS 服務典型代表: AWS: S3、Dynamodb、SQS 等; 阿里雲: OSS、 TableStore、MNS 等

Serverless 計算

當然隨着需求和技術的發展,業界出現了一些 FaaS 以外的其它形態的 Serverless 計算服務,比如 Google Cloud Run、AWS App Runner、阿里雲 Serverless 應用引擎 SAE、 阿里雲 Serverless Kubernetes ASK 等,這些服務也提供了彈性伸縮能力和按使用計費的收費模式,具備 Serverless 服務的形態,可以說進一步擴大了Serverless 計算的陣營。

而在 Serverless 計算領域最典型的兩種產品形態代表 FaaS 和 Google Cloud Run, 都不約而同採用了併發度(Concurrency)這個指標作爲擴縮容策略。接下來我們重點剖析下不同產品形態下併發的語義以及爲什麼這些流行的 Serverless 計算產品爲什麼採用併發度作爲擴縮容的策略。

什麼是併發?

併發是現代計算的核心原則之一, 併發是指計算系統同時處理多個任務的能力。例如,如果您的計算機同時運行多個程序,則具有多個併發進程/線程可以共享 CPU 時間。如果單個應用程序進程同時處理多個網絡請求,或者並行處理隊列中的多個作業,則也可以認爲該應用程序正在執行併發工作。

比如 “世界第一語言 PHP” 在 Web 領域的實踐,使用就是進程池,如下圖中的 FastCGI 進程管理器。發送到服務器的 Web 請求將被分配給進程池中的 CGI 進程。該 CGI 進程將處理該單個請求。如果同時收到多個請求,則將啓動多個 CGI 進程並行處理它們。然而,每個進程一次只能處理一個請求。服務器能夠通過對 CGI 進程進行上下文切換來處理併發請求。操作系統調度程序將跟蹤所有 CGI 進程,並在需要時切換正在 CPU 上運行的 CGI 進程,以使每個 CGI 進程在需要時都能獲得屬於自己的、公平的 CPU 時間份額。

PHP Web 運行原理圖

如今,有更多用於併發的工具, 這包括現代編程語言內置的強大異步併發機制,以及幫助簡化併發的雲計算服務。讓我們看看一些雲計算服務如何設計和使用併發。

單實例單併發

雲廠商的 FaaS 服務的併發擴縮容原理基本大同小異, 我們以 AWS Lambda 官方文檔[3] 爲參考:

當首次調用一個函數時,FaaS 服務會創建一個函數實例,並運行處理程序方法以處理事件。完成後,函數會在一段時間內保持可用狀態,以處理後續的事件。如果在函數忙碌時有其他事件到達,FaaS 會創建更多的函數實例來同時處理這些請求。

從文檔中我們可以看出,每個函數實例一次只能處理一個事件請求(即 one concurrent request per instance,也稱爲單實例單併發)。在處理事件請求時,函數被認爲是繁忙的,因此任何併發事件都必須轉到另一個函數實例。每次必須創建函數的新實例時,都會出現短暫的“冷啓動”(Cold Start)延遲。這個冷啓動的持續時間取決於您的代碼大小和使用的運行時 Runtime。下圖[4]顯示了當有多個併發請求需要進行並行處理時,FaaS 如何實時擴展函數實例的數量:

Tips: 只有綠色部分是毫秒計費, 黃色和空白部分均不會計費, 真正100%爲計算資源付費。

FaaS scaling and concurrency

這使得 FaaS 的併發模型在某些方面類似於那些老式的 PHP 進程管理器。在這兩種情況下: 1). PHP 進程管理器通過並行啓動更多進程來實現併發。單個進程一次只能處理一個事件請求。2). FaaS 通過並行啓動更多的執行環境容器實例來實現併發, 單個實例一次只能處理一個事件請求。 但使用 PHP 進程管理器那樣的進程級別的併發有兩個經典難題需要解決:

  • 進程之間的安全隔離:您必須在操作系統分配 CPU 時間和系統資源給進程時做出正確的決策。一個進程可能會消耗過多的資源,影響在同一臺機器上運行的其他進程的性能。
  • 自動擴縮容:以 PHP 應用程序爲例,您必須管理每個服務器上的 PHP CGI 進程數量,並且必須對運行這些進程的服務器數量進行手動擴縮容。

FaaS 能很好解決上述兩個難題,FaaS 明顯有一些現代化的特點, 以函數計算執行環境容器的安全隔離爲例[5]:

阿里雲FC計算節點安全隔離

  • 虛擬化級別安全隔離
    • 神龍裸金屬計算節點可運行來自不同用戶的函數實例,使用阿里雲安全沙箱提供函數級別虛擬化及容器隔離,ECS虛擬機只允許運行同用戶的函數實例,藉助ECS隔離提供用戶級別虛擬化隔離,使用Runc等容器技術實現函數級別的容器隔離。
  • 函數實例網絡訪問受限,用戶決定網絡外訪權限
    • 函數實例配置私有IP地址,用戶不可直接訪問,且實例間網絡不可達,網絡隔離使用 open vSwitch、iptables 和 routing tables 實現。
  • 函數實例資源受限函數CPU/內存設置的配額
  • 函數計算負責函數實例沙箱容器的漏洞修復及安全升級

使用 FaaS 這種事件驅動的全託管計算服務,您將自動獲得隔離的執行環境實例,FaaS 服務自動管理執行環境實例的數量和容量。您所要做的事情就是提供您的代碼到 FaaS 服務,並向 FaaS 服務發送事件以觸發該代碼執行即可。

FaaS 簡略概覽

從上面對 FaaS 併發擴縮容的討論中,相信大家很快 get 到單個實例一個併發的能力對 CPU 密集型的邏輯非常友好。而現代的許多工作負載都充滿了 I/O 操作,如果我們採用 FaaS 經典的 one concurrent request per instance 模式,會有如下痛點問題:

  1. 嚴重的資源浪費

IO-intensive workload[11]

2.藍色方框表示程序正在工作時的時間,紅色方框表示等待 IO 操作完成所花費的時間。由於 IO 請求可能比 CPU 指令花費的時間長几個數量級,因此您的程序可能會花費大部分時間等待, 實例資源浪費嚴重。並且隨着並併發數目的變大,浪費的資源也呈線性增長,如下面紅色部分即爲浪費的計算資源:

FaaS IO-intensive workload

3.可能會對共享資源造成意想不到的後果

數據庫是一個典型的例子。當使用傳統的關係型數據庫(如 mysql)時,數據庫有一個最大併發連接數。傳統常駐型服務器通常使用“數據庫連接池”進行優化。“數據庫連接池”限制了單個服務器實例對數據庫的最大併發連接數,同時允許併發的請求能有效地共享“數據庫連接池”的連接。然而,如果每個實例只能處理一個請求並維持與數據庫的開放連接,則請求的數量與到數據庫的連接數之間存在一對一的關係。結果是在負載高峯期間,數據庫可能會因過多連接而打滿,並最終拒絕新連接。如果一個數據庫實例的最大連接數爲 100,使用 FaaS, 示意圖如下:

FaaS with DB

單實例多併發

因此,就 FaaS 領域的 one concurrent request per instance 的痛點問題, Google Cloud Run 提供了 multi concurrent requests per instance 的能力[6],這就很好解決我們上文討論的單實例單併發擴縮容模型的痛點:

Google Cloud Run單個實例默認最大併發度(即單個實例的併發請求數上限)爲80,最大可調整到100

1.IO等待期間不再是資源浪費

Google Cloud Run IO-Intensive workload

2.對共享資源造成影響可預期:提高數據庫連接吞吐

Google Cloud Run With DB

如果每個實例配置了數據庫連接池大小爲 10,那麼每個實例可以允許 10 個並行請求到數據庫。由於每個實例可能會接收高達 80 個併發請求,“數據庫連接池”將在等待數據庫連接被釋放並返回到池中時,自動阻止傳入的請求。通過使用 10 個數據庫連接響應 80 個請求,理論上可以在數據庫達到其最大連接限制之前將數據庫的吞吐量提高 10 倍。

有趣的是, 一些 FaaS 廠商勇敢做出了 multi concurrent requests per instance的嘗試, 比如阿里雲函數計算設置實例併發度 , Google Cloud Functions 第2代也開始支持設置實例併發度。旨在解決現代很重要的 IO 密集型工作負載問題。

爲什麼 Serverless 使用併發度進行擴縮容

FaaS 和 Google Cloud Run 採用實例併發度(即實例的併發請求數上限)這個指標進行擴縮容,而不是採用 CPU 指標等 HPA 策略,是因爲在Serverless領域,實例併發度是“基於請求處理/事件驅動進行擴縮容”表達最好的一個方式。

  • FaaS 和 Google Cloud Run 都有實例縮至爲 0 和有請求進來可以拉起一個新實例的能力,在實例 0-1 過程中無法使用 CPU 或內存等指標進行擴容。
  • 更好地匹配請求處理:併發度能夠更好地匹配實際請求的數量,因此可以更好地利用計算資源,同時確保請求能夠快速得到響應。以阿里雲函數計算和 K8S 做一個資源匹配請求速度的對比[7]:

  • 更好的資源利用率:實例併發度策略可以更好地利用計算資源,可以在請求高峯期間快速擴容,而在請求較少時保持最小的實例數量,從而減少資源浪費。FaaS 和 Google Cloud Run 允許用戶運行任何語言的代碼,並自動擴展以匹配流量: 併發度總數 = 同時處理請求的實例數量 * 每個實例的最大併發請求數上限

當然,引入的併發度的概念也給習慣了 CPU 指標等擴縮容的開發者帶來的新的疑惑, 對於 IO 密集型的應用,基於 CPU 指標的 HPA 擴容策略很簡單就可以提高應用程序的可用性、性能和可靠性,並使資源更高效地利用。反而單個實例的最大併發度的合理值怎麼去設置是一個比較頭疼的問題? 這個問題,業界通常都是建議您根據自己的負載情況做壓測迭代出合適的併發度值。 阿里雲函數計算爲此做了一個業界最前沿的探索, 提供了自動化推薦能力:從青銅到王者,揭祕 Serverless 自動化函數最佳配置[8], 並由此展望智能動態併發度:在這種模式下,用戶不需要通過手動配置參數,而是在函數運行時動態調整,根據實例 CPU 負載的健康指標自動調整到最佳值。

結論

基於上文對併發度的討論,對於單實例單併發(雲產品代表 FaaS)和 單實例多併發(雲產品代表 Google Cloud Run) 這兩種形態的 Serverless 產品, 我應該選擇哪個產品來託管我的應用程序呢? 以下是一些情景是我個人會選擇哪種產品的建議:

但最終還是需要根據您具體的業務需求做取捨,選擇最合適的產品和方案。
注:FaaS 中的函數計算 FC 和 Google Cloud Functions V2 也支持單實例多併發

上述表格中的建議是基於阿里雲函數計算應用中心[9]中的用戶對於應用的偏好部署次數【見下圖】以及客戶落地案例【見參考12】來佐證的, 尤其對於每個請求必須相互隔離或者 CPU 密集型任務, FaaS 具有無與倫比的優勢:

  • 對於存量應用,將 CPU 密集型任務從應用中抽離出來,提升服務的穩定性,這個文章 PDF Generation With AWS Lambda[10] 深入討論了這種實踐的收益。
  • 對於新業務 CPU/GPU 密集型應用, 如音視頻處理以及最近大火的大模型 AIGC(AI generated content ) 應用, 是 FaaS 天然契合的場景 。
在 AI 場景中請求和後端資源的調度比傳統的微服務場景的要求會更高,主要原因是 AI 場景的請求對資源的消耗特別大。比如一個 Stable Diffusion 使用 A10 GPU 卡部署,一塊 A10 卡(ecs.gn7i-c8g1.2xlarge) 啓動 Stable Diffusion 服務一次只能處理個位數的文本繪圖請求。一旦同時進來請求過多,就會出現計算資源競爭從而導致請求超時的情況。而 FaaS 的 "one concurrent request per instance" 天然契合這個場景, 簡直就是絕配。

函數計算FC應用中心文件處理應用部署情況圖

函數計算 FC 應用中心音視頻處理應用部署情況圖

函數計算FC應用中心AI應用部署情況圖

參考文獻

  1. https://www2.eecs.berkeley.edu/Pubs/TechRpts/2019/EECS-2019-3.pdf
  2. https://glossary.cncf.io/serverless/
  3. https://docs.aws.amazon.com/lambda/latest/operatorguide/scaling-concurrency.html
  4. https://nathanpeck.com/concurrency-compared-lambda-fargate-app-runner/files/Concurrency%20Compared.pptx
  5. https://help.aliyun.com/document_detail/438853.html
  6. https://cloud.google.com/run/docs/about-concurrency?hl=zh-cn
  7. https://developer.aliyun.com/article/1243681
  8. https://developer.aliyun.com/article/1161868
  9. https://help.aliyun.com/document_detail/606948.html
  10. https://medium.com/1mgofficial/pdf-generation-with-aws-lambda-627b8dd07c77
  11. https://realpython.com/python-concurrency/
  12. 基於函數計算應用中心落地案例參考
    1. 網易雲音樂音視頻算法的 Serverless 探索之路
    2. 聚焦彈性問題,杭州銘師堂的 Serverless 之路
    3. 優化 20% 資源成本,新東方的 Serverless 實踐之路
    4. 當 Rokid 遇上函數計算
    5. 多家遊戲公司 apk 實時打渠道包 repackAPK

作者|西流(阿里雲技術專家)

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章