
作者 | 易立 阿裡巴巴資深技術專家
導讀:WebAssembly 技術已經走出浏覽器,讓計算無處不在。本文利用 containerd 的擴充機制,可以為 WebAssembly 應用提供與其他容器應用一緻的、抽象的、應用分發、傳遞和運維模型,可以在 Kubernetes 叢集中進行統一排程和管理。
無處不在的 WebAssembly
如果評選 2019 年程式設計技術的“網紅”,無論是前端圈還是後端圈,WebAssembly (WASM) 都絕對能夠高票入選。然而,如果評選最被“低估”的技術,我覺得 WebAssembly 也可以輕松入圍。
借用伏爾泰曾評價神聖羅馬帝國的句式 “既不神聖,也不羅馬,更非帝國”,我們也可以說WebAssembly “既不限于 Web,更不是 Assembly(彙編語言)”。
在 2019 年 12 月,網際網路聯盟 (World Wide Web Consortium - W3C) 宣布
WebAssembly 核心規範正式成為 Web 标準, 這使得 WebAssembly 成為網際網路上與 HTML, CSS, and JavaScript 并列的第四種官方語言,可以原生的運作在浏覽器上。而更加重要的是,WebAssembly 作為一個安全的、可移植、高效率的虛拟機沙箱,可以在 Internet 的任何地方、任何平台(不同作業系統,不同 CPU 體系架構下)安全運作應用。WebAssembly 已得到了所有主流浏覽器廠商的廣泛支援(Google Chrome, Microsoft Edge, Apple Safari, Mozilla Firefox 等),然而它的影響已經遠超 Web。
WebAssembly 的設計初衷之一是為了解決 JavaScript 的性能問題,使得 Web 網頁應用有接近本機原生應用的性能。作為一個通用、開放、高效的底層虛拟機抽象,衆多程式設計語言(如 C/C++, Rust 等)可以将現有應用編譯成為 WASM 的目标代碼,運作在浏覽器中 。這讓應用開發技術與運作時技術解耦,極大促進了代碼複用。
Mozilla 在 2019 年 3 月推出了 WebAssembly System Interface(WASI),來标準化 WebAssembly 應用與系統資源的互動抽象,比如檔案系統通路,記憶體管理,網絡連接配接等,類似 POSIX 這樣的标準 API。WASI 規範大大拓展了 WASM 應用的場景,可以讓其可以超越浏覽器環境,作為一個獨立的虛拟機運作各種類型的應用。同時,平台開發商可以針對具體的作業系統和運作環境提供 WASI 接口不同的實作,可以在不同裝置和作業系統上運作跨平台的 WebAssembly 應用。這可以讓應用執行與具體平台環境實作解耦。這一切使得“Build Once, Run Anywhere”的理想逐漸形成現實。WASI 的示意圖如下所示。2019 年 11 月,為了進一步推動子產品化 WebAssembly 生态系統,Mozilla、Fastly、英特爾和紅帽公司攜手成立了位元組碼聯盟(
Bytecode Alliance),共同上司 WASI 标準、 WebAssembly 運作時、語言工具等工作。
圖檔來源:
https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/WASM 與容器相愛相殺
WebAssembly 是否會取代容器?
正因為 WebAssembly 所具備的的安全、可移植、高效率,輕量化的特點,非常适于應用安全沙箱場景。WASM 得到了容器、函數計算、IoT / 邊緣計算等社群的廣泛關注。Docker 創始人 Solomon Hykes 在 WASI 釋出之際的一句 Twitter,更是成為了去年容器和 WebAssembly 社群引用頻率最高的一句話之一。
Fastly, Cloudflare 等 CDN 廠商基于 WebAssembly 技術實作了更加輕量化的應用安全沙箱,可以在一個程序内部運作多個獨立的使用者應用。阿裡雲 CDN 團隊 EdgeRoutine 也實作了類似技術。與容器技術相比,WASM 可以實作毫秒級冷啟動時間和極低的資源消耗。
原圖:
https://blog.cloudflare.com/cloud-computing-without-containers/當然,世界上沒有完美的技術。任何沙箱技術不可能同時滿足執行效率、安全隔離性和通用性這三個次元的要求。WASM 在安全隔離和通用性等方面與 Docker Container 等存在差距。雖然如此,我們還是看到了 WebAssembly 技術巨大的潛力。
WebAssembly 容器
我的了解是 WebAssmebly 可以成為一種容器類型,類似 Linux Container 或者 Windows Container 一樣。成為一個跨平台的标準應用分發方式和運作時環境。
應用分發
Docker 容器的一個重要貢獻是其标準化了容器化應用打包規範 Docker Image,而且它已經成為開放容器計劃 (Open Container Initiative - OCI) 的鏡像格式标準。Docker 鏡像提供了自包含、自描述的鏡像格式。它可以将應用以及其依賴的環境資訊打包在一起,進而實作應用與運作環境解耦,讓容器應用可以輕松運作在從本地開發環境到雲端生産環境的不同場景中。并且社群圍繞 Docker 鏡像建構了繁榮的工具鍊生态,如 Docker Hub 可以進行應用分發和 CI / CD 協同,Nortary / TUF 項目可以保障應用可信地分發、傳遞。
對與 WebAssembly,目前社群提供了類似 NPM 的包管理實作
WAPM,可以較好地支援應用的分發。 為 WebAssembly 應用建構 Docker 鏡像,可以實作雙赢的局面。
- WebAssembly 開發者可以完全複用 Docker/OCI 鏡像規範和工具鍊,進一步簡化應用分發和傳遞。比如,我們可以将 Nginx 的 WASM 鏡像作為基礎鏡像,基于這個鏡像可以建構包含不同 Web 内容的應用鏡像;我們可以利用 tag 對應用版本進行追蹤;利用 Docker Registry 進行應用分發;在這個過程我們還可以進一步利用數字簽名來保障安全的軟體供應鍊;
- Docker 鏡像規範支援 Multi-Arch 鏡像,可以簡化不同 CPU 體系架構(如 x86, ARM, RISC-V 等)的應用鏡像的建構與分發。而 WebAssembly 天生具備可移植性,大大簡化了跨平台 Docker 應用鏡像的建構和分發。參考: 利用 Docker 加速 ARM 容器應用開發和測試流程 。
我提供了一個技術原型
示例項目,大家可以參考其中的例子來建構 WASM 容器鏡像。由于 WebAssembly 應用采用緊湊的二進制格式,而且沒有任何作業系統依賴,WASM 應用可以建構出非常小的容器鏡像。大家可以自行感受一下:
$ sudo ctr image ls
REF TYPE DIGEST SIZE PLATFORMS LABELS
docker.io/denverdino/c-http-server-wasm:latest application/vnd.docker.distribution.manifest.v2+json sha256:2efa759f46f901cda2e6a9b4228c423b17a960c06e957964e72c21dc5b42408f 29.2 KiB linux/amd64 -
docker.io/denverdino/hellowasm:latest application/vnd.docker.distribution.manifest.v2+json sha256:cadcc8b07eb82b18db2c8f500fa2b11e5ebf2e9054cfa687e4ffe44861860132 8.2 KiB linux/amd64 -
docker.io/denverdino/nginxwasm:latest application/vnd.docker.distribution.manifest.v2+json sha256:8735c82524a463b842b7c79f2c1be8094ee1c57cfd34154f68752fbe79c25998 582.7 KiB linux/amd64 -
安全隔離
WebAssembly 的最初設計目标是讓應用可以安全運作在浏覽器中。WASM 虛拟機提供的
沙箱和記憶體隔離機制,可以有效減少安全攻擊面。而當 WebAssembly 走出浏覽器,面向更加通用的場景。WASM 也面對更加複雜的安全挑戰。
WASI 提供了
基于能力的安全模型。WASI 應用遵循最小權限原則,應用隻能通路其執行所需的确切資源。傳統上,如果應用需要打開檔案,它會帶路徑名字元串調用系統操作 open。然後系統調用會檢查應用是否具有通路該檔案的相關權限,比如 Linux 實作了基于使用者/組的權限模型。這樣隐式的安全模型,依賴于正确的安全管理配置,比如一旦特權使用者執行了一個惡意應用,它就可以通路系統中任意的資源。而對于 WASI 應用而言,如果它需要需要通路指定檔案等系統資源,需要從外部顯式傳入加有權限的檔案描述符引用,而不能通路任何其他未授權資源。這中依賴注入的方式可以避免傳統安全模型的潛在風險。
一個示意圖如下:
我們可以看到 WASI 的安全模型與傳統作業系統安全模型非常不同,而且還在持續演進中。比如位元組碼聯盟提出了
nanoprocess來解決應用子產品間的安全協同和信任傳遞。
WebAssembly/WASI 的安全模型依然存在不足,比如:
- 資源隔離
對于記憶體資源,WebAssembly 實作了線性記憶體模型。WebAssembly 應用隻能利用索引通路傳入的一段邏輯線性記憶體。而 WASM 虛拟機負責确定記憶體的實際實體位址,WASM 應用無法獲知記憶體的真實位址,也無法通過越界通路等方式發動攻擊。是以理論上,可以對 WASM 應用進行資源容量限制。但是目前部分 WASM 虛拟機還無法對記憶體進行精确的隔離限制。
對于 CPU 資源,部分的 WASM 虛拟機實作可以對應用使用的 CPU 資源進行計量,但是大多無法實作精确的配額限制、優先級和搶占式排程。
I/O 資源,比如 IOPS 等,WASM 目前完全沒有相關的隔離能力。
- 網絡安全
WASI 的 Capability 模型對于檔案系統通路相對比較容易保護。但是這個靜态的安全模型無法适用于動态的網絡應用場景。在微服務架構中,應用經常通過 Service Registry 進行服務發現,為服務的調用者和提供者實作動态的調用綁定。這個語義是無法用靜态的 capability 模型描述和注入的。這也導緻了 WASI 的網絡部分 API 還處于讨論之中。現有的
WASI 網絡安全模型,以及相關
讨論Linux 作業系統和容器技術已經提供了非常完備的資源隔離和安全隔離實作。與 WebAssembly 結合在一起可以應對不同場景對不同隔離級别的需求。
- 共享程序資源 - 多個 WASM 應用子產品運作在一個 WASM 虛拟機程序内部,依賴 WASM 運作時進行隔離。隔離級别低,控制粒度比較粗,資源開銷極小。可以以較小代價保障系統安全。适合受限問題域的應用安全隔離;
- 獨立程序資源 - 不同 WASM 應用子產品運作在不同的 WASM 虛拟機程序中,可以複用作業系統的程序級隔離能力,比如 CGroup。此外,還可以利用類似 Kubernetes 中的 Network Policy (網絡政策),或者服務網格(如Istio)等技術,對程序的網絡通路進行細粒度的控制,甚至實作零信任網絡。隔離級别比較高,控制粒度比較細,資源開銷适中。可以應用于更加通用的場景。
注:當然利用安全沙箱如虛拟化等技術,結合 WebAssembly,可以進一步最小化安全攻擊面,但是 ROI 不高。
排程與編排
在雲時代,Kubernetes 已經成為分布式環境下資源排程和應用編排的事實标準。Kubernetes 可以屏蔽底層設施的差異性。可以在同一個 K8s 叢集中包含 x86、ARM 等不同體系架構的節點,可以支援 Linux,Windows 等不同的作業系統。Kubernetes 和 WebAssembly 相結合可以進一步提升應用的可移植性。
微軟的 Deis Labs 年初釋出了一個
實驗項目,來利用 Virtual Kubelet 類似的架構排程 WebAssembly 應用。但是這個方式有很多局限,無法借助容器方式進行應用分發,也無法利用 K8s 的語義進行資源編排。
難得有一個春節假期可以宅在家裡,在此期間我基于 Derek McGowan 去年的一個
實驗性項目,完善了 containerd 的 WASM shim 實作。可以讓 containerd 支援 WASM container,并且可以利用 Kubernetes 叢集管理和排程 WASM container。
項目的代碼實作:
https://github.com/denverdino/containerd-wasm注:這個項目更多是概念驗證,程序管理、資源限制,性能優化等的細節并沒未完整實作。
整個系統的架構設計如下,“container-shim-wasm-v1”作為 Containerd 的擴充,利用
wasmer作為 WASM 應用運作時環境,可以實作與 runc 容器一緻的使用者體驗。
我們還會将其注冊為 K8s 的一個
RuntimeClass,允許使用者利用 K8s 來傳遞和運維 WASM 應用。
注:RuntimeClass 是 Kubernetes v1.12 引入的新概念,可以讓 Kubernetes 支援多種不同的容器運作時,比如 runc 容器、或者 Kata Containers,gVisor 等安全沙箱容器。更多細節可以參考:
containerd 與安全沙箱的 Kubernetes 初體驗Talk is Cheap, 放碼過來
首先,我們将利用 Minikube 建立一個 K8s 測試環境,并将 Containerd 作為 Kubernetes 叢集的容器運作時。
建立虛拟機測試環境
建立 Minikube K8s 叢集,并将 Containerd 作為 Kubernetes 叢集容器運作時。
minikube start --image-mirror-country cn \
--iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.6.0.iso \
--registry-mirror=https://tgtsuwdg.mirror.aliyuncs.com \
--container-runtime=containerd
進入 Minikube 虛拟機:
$ minikube ssh
_ _
_ _ ( ) ( )
___ ___ (_) ___ (_)| |/') _ _ | |_ __
/' _ ` _ `\| |/' _ `\| || , < ( ) ( )| '_`\ /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )( ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)
配置環境所需依賴:
- wasmer 0.13;
- minikube 預設安裝了 container 1.2.x,需要更新 containerd 1.3.x;
- 我提供了一個預編譯的 containerd-wasm-shim-v1,也可自己編譯一個版本。
cd ~
# Install Wasmer 0.13.1
curl -L -O https://github.com/wasmerio/wasmer/releases/download/0.13.1/wasmer-linux-amd64.tar.gz
gunzip wasmer-linux-amd64.tar.gz
tar xvf wasmer-linux-amd64.tar
sudo cp bin/* /usr/bin/
# Upgrade containerd to v1.3.2
curl -L -O https://github.com/containerd/containerd/releases/download/v1.3.2/containerd-1.3.2.linux-amd64.tar.gz
gunzip containerd-1.3.2.linux-amd64.tar.gz
tar xvf containerd-1.3.2.linux-amd64.tar
sudo systemctl stop containerd
sudo cp bin/* /usr/bin/
sudo systemctl restart containerd
# Install containerd-wasm-shim
wget http://kubernetes.oss-cn-hangzhou.aliyuncs.com/containerd-wasm/containerd-shim-wasm-v1
chmod +x containerd-shim-wasm-v1
sudo mv containerd-shim-wasm-v1 /usr/bin/
配置 containerd 支援 WASM shim
在 containerd 配置檔案中添加 wasm shim 相關配置,并重新開機 containerd。
$ cat <<EOF | sudo tee -a /etc/containerd/config.toml
disabled_plugins = ["restart"]
[plugins.cri.containerd.runtimes.wasm]
runtime_type = "io.containerd.wasm.v1"
EOF
$ sudo systemctl restart containerd
測試 Hello World WASM 容器應用:
$ sudo ctr image pull docker.io/denverdino/hellowasm:latest
docker.io/denverdino/hellowasm:latest: resolved |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:cadcc8b07eb82b18db2c8f500fa2b11e5ebf2e9054cfa687e4ffe44861860132: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:ecda28441283ecf01d35bca0361f2c1ef26a203454a06789ee5ce71ba1e32ca3: done |++++++++++++++++++++++++++++++++++++++|
config-sha256:57974480d640c8d60d254a8b0fa4606b2c7107fe169bc3ddd455091277c3a5e4: done |++++++++++++++++++++++++++++++++++++++|
elapsed: 3.0 s total: 0.0 B (0.0 B/s)
unpacking linux/amd64 sha256:cadcc8b07eb82b18db2c8f500fa2b11e5ebf2e9054cfa687e4ffe44861860132...
done
$ sudo ctr run --rm --runtime io.containerd.wasm.v1 docker.io/denverdino/hellowasm:latest test1
Hello world
測試 Nginx 的 WASM 容器應用:
$ sudo ctr image pull docker.io/denverdino/nginxwasm:latest
docker.io/denverdino/nginxwasm:latest: resolved |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:8735c82524a463b842b7c79f2c1be8094ee1c57cfd34154f68752fbe79c25998: exists |++++++++++++++++++++++++++++++++++++++|
layer-sha256:27f4d8ad067fbb709d18ea5acd7a5ddfb85851e5d9f030636e9da3d16cc4bd07: done |++++++++++++++++++++++++++++++++++++++|
config-sha256:a55bd3bdb9d00fdac5ee2f64bfc1856e58e8bb90587943969ad3d8115f4ced70: done |++++++++++++++++++++++++++++++++++++++|
elapsed: 3.0 s total: 0.0 B (0.0 B/s)
unpacking linux/amd64 sha256:8735c82524a463b842b7c79f2c1be8094ee1c57cfd34154f68752fbe79c25998...
done
$ sudo ctr run --rm --runtime io.containerd.wasm.v1 docker.io/denverdino/nginxwasm:latest test2
2020/02/01 07:01:21 [notice] 30672#0: using the "select" event method
2020/02/01 07:01:21 [notice] 30672#0: nginx/1.15.3
2020/02/01 07:01:21 [notice] 30672#0: built by clang 6.0.1 (emscripten 1.38.11 : 1.38.11)
2020/02/01 07:01:21 [notice] 30672#0: OS: Linux 4.19.81
2020/02/01 07:01:21 [notice] 30672#0: getrlimit(RLIMIT_NOFILE): 1024:1024
在 Minikube 外部,可以用如下方式獲得 nginx 應用的通路位址:
$ echo http://$(minikube ip):8080
http://192.168.64.13:8080
利用浏覽器打開上述位址,顯示如下:
建立 WASM 容器的 RuntimeClass CRD
為了 WASM 容器可以被 Kubernetes 所排程,我們需要建立一個 RuntimeClass CRD。
下載下傳示例檔案:
$ git clone https://github.com/denverdino/wasm-container-samples
$ cd wasm-container-samples
注冊 RuntimeClass “wasm”這個值:
$ cat wasm-runtimeclass.yaml
apiVersion: node.k8s.io/v1beta1
kind: RuntimeClass
metadata:
name: wasm
handler: wasm
$ kubectl apply -f wasm-runtimeclass.yaml
runtimeclass.node.k8s.io/wasm created
$ kubectl get runtimeclass
kubectl get runtimeclass
NAME CREATED AT
wasm 2020-02-01T06:24:12Z
在 K8s 中運作 WASM 容器應用
在 K8s 應用的 yaml manifest 中,我們可以在 Pod Spec 上指明所需 runtimeClassName。下面我們就用 K8s 來部署一個 nginx 的 WASM 容器。
$ cat nginx-wasm.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-wasm
spec:
runtimeClassName: wasm
containers:
- name: nginx
image: denverdino/nginxwasm
ports:
- containerPort: 8080
$ kubectl apply -f nginx-wasm.yaml
pod/nginx-wasm created
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-wasm 1/1 Running 0 9s
新機遇、新希望
目前為止,WebAssembly 技術仍處于初期階段,WASI 也有很多局限性。但是社群的進展非常快,SIMD 指令支援,多線程處理等規範也正在快速演進中。WebAssembly 已經打破次元壁,将高性能的計算能力帶領到 Web 浏覽器端,越來越多的計算密集型的遊戲、AI 模型預測、和資料處理應用被移植到浏覽器端,可以為應用提供更加優化的使用者體驗。
WebAssembly 更廣闊的空間在雲計算領域、區塊鍊等分布式計算領域。WebAssembly 輕量、靈活、安全的特性,可以有效降低 Serverless 應用啟動速度和資源消耗。同時 WebAssembly 的可移植,可以讓應用一緻運作在從雲端伺服器到邊緣 IoT 裝置等不同平台環境中,讓計算無處不在。
利用 containerd 的擴充機制,可以為 WebAssembly 應用提供與其他容器應用一緻的、抽象的、應用分發、傳遞和運維模型,可以在 Kubernetes 叢集中進行統一排程和管理。希望通過類似的探索可以簡化基于 WebAssembly 的分布式應用管理和運維。
關于作者
易立,阿裡雲資深技術專家,阿裡雲容器服務的研發負責人。之前曾在 IBM 中國開發中心工作,擔任資深技術專員;作為架構師和主要開發人員負責或參與了一系列在雲計算、區塊鍊、Web 2.0,SOA 領域的産品研發和創新。阿裡雲容器平台團隊求賢若渴!社招技術專家 / 進階技術專家,base 杭州 / 北京 / 深圳。歡迎發履歷到 [email protected]
“ 阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”