天天看點

WebAssembly 與 Kubernetes雙劍合璧

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年月,為了進一步推動子產品化 WebAssembly 生态系統,Mozilla、Fastly、英特爾和紅帽公司攜手成立了位元組碼聯盟(

Bytecode Alliance

),共同上司 WASI 标準、 WebAssembly 運作時、語言工具等工作。

WebAssembly 與 Kubernetes雙劍合璧

原圖:

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

WASM與容器相愛相殺

WebAssembly是否會取代容器?

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

WebAssembly 與 Kubernetes雙劍合璧

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

WebAssembly 與 Kubernetes雙劍合璧
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應用鏡像的建構和分發。

我提供了一個技術原型示例項目,

https://github.com/denverdino/wasm-container-samples

,大家可以參考其中的例子來建構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應用而言,如果它需要需要通路指定檔案等系統資源,需要從外部顯式傳入加有權限的檔案描述符引用,而不能通路任何其他未授權資源。這中依賴注入的方式可以避免傳統安全模型的潛在風險。一個示意圖如下

WebAssembly 與 Kubernetes雙劍合璧

我們可以看到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年初釋出了一個實驗項目, 

https://github.com/deislabs/krustlet

 來利用 Virtual Kubelet類似的架構排程 WebAssembly 應用。但是這個方式有很多局限,無法借助容器方式進行應用分發,也無法利用 K8s 的語義進行資源編排。 

難得有一個春節假期可以宅在家裡間,我基于Derek McGowan去年的一個實驗性項目

https://github.com/dmcgowan/containerd-wasm

,完善了containerd的WASM shim實作。可以讓containerd支援WASM container,并且可以利用Kubernetes叢集管理和排程 WASM container。

項目的代碼實作: 

https://github.com/denverdino/containerd-wasm

注:這個項目更多是概念驗證,程序管理、資源限制,性能優化等的細節并沒未完整實作。

整個系統的架構設計如下,“container-shim-wasm-v1”做為Containerd的擴充,利用

wasmer

作為WASM應用運作時環境,可以實作與runc容器一緻的使用者體驗。

WebAssembly 與 Kubernetes雙劍合璧

我們還會将其注冊為 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           

利用浏覽器打開上述位址,顯示如下

WebAssembly 與 Kubernetes雙劍合璧

建立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的分布式應用管理和運維。

後記

本文寫在2020年的春節期間,這個春節注定将會被所有人銘記。衆志成城,抗擊疫情!天佑中華,武漢加油!