開發與維運

“網紅” WebAssembly 與 K8s 如何實現雙劍合璧?

1.png

作者 | 易立  阿里巴巴資深技術專家

導讀: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 運行時、語言工具等工作。

2.png

圖片來源:https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/

WASM 與容器相愛相殺

WebAssembly 是否會取代容器?

正因為 WebAssembly 所具備的的安全、可移植、高效率,輕量化的特點,非常適於應用安全沙箱場景。WASM 得到了容器、函數計算、IoT / 邊緣計算等社區的廣泛關注。Docker 創始人 Solomon Hykes 在 WASI 發佈之際的一句 Twitter,更是成為了去年容器和 WebAssembly 社區引用頻率最高的一句話之一。

3.png

Fastly, Cloudflare 等 CDN 廠商基於 WebAssembly 技術實現了更加輕量化的應用安全沙箱,可以在一個進程內部運行多個獨立的用戶應用。阿里雲 CDN 團隊 EdgeRoutine 也實現了類似技術。與容器技術相比,WASM 可以實現毫秒級冷啟動時間和極低的資源消耗。

4.png

原圖: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 應用而言,如果它需要需要訪問指定文件等系統資源,需要從外部顯式傳入加有權限的文件描述符引用,而不能訪問任何其他未授權資源。這中依賴注入的方式可以避免傳統安全模型的潛在風險。

一個示意圖如下:

5.png

原圖:https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/

我們可以看到 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 容器一致的用戶體驗。

6.png

我們還會將其註冊為 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

利用瀏覽器打開上述地址,顯示如下:

7.png

創建 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]

8.png

阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”

Leave a Reply

Your email address will not be published. Required fields are marked *