当 Knative 遇见 WebAssembly
作者:易立
Knative 是在 Kubernetes 基礎之上的 Serverless 計算的技術框架,可以極大簡化 Kubernetes 應用的開發與運維體驗。在 2022 年 3 月成為 CNCF 孵化項目。Knative 由兩個主要部分組成:一個是支持 HTTP 在線應用的 Knative Serving,一個是支持 CloudEvents 和事件驅動應用的 Knative Eventing。
Knative 可以支持各種容器化的運行時環境,我們今天來探索一下利用 WebAssembly 技術作為一個新的 Serverless 運行時。
從 WASM、WASI 到 WAGI
WebAssembly(簡稱 WASM)是一個新興的 W3C 規范。它是一個虛擬指令集體系架構(virtual ISA),其初始目標是為 C/C++等語言編寫的程序,可以安全和高效地運行在瀏覽器中。在 2019 年 12 月,W3C 正式宣布 WebAssembly 的核心規范成為Web標準, 大大推進了 WASM 技術普及。今天,WebAssembly 已經得到了 Google Chrome、Microsoft Edge、Apple Safari、Mozilla Firefox 等流瀏覽器的全面支持。而更加重要的是,WebAssembly 作為一個安全的、可移植、高效率的虛擬機沙箱,可以在任何地方、任何操作系統,任何 CPU 體系架構中安全地運行應用。
Mozilla 在 2019 年提出了 WebAssembly System Interface(WASI),它提供類似 POSIX 這樣的標準 API 來標準化 WebAssembly 應用與文件系統,內存管理等系統資源的交互。WASI 的出現大大拓展了 WASM 的應用場景,可以讓其作為一個虛擬機運行各種類型的服務端應用。為了進一步推動 WebAssembly 生態發展,Mozilla、Fastly、英特爾和紅帽公司攜手成立了字節碼聯盟(Bytecode Alliance),共同領導 WASI 標準、WebAssembly 運行時、工具等工作。后續微軟,谷歌、ARM 等公司也成為其成員。
WebAssembly 技術仍然在持續快速演進中,2022 年 4 月,W3C 公布了 WebAssembly 2.0 的第一批公共工作草案,這也成為其成熟與發展的重要標志。
WASM/WASI 作為一種新興的后端技術,具備的的原生安全、可移植、高性能,輕量化的特點,非常適于作為分布式應用運行環境。與容器是一個一個獨立隔離的操作系統進程不同,WASM 應用可以在一個進程內部實現安全隔離,支持毫秒級冷啟動時間和極低的資源消耗。如下圖所示:
圖片來源:cloudflare
目前 WASM/WASI 還在發展初期,還有很多技術限制,比如不支持線程,無法支持低級 Socket 網絡應用等等,這極大限制了 WASM 在服務器端的應用場景。社區都在探索一個能夠充分適配 WASM 的應用開發模型,揚長避短。微軟 Deislabs 的工程師從 HTTP 服務器發展的歷史中汲取靈感,提出了 WAGI - WebAssembly Gateway Interface 項目 [ 1] 。沒錯 WAGI 的概念就是來自于互聯網的上古傳奇,CGI。
CGI 是“公共網關接口”(Common Gateway Interface)的簡稱,是 HTTP 服務器與其它程序進行交互的一種規范。HTTP Server 通過標準輸入、輸出接口等與 CGI 腳本語言進行通信,開發者可以使用 Python/PHP/Perl 等各種實現來處理 HTTP 請求。
一個非常自然的推演,如果我們可以通過 CGI 規范來調用 WASI 應用,開發者就可以非常輕松地利用 WebAssembly 來編寫 Web API 或者微服務應用了,而且無需在 WASM 中處理太多的網絡實現細節。下圖就是 CGI 與 WAGI 的概念架構圖對比:
二者架構上高度相似,其不同之處是:傳統 CGI 架構,每次 HTTP 請求會創建一個 OS 進程來進行處理,由操作系統的進程機制來實現安全隔離;而 WAGI 中 ,每次 HTTP 請求會在一個獨立的線程來中調用 WASI 應用,應用之間利用 WebAssembly 虛擬機實現安全隔離。在理論上,WAGI 可以有比 CGI 更低的資源損耗和更快的響應時間。
本文不會對 WAGI 自身架構以及 WAGI 應用開發進行分析。有興趣的小伙伴可以自行閱讀項目文檔。
進一步思考,如果我們可以將 WAGI 作為一個 Knative Serving 運行時,我們就可以建立起一座將 WebAssembly 應用于 Serverless 場景的橋梁。
WAGI 應用冷啟動分析與優化
冷啟動性能是 Serverless 場景的關鍵指標。為了更好了了解 WAGI 執行效率,我們可以利用 ab 做一個簡單的壓測:
$ ab -k -n 10000 -c 100 http://127.0.0.1:3000/...Server Software: Server Hostname: 127.0.0.1 Server Port: 3000Document Path: / Document Length: 12 bytesConcurrency Level: 100 Time taken for tests: 7.632 seconds Complete requests: 10000 Failed requests: 0 Keep-Alive requests: 10000 Total transferred: 1510000 bytes HTML transferred: 120000 bytes Requests per second: 1310.31 [#/sec] (mean) Time per request: 76.318 [ms] (mean) Time per request: 0.763 [ms] (mean, across all concurrent requests) Transfer rate: 193.22 [Kbytes/sec] receivedConnection Times (ms)min mean[+/-sd] median max Connect: 0 0 0.6 0 9 Processing: 8 76 29.6 74 214 Waiting: 1 76 29.6 74 214 Total: 8 76 29.5 74 214Percentage of the requests served within a certain time (ms)50% 7466% 8875% 9580% 10090% 11595% 12598% 13999% 150100% 214 (longest request)我們可以看到 P90 請求響應時間在 115ms,就這?這個和我們對 WASM 應用輕量化的認知不同。利用火焰圖,我們可以快速定位到問題所在:prepare_wasm_instance 函數消耗了整體應用運行 80% 的時間。
經過對代碼的分析,我們發現在每次響應 HTTP 請求過程中,WAGI 都要對已經編譯過的 WSM 應用,重新連接 WASI 以及 wasi-http 等擴展和并進行環境配置。這消耗了大量的時間。定位了問題,解決思路就非常簡單了,重構執行邏輯,讓這些準備工作只在初始化過程中執行一次,無需在每次 HTTP 請求過程中重復執行。具體可參考優化過的實現 [ 2]
我們重新運行一遍壓力測試:
$ ab -k -n 10000 -c 100 http://127.0.0.1:3000/...Server Software: Server Hostname: 127.0.0.1 Server Port: 3000Document Path: / Document Length: 12 bytesConcurrency Level: 100 Time taken for tests: 1.328 seconds Complete requests: 10000 Failed requests: 0 Keep-Alive requests: 10000 Total transferred: 1510000 bytes HTML transferred: 120000 bytes Requests per second: 7532.13 [#/sec] (mean) Time per request: 13.276 [ms] (mean) Time per request: 0.133 [ms] (mean, across all concurrent requests) Transfer rate: 1110.70 [Kbytes/sec] receivedConnection Times (ms)min mean[+/-sd] median max Connect: 0 0 0.6 0 9 Processing: 1 13 5.7 13 37 Waiting: 1 13 5.7 13 37 Total: 1 13 5.6 13 37Percentage of the requests served within a certain time (ms)50% 1366% 1575% 1780% 1890% 2195% 2398% 2599% 27100% 37 (longest request)在經過優化過的實現中,P90響應時間已經下降到 21ms,其中 prepare_wasm_instance 所占運行時間已經下降到 17%。整體冷啟動效率有了很大的提升!
注:本文利用 flamegraph [ 3] 進行的性能分析。
利用 Knative 運行 WAGI 應用
為了讓 WAGI 可以作為 Knative 應用運行,我們還需在 WAGI 上增加了對 SIGTERM 信號的支持,讓 WAGI 容器支持優雅下線。具體細節不再贅述。
Knative 的環境準備可以參考 Knative 安裝文檔 [ 4] ,利用 Minikube 創建本地測試環境。
注:前提是需要有一定的網絡能力,因國內無法訪問在 gcr.io 中的 Knative 鏡像。
一個更加簡單的方式是直接使用阿里云 Serverless 容器服務 ASK [ 5] 上 Serverless K8s 集群。ASK 內建了 Knative 支持 [ 6 ] ,無需復雜的配置安裝過程即可以開發和使用 Knative 應用。
首先我們利用 WAGI 來定義一個 Knative 服務:
apiVersion: serving.knative.dev/v1 kind: Service metadata:name: autoscale-waginamespace: default spec:template:metadata:annotations:# Knative concurrency-based autoscaling (default).autoscaling.knative.dev/class: kpa.autoscaling.knative.devautoscaling.knative.dev/metric: concurrency# Target 10 requests in-flight per pod.autoscaling.knative.dev/target: "10"# Disable scale to zero with a min scale of 1.autoscaling.knative.dev/min-scale: "1"# Limit scaling to 100 pods.autoscaling.knative.dev/max-scale: "10"spec:containers:- image: registry.cn-hangzhou.aliyuncs.com/denverdino/knative-wagi:0.8.1-with-cache其中:
-
容器鏡像 knative-wagi 包含了 WAGI 網關和一些示例的 WASI 應用,更多細節可以參考項目 [ 7] 。
-
autoscale-wagi 服務可以根據請求數進行彈性伸縮
大家也可以進行一些壓測,學習一下 Knative 的彈性伸縮能力。
后記
本文介紹了 WAGI 這個項目,它可以將 HTTP 服務器的網絡處理細節,與 WASM 應用邏輯實現解耦。這樣可以輕松將 WASM/WASI 應用與 Knative 這樣的 Serverless 框架相結合。一方面我們可以復用 Knative/K8s 帶來的彈性和大規模資源調度能力,一方面我們可以發揮 WebAssembly 帶來的安全隔離、可移植、輕量化等優勢。
一脈相承的思考,在之前一篇文章《WebAssembly + Dapr = 下一代云原生運行時?》 中,我介紹了一個思路是將 WASM 應用與外部服務依賴通過 Dapr 實現解耦,來解決可移植性與多樣化的服務能力之間的矛盾。
當然這些工作還是簡單的玩具,只是用來驗證技術的可能性邊界。主要目的還是拋磚引玉,聽到大家關于下一代分布式應用框架和運行時環境的思考和設想。
文章書寫過程中,忽然回憶起在 90 年代與師兄們一起根據 RFC 規范來實現 HTTP Server 與 CGI Gateway 的歲月,那是一種非常簡單而單純的快樂。在這里,也祝每一位技術人永葆好奇,享受每一天的編程時光。
點擊此處,了解阿里云 Serverless 容器服務 ASK 更多詳情!
參考鏈接
[1] WAGI - WebAssembly Gateway Interface 項目:
https://github.com/deislabs/wagi
[2] 優化過的實現:
https://github.com/denverdino/wagi/tree/with_cache
[3] flamegraph:
https://github.com/flamegraph-rs/flamegraph
[4] Knative 安裝文檔:
https://knative.dev/docs/install/
[5] 阿里云 Serverless 容器服務 ASK:
https://www.aliyun.com/product/cs/ask
[6] Knative 支持:
https://help.aliyun.com/document_detail/121508.html
[7] 項目:
https://github.com/denverdino/knative-wagi
總結
以上是生活随笔為你收集整理的当 Knative 遇见 WebAssembly的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ERC20协议API接口规范
- 下一篇: 北上或者南下之被摆了一道