自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

WebAssembly 在云原生中的實(shí)踐指南

開發(fā) 前端
WebAssembly 的初衷之一是解決 JavaScript 的性能問題,讓 Web 應(yīng)用程序能夠達(dá)到與本地原生應(yīng)用程序類似的性能。作為底層 VM 的通用、開放、高效的抽象,許多編程語言,例如C、C++ 和 Rust,都可以將現(xiàn)有應(yīng)用程序編譯成 Wasm 的目標(biāo)代碼,以便它們在瀏覽器中運(yùn)行。這將應(yīng)用程序開發(fā)技術(shù)與運(yùn)行時(shí)技術(shù)解耦,并大大提高了代碼的可重用性。

1 WebAssembly 介紹

WebAssembly(Wasm)是一種通用字節(jié)碼技術(shù),它可以將其他編程語言(如 Go、Rust、C/C++ 等)的程序代碼編譯為可在瀏覽器環(huán)境直接執(zhí)行的字節(jié)碼程序。

WebAssembly 的初衷之一是解決 JavaScript 的性能問題,讓 Web 應(yīng)用程序能夠達(dá)到與本地原生應(yīng)用程序類似的性能。作為底層 VM 的通用、開放、高效的抽象,許多編程語言,例如C、C++ 和 Rust,都可以將現(xiàn)有應(yīng)用程序編譯成 Wasm 的目標(biāo)代碼,以便它們在瀏覽器中運(yùn)行。這將應(yīng)用程序開發(fā)技術(shù)與運(yùn)行時(shí)技術(shù)解耦,并大大提高了代碼的可重用性。

圖片圖片

2019 年 3 月,Mozilla 推出了 WebAssembly 系統(tǒng)接口(Wasi),以標(biāo)準(zhǔn)化 WebAssembly 應(yīng)用程序與系統(tǒng)資源之間的交互抽象,例如文件系統(tǒng)訪問、內(nèi)存管理和網(wǎng)絡(luò)連接,該接口類似于 POSIX 等標(biāo)準(zhǔn) API。Wasi 規(guī)范的出現(xiàn)極大地?cái)U(kuò)展了 WebAssembly 的應(yīng)用場景,使得 Wasm 不僅限于在瀏覽器中運(yùn)行,而且可以在服務(wù)器端得到應(yīng)用。同時(shí),平臺開發(fā)者可以針對特定的操作系統(tǒng)和運(yùn)行環(huán)境提供 Wasi 接口的不同實(shí)現(xiàn),允許跨平臺的 WebAssembly 應(yīng)用程序運(yùn)行在不同的設(shè)備和操作系統(tǒng)上。

圖片圖片

2 WebAssembly 會取代容器嗎?

Docker 的創(chuàng)始人 Solomon Hykes 是這樣評價(jià) WASI 的:

如果 WASM+WASI 在 2008 年就存在,我們就不需要創(chuàng)建 Docker 了。這就是它的重要性。服務(wù)器上的 WebAssembly 是計(jì)算的未來。

圖片圖片

Solomon Hykes 后續(xù)還發(fā)布了一條推文,表示  WebAssembly 將與容器一起工作,而不是取代它們。WebAssembly 可以成為一種容器類型,類似于 Linux 容器或 Windows 容器。它將成為標(biāo)準(zhǔn)的跨平臺應(yīng)用程序分發(fā)和運(yùn)行時(shí)環(huán)境。

圖片圖片

3 WebAssembly 的優(yōu)勢

WebAssembly 相較于傳統(tǒng)的容器有著許多顯著的優(yōu)勢:

體積更小:WebAssembly 應(yīng)用程序比容器小,以下是兩個簡單的用于輸出文檔的應(yīng)用程序,都是使用標(biāo)準(zhǔn)工具構(gòu)建的,從下圖可以看出,Wasm 應(yīng)用程序比容器化應(yīng)用程序小了近 10 倍。

圖片圖片

  • 速度更快:WebAssembly 應(yīng)用程序的啟動速度可以比容器快 1000 倍,你可以在不到一毫秒的時(shí)間內(nèi)執(zhí)行應(yīng)用程序的第一條指令,有時(shí)甚至可以達(dá)到微秒級。這將使構(gòu)建可伸縮的應(yīng)用程序變得更加容易,當(dāng)請求達(dá)到峰值時(shí),應(yīng)用程序可以快速伸縮,當(dāng)請求下降到零且沒有流量時(shí),應(yīng)用程序不會浪費(fèi) CPU 或內(nèi)存。
  • 更加安全:WebAssembly 在沙箱環(huán)境中運(yùn)行,具有強(qiáng)大的安全性。它提供了一系列安全特性,如內(nèi)存隔離、類型檢查和資源限制,以防止惡意代碼執(zhí)行和訪問敏感信息。
  • 可移植性更好:容器的架構(gòu)限制了它們的可移植性。例如,針對 linux/amd64 構(gòu)建的容器無法在 linux/arm64 上運(yùn)行,也無法在 windows/amd64 或 windows/arm64 上運(yùn)行。這意味著組織需要為同一個應(yīng)用程序創(chuàng)建和維護(hù)多個鏡像,以適應(yīng)不同的操作系統(tǒng)和 CPU 架構(gòu)。而 WebAssembly 通過創(chuàng)建一個在可以任何地方運(yùn)行的單一 Wasm 模塊來解決這個問題。只需構(gòu)建一次 wasm32/wasi 的應(yīng)用程序,任何主機(jī)上的 Wasm 運(yùn)行時(shí)都可以執(zhí)行它。這意味著 WebAssembly 實(shí)現(xiàn)了一次構(gòu)建,到處運(yùn)行的承諾,不再需要為不同的操作系統(tǒng)和 CPU 架構(gòu)構(gòu)建和維護(hù)多個鏡像。

關(guān)于 WebAssembly 和容器詳細(xì)的對比,可以查看這個表格: WebAssembly vs Linux Container [1]。

4 使用 Rust 開發(fā) Wasm 應(yīng)用

是否可以將應(yīng)用程序編譯為 Wasm 在很大程度上取決于所使用的編程語言。Rust、C、C++ 等語言對 Wasm 有很好的支持。從 Go 1.21 版本開始,Go 官方也初步支持了 Wasi,之前需要使用第三方工具如 tinygo 進(jìn)行編譯。由于 Rust 對 Wasm 的一流支持以及無需 GC、零運(yùn)行時(shí)開銷的特點(diǎn),使其成為了開發(fā) Wasm 應(yīng)用的理想選擇。因此,本文選用 Rust 來開發(fā) Wasm 應(yīng)用程序。

4.1 安裝 Rust

執(zhí)行以下命令安裝 rustup,并通過 rustup 安裝 Rust 的最新穩(wěn)定版本,rustup 是用于管理 Rust 版本和工具鏈的命令行工具。

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

4.2 為 Rust 添加 wasm32-wasi 編譯目標(biāo)

前面提到過,Wasi(WebAssembly System Interface)是用于 WebAssembly 的系統(tǒng)級接口,旨在實(shí)現(xiàn) WebAssembly 在不同環(huán)境中與宿主系統(tǒng)交互。它提供標(biāo)準(zhǔn)化的方式,使得 WebAssembly 可以進(jìn)行文件 I/O、網(wǎng)絡(luò)操作和系統(tǒng)調(diào)用等系統(tǒng)級功能訪問。

rustc 本身是一個跨平臺的編譯器,其編譯的目標(biāo)有很多,具體可以通過 rustup target list 命令來查看。wasm32-wasi 是 Rust 的編譯目標(biāo)之一,用于將 Rust 代碼編譯為符合 Wasi 標(biāo)準(zhǔn)的 Wasm 模塊。通過將 Rust 代碼編譯為 wasm32-wasi 目標(biāo),可以將 Rust 的功能和安全性引入到 WebAssembly 環(huán)境中,同時(shí)利用 wasm32-wasi 提供的標(biāo)準(zhǔn)化系統(tǒng)接口實(shí)現(xiàn)與宿主系統(tǒng)的交互。

執(zhí)行以下命令,為 Rust 編譯器添加 wasm32-wasi 目標(biāo)。

rustup target add wasm32-wasi

4.3 編寫 Rust 程序

首先執(zhí)行以下命令構(gòu)建一個新的 Rust 項(xiàng)目。

cargo new http-server

編輯 Cargo.toml 添加如下依賴。這里我們使用 wrap_wasi 來開發(fā)一個簡單的 HTTP Server, warp_wasi 構(gòu)建在 Warp 框架之上,Warp 是一個輕量級的 Web 服務(wù)器框架,用于構(gòu)建高性能的異步 Web 應(yīng)用程序。

原生的 Warp 框架編寫的代碼無法直接編譯成 Wasm 模塊。因此我們可以使用 warp_wasi,通過它我們可以在 Rust 中利用 Wasi 接口來開發(fā) Web 應(yīng)用程序。

[dependencies]
tokio_wasi = { version = "1", features = ["rt", "macros", "net", "time", "io-util"]}
warp_wasi = "0.3"

編寫一個簡單的 HTTP Server,在 8080 端口暴露服務(wù),當(dāng)接收到請求時(shí)返回 Hello, World!。

use warp::Filter;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let hello = warp::get()
        .and(warp::path::end())
        .map(|| "Hello, World!");

    warp::serve(hello).run(([0, 0, 0, 0], 8080)).await;
}

執(zhí)行以下命令,將程序編譯為 Wasm 模塊。

cargo build --target wasm32-wasi --release

4.4 安裝 WasmEdge

編譯完成的 Wasm 模塊需要使用相應(yīng)的 Wasm 運(yùn)行時(shí)來運(yùn)行。常見的 Wasm 運(yùn)行時(shí)包括 WasmEdge、Wasmtime 和 Wasmer 等。

在這里,我們選擇使用 WasmEdge,它是一個輕量、高性能且可擴(kuò)展的 WebAssembly Runtime。執(zhí)行以下命令安裝 WasmEdge。

curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash

運(yùn)行以下命令以使已安裝的二進(jìn)制文件在當(dāng)前會話中可用。

source $HOME/.wasmedge/env

4.5 運(yùn)行 Wasm 模塊

使用 wasmedge 來運(yùn)行前面編譯好的 Wasm 模塊。

wasmedge target/wasm32-wasi/release/http-server.wasm

在本地通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

5 運(yùn)行 Wasm 工作負(fù)載

5.1 在 Linux 容器中運(yùn)行 Wasm 工作負(fù)載

在容器生態(tài)系統(tǒng)中運(yùn)行 Wasm 應(yīng)用程序最簡單的方法就是將 Wasm 模塊直接嵌入到 Linux 容器鏡像中。具體來說,我們可以將容器內(nèi)的 Linux 操作系統(tǒng)精簡到足夠支持 Wasmedge 運(yùn)行時(shí),然后通過 Wasmedge 來運(yùn)行 Wasm 模塊。由于 Wasm 應(yīng)用程序包裝在常規(guī)容器中,因此它可以與任何容器生態(tài)系統(tǒng)無縫地協(xié)作。通過這種方式,整個 Linux 操作系統(tǒng)和 Wasmedge 運(yùn)行時(shí)的內(nèi)存占用可以減少到僅為 4MB。

相較于常規(guī)的 Linux 操作系統(tǒng),精簡版的 Linux 操作系統(tǒng)大大減少了攻擊面。然而,這種方法仍然需要啟動 Linux 容器。即使是精簡版的 Linux 操作系統(tǒng),在鏡像大小上仍然占據(jù)了整個容器大小的 80%,因此仍然有很大的優(yōu)化空間。

接下來根據(jù)前面編寫的 Rust 代碼構(gòu)建出 Linux 容器鏡像。首先在前面創(chuàng)建的 http-server 項(xiàng)目根目錄下創(chuàng)建一個名為 Dockerfile-wasmedge-slim 的 Dockerfile,將編譯完成的 Wasm 模塊添加到安裝了 wasmedge 的精簡 linux 鏡像中,并指定通過 wasmedge 命令來啟動 Wasm 模塊。

FROM wasmedge/slim-runtime:0.10.1
COPY target/wasm32-wasi/release/http-server.wasm /
CMD ["wasmedge", "--dir", ".:/", "/http-server.wasm"]

執(zhí)行以下命令構(gòu)建容器鏡像。

docker build -f Dockerfile-wasmedge-slim -t cr7258/wasm-demo-app:slim .

啟動容器。

docker run -itd -p 8080:8080 \
--name wasm-demo-app \
docker.io/cr7258/wasm-demo-app:slim

在本地通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

5.2 在支持 Wasm 的容器運(yùn)行時(shí)中運(yùn)行 Wasm 工作負(fù)載

前面我們介紹了如何將 Wasm 模塊直接嵌入到 Linux 容器中來運(yùn)行 Wasm 工作負(fù)載,這種方式的好處就是可以無縫地與現(xiàn)有的環(huán)境進(jìn)行集成,同時(shí)享受到 Wasm 帶來的性能的提升。

然而這種方法的性能和安全性不如直接在支持 Wasm 的容器運(yùn)行時(shí)中運(yùn)行 Wasm 程序那么好。一般我們將容器運(yùn)行時(shí)分為高級運(yùn)行時(shí)和低級運(yùn)行時(shí):

  • 低級容器運(yùn)行時(shí) (Low level Container Runtime):一般指按照 OCI 規(guī)范實(shí)現(xiàn)的、能夠接收可運(yùn)行文件系統(tǒng)(rootfs) 和 配置文件(config.json)并運(yùn)行隔離進(jìn)程的實(shí)現(xiàn)。低級容器運(yùn)行時(shí)負(fù)責(zé)直接管理和運(yùn)行容器。常見的低級容器運(yùn)行時(shí)有:runc, crun, youki, gvisor, kata 等等。
  • 高級容器運(yùn)行時(shí) (High level Container Runtime):負(fù)責(zé)容器鏡像的傳輸和管理,將鏡像轉(zhuǎn)換為 rootfs 和 config.json,并將其傳遞給低級運(yùn)行時(shí)執(zhí)行。高級容器運(yùn)行時(shí)是對低級容器運(yùn)行時(shí)的抽象和封裝,為用戶提供了更簡單、易用的容器管理接口,隱藏了低級容器運(yùn)行時(shí)的復(fù)雜性。用戶可以使用同一種高級容器運(yùn)行時(shí)來管理不同的低級容器運(yùn)行時(shí)。常見的高級容器運(yùn)行時(shí)有:containerd, cri-o 等等。

以下是一個概念圖,可以幫助你了解高級和低級運(yùn)行時(shí)是如何協(xié)同工作的。

圖片圖片

接下來將會分別介紹如何通過高級和低級容器運(yùn)行時(shí)來運(yùn)行 Wasm 模塊,首先構(gòu)建一個 Wasm 鏡像。

5.2.1 構(gòu)建鏡像

在前面創(chuàng)建的 http-server 項(xiàng)目根目錄下創(chuàng)建一個 Dockerfile 文件,這次我們直接使用 scratch 空鏡像來構(gòu)建,scratch 是 Docker 中預(yù)留的最小的基礎(chǔ)鏡像。

FROM scratch
COPY target/wasm32-wasi/release/http-server.wasm /
CMD ["/http-server.wasm"]

執(zhí)行以下命令構(gòu)建容器鏡像。

docker build -t docker.io/cr7258/wasm-demo-app:v1 .

將鏡像推送到 Docker Hub 上,方便后續(xù)的實(shí)驗(yàn)使用。

# 登錄 Docker Hub
docker login
# 推送鏡像
docker push docker.io/cr7258/wasm-demo-app:v1

在 Docker Hub 上可以看到這次構(gòu)建的鏡像僅有 989.89 KB(壓縮后),大小僅有前面構(gòu)建的 wasm-demo-app:slim 鏡像的 1/4。

圖片圖片

5.2.2 低級容器運(yùn)行時(shí)

在 5.2.2 章節(jié)中將會介紹使用 crun 和 youki 這兩種低級容器運(yùn)行時(shí)在不依賴高級容器運(yùn)行時(shí)的情況下,使用準(zhǔn)備好的 config.json 和 rootfs 文件來直接啟動 Wasm 應(yīng)用。

5.2.2.1 Crun

crun 是用 C 編寫的快速輕量的 OCI 容器運(yùn)行時(shí),并且內(nèi)置了對 WasmEdge 的支持。本小節(jié)將演示如何通過 crun 來運(yùn)行 Wasm 模塊。

請確保按照 4.4 小節(jié)安裝好了 WasmEdge。

然后在 Ubuntu 系統(tǒng)上從源代碼來構(gòu)建它,執(zhí)行以下命令安裝編譯所需的依賴。

apt update
apt install -y make git gcc build-essential pkgconf libtool \
     libsystemd-dev libprotobuf-c-dev libcap-dev libseccomp-dev libyajl-dev \
     go-md2man libtool autoconf python3 automake

接下來,配置、構(gòu)建和安裝支持 WasmEdge 的 crun 二進(jìn)制文件。

git clone https://github.com/containers/crun
cd crun
./autogen.sh
./configure --with-wasmedge
make
make install

接下來,運(yùn)行 crun -v 檢查是否安裝成功??吹接?+WASM:wasmedge,說明已經(jīng)在 crun 中安裝了 WasmEdge 了。

crun -v

# 返回結(jié)果
crun version 1.8.5.0.0.0.23-3856
commit: 385654125154075544e83a6227557bfa5b1f8cc5
rundir: /run/crun
spec: 1.0.0
+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +WASM:wasmedge +YAJL

創(chuàng)建一個目錄來存放運(yùn)行容器所需的文件。

mkdir test-crun
cd test-crun
mkdir rootfs
# 將編譯好的 Wasm 模塊拷貝到 rootfs 目錄中,注意替換成自己對應(yīng)的目錄
cp ~/hands-on-lab/wasm/runtime/http-server/target/wasm32-wasi/release/http-server.wasm rootfs

使用 crun spec 命令生成默認(rèn)的 config.json 配置文件,然后進(jìn)行修改:

  • 1.在 args 中將 sh 替換為 /http-server.wasm。
  • 2.在 annotations 中添加 "module.wasm.image/variant": "compat",表明表明這是一個沒有 guest OS 的 WebAssembly 應(yīng)用程序。
  • 3.在 network namespace 中添加 "path": "/proc/1/ns/net",讓程序與宿主機(jī)共享網(wǎng)絡(luò) namespace,方便在本機(jī)進(jìn)行訪問。

修改完成后的配置文件如下:

{
 "ociVersion": "1.0.0",
 "process": {
  "terminal": true,
  "user": {
   "uid": 0,
   "gid": 0
  },
  "args": [
   "/http-server.wasm"
  ],
  "env": [
   "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
   "TERM=xterm"
  ],
  "cwd": "/",
  "capabilities": {
   "bounding": [
    "CAP_AUDIT_WRITE",
    "CAP_KILL",
    "CAP_NET_BIND_SERVICE"
   ],
   "effective": [
    "CAP_AUDIT_WRITE",
    "CAP_KILL",
    "CAP_NET_BIND_SERVICE"
   ],
   "inheritable": [
   ],
   "permitted": [
    "CAP_AUDIT_WRITE",
    "CAP_KILL",
    "CAP_NET_BIND_SERVICE"
   ],
   "ambient": [
    "CAP_AUDIT_WRITE",
    "CAP_KILL",
    "CAP_NET_BIND_SERVICE"
   ]
  },
  "rlimits": [
   {
    "type": "RLIMIT_NOFILE",
    "hard": 1024,
    "soft": 1024
   }
  ],
  "noNewPrivileges": true
 },
 "root": {
  "path": "rootfs",
  "readonly": true
 },
 "hostname": "crun",
 "mounts": [
  {
   "destination": "/proc",
   "type": "proc",
   "source": "proc"
  },
  {
   "destination": "/dev",
   "type": "tmpfs",
   "source": "tmpfs",
   "options": [
    "nosuid",
    "strictatime",
    "mode=755",
    "size=65536k"
   ]
  },
  {
   "destination": "/dev/pts",
   "type": "devpts",
   "source": "devpts",
   "options": [
    "nosuid",
    "noexec",
    "newinstance",
    "ptmxmode=0666",
    "mode=0620",
    "gid=5"
   ]
  },
  {
   "destination": "/dev/shm",
   "type": "tmpfs",
   "source": "shm",
   "options": [
    "nosuid",
    "noexec",
    "nodev",
    "mode=1777",
    "size=65536k"
   ]
  },
  {
   "destination": "/dev/mqueue",
   "type": "mqueue",
   "source": "mqueue",
   "options": [
    "nosuid",
    "noexec",
    "nodev"
   ]
  },
  {
   "destination": "/sys",
   "type": "sysfs",
   "source": "sysfs",
   "options": [
    "nosuid",
    "noexec",
    "nodev",
    "ro"
   ]
  },
  {
   "destination": "/sys/fs/cgroup",
   "type": "cgroup",
   "source": "cgroup",
   "options": [
    "nosuid",
    "noexec",
    "nodev",
    "relatime",
    "ro"
   ]
  }
 ],
 "annotations": {
  "module.wasm.image/variant": "compat"
 },
 "linux": {
  "resources": {
   "devices": [
    {
     "allow": false,
     "access": "rwm"
    }
   ]
  },
  "namespaces": [
   {
    "type": "pid"
   },
   {
    "type": "network",
    "path": "/proc/1/ns/net"
   },
   {
    "type": "ipc"
   },
   {
    "type": "uts"
   },
   {
    "type": "cgroup"
   },
   {
    "type": "mount"
   }
  ],
  "maskedPaths": [
   "/proc/acpi",
   "/proc/asound",
   "/proc/kcore",
   "/proc/keys",
   "/proc/latency_stats",
   "/proc/timer_list",
   "/proc/timer_stats",
   "/proc/sched_debug",
   "/sys/firmware",
   "/proc/scsi"
  ],
  "readonlyPaths": [
   "/proc/bus",
   "/proc/fs",
   "/proc/irq",
   "/proc/sys",
   "/proc/sysrq-trigger"
  ]
 }
}

通過 crun 啟動容器。

crun run wasm-demo-app

在本地通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

如果想要停止并刪除容器,可以執(zhí)行以下命令。

crun kill wasm-demo-app SIGKILL
5.2.2.2 Youki

youki 是一個使用 Rust 編寫的符合 OCI 規(guī)范的容器運(yùn)行時(shí)。相較于 C,Rust 的使用帶來了內(nèi)存安全的優(yōu)勢。和 crun 一樣,Youki 同樣支持了 WasmEdge。

請確保按照 4.1 小節(jié)安裝好了 Rust。

然后 Ubuntu 系統(tǒng)上從源代碼來構(gòu)建它,執(zhí)行以下命令安裝編譯所需的依賴。

apt-get update
sudo apt-get -y install    \
      pkg-config          \
      libsystemd-dev      \
      libdbus-glib-1-dev  \
      build-essential     \
      libelf-dev          \
      libseccomp-dev      \
      libclang-dev        \
      libssl-dev

執(zhí)行以下命令編譯支持 WasmEdge 的 youki 二進(jìn)制文件。

./scripts/build.sh -o . -r -f wasm-wasmedge

指定 wasm-wasmedge 參數(shù)將在 $HOME/.wasmedge 目錄中安裝 WasmEdge 運(yùn)行時(shí)庫。要使該庫在系統(tǒng)中可用,請運(yùn)行以下命令:

export LD_LIBRARY_PATH=$HOME/.wasmedge/lib

或者:

source $HOME/.wasmedge/env

最后將編譯完成后的 youki 文件移動到任意 $PATH 所包含的目錄。

mv youki /usr/local/bin

創(chuàng)建一個目錄來存放運(yùn)行容器所需的文件。

mkdir test-youki
cd test-youki
mkdir rootfs
# 將編譯好的 Wasm 模塊拷貝到 rootfs 目錄中,注意替換成自己對應(yīng)的目錄
cp ~/hands-on-lab/wasm/runtime/http-server/target/wasm32-wasi/release/http-server.wasm rootfs

使用 youki spec 命令生成默認(rèn)的 config.json 配置文件,然后進(jìn)行修改,和前面修改 crun 配置文件的內(nèi)容是一樣的:

  • 1.在 args 中將 sh 替換為 /http-server.wasm。
  • 2.在 annotations 中添加 "module.wasm.image/variant": "compat",表明表明這是一個沒有 guest OS 的 WebAssembly 應(yīng)用程序。
  • 3.在 network namespace 中添加 "path": "/proc/1/ns/net",讓程序與宿主機(jī)共享網(wǎng)絡(luò) namespace,方便在本機(jī)進(jìn)行訪問。

修改完成后的配置文件如下:

{
  "ociVersion": "1.0.2-dev",
  "root": {
    "path": "rootfs",
    "readonly": true
  },
  "mounts": [
    {
      "destination": "/proc",
      "type": "proc",
      "source": "proc"
    },
    {
      "destination": "/dev",
      "type": "tmpfs",
      "source": "tmpfs",
      "options": [
        "nosuid",
        "strictatime",
        "mode=755",
        "size=65536k"
      ]
    },
    {
      "destination": "/dev/pts",
      "type": "devpts",
      "source": "devpts",
      "options": [
        "nosuid",
        "noexec",
        "newinstance",
        "ptmxmode=0666",
        "mode=0620",
        "gid=5"
      ]
    },
    {
      "destination": "/dev/shm",
      "type": "tmpfs",
      "source": "shm",
      "options": [
        "nosuid",
        "noexec",
        "nodev",
        "mode=1777",
        "size=65536k"
      ]
    },
    {
      "destination": "/dev/mqueue",
      "type": "mqueue",
      "source": "mqueue",
      "options": [
        "nosuid",
        "noexec",
        "nodev"
      ]
    },
    {
      "destination": "/sys",
      "type": "sysfs",
      "source": "sysfs",
      "options": [
        "nosuid",
        "noexec",
        "nodev",
        "ro"
      ]
    },
    {
      "destination": "/sys/fs/cgroup",
      "type": "cgroup",
      "source": "cgroup",
      "options": [
        "nosuid",
        "noexec",
        "nodev",
        "relatime",
        "ro"
      ]
    }
  ],
  "process": {
    "terminal": false,
    "user": {
      "uid": 0,
      "gid": 0
    },
    "args": [
      "/http-server.wasm"
    ],
    "env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
      "TERM=xterm"
    ],
    "cwd": "/",
    "capabilities": {
      "bounding": [
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE",
        "CAP_AUDIT_WRITE"
      ],
      "effective": [
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE",
        "CAP_AUDIT_WRITE"
      ],
      "inheritable": [
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE",
        "CAP_AUDIT_WRITE"
      ],
      "permitted": [
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE",
        "CAP_AUDIT_WRITE"
      ],
      "ambient": [
        "CAP_KILL",
        "CAP_NET_BIND_SERVICE",
        "CAP_AUDIT_WRITE"
      ]
    },
    "rlimits": [
      {
        "type": "RLIMIT_NOFILE",
        "hard": 1024,
        "soft": 1024
      }
    ],
    "noNewPrivileges": true
  },
  "hostname": "youki",
  "annotations": {
     "module.wasm.image/variant": "compat"
  },
  "linux": {
    "resources": {
      "devices": [
        {
          "allow": false,
          "access": "rwm"
        }
      ]
    },
    "namespaces": [
      {
        "type": "pid"
      },
      {
        "type": "network",
        "path": "/proc/1/ns/net"
      },
      {
        "type": "ipc"
      },
      {
        "type": "uts"
      },
      {
        "type": "mount"
      },
      {
        "type": "cgroup"
      }
    ],
    "maskedPaths": [
      "/proc/acpi",
      "/proc/asound",
      "/proc/kcore",
      "/proc/keys",
      "/proc/latency_stats",
      "/proc/timer_list",
      "/proc/timer_stats",
      "/proc/sched_debug",
      "/sys/firmware",
      "/proc/scsi"
    ],
    "readonlyPaths": [
      "/proc/bus",
      "/proc/fs",
      "/proc/irq",
      "/proc/sys",
      "/proc/sysrq-trigger"
    ]
  }
}

通過 youki 啟動容器。

youki run wasm-demo-app

在本地通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

如果想要停止并刪除容器,可以執(zhí)行以下命令。

youki kill wasm-demo-app SIGKILL

5.2.3 高級容器運(yùn)行時(shí)

在高級容器運(yùn)行時(shí)中,使用不同的 shim 來對接各種低級容器運(yùn)行時(shí)。在本節(jié)中,我們將以 containerd 為例進(jìn)行介紹。containerd shim 充當(dāng) containerd 和低級容器運(yùn)行時(shí)之間的橋梁,其主要功能是抽象了底層運(yùn)行時(shí)的細(xì)節(jié),使 containerd 能夠統(tǒng)一地管理各種運(yùn)行時(shí)。在 5.3 章節(jié)中將會介紹兩種 containerd 管理 Wasm 工作負(fù)載的方式:

  • containerd 使用 crun, youki 這兩種支持 WasmEdge 的不同的低級容器運(yùn)行時(shí)來管理 Wasm 模塊。(當(dāng)然這兩個運(yùn)行時(shí)也可以運(yùn)行普通的 Linux 容器)
  • containerd 通過 containerd-wasm-shim 直接通過 Wasm 運(yùn)行時(shí)來管理 Wasm 模塊。

圖片圖片

5.2.3.1 Containerd + Crun

請確保按照 5.2.2.1 小節(jié)安裝好了 crun。

使用以下命令安裝 containerd。

export VERSION="1.7.3"
sudo apt install -y libseccomp2
sudo apt install -y wget

wget https://github.com/containerd/containerd/releases/download/v${VERSION}/cri-containerd-cni-${VERSION}-linux-amd64.tar.gz
wget https://github.com/containerd/containerd/releases/download/v${VERSION}/cri-containerd-cni-${VERSION}-linux-amd64.tar.gz.sha256sum
sha256sum --check cri-containerd-cni-${VERSION}-linux-amd64.tar.gz.sha256sum

sudo tar --no-overwrite-dir -C / -xzf cri-containerd-cni-${VERSION}-linux-amd64.tar.gz
sudo systemctl daemon-reload
sudo systemctl start containerd

然后我們可以通過 containerd 運(yùn)行 Wasm 程序:

  • --runc-binary:指定使用 crun 來啟動容器。
  • --runtime:指定 shim 的版本和名稱,這將由 containerd 轉(zhuǎn)換為 shim 的二進(jìn)制名稱,io.containerd.runc.v2 -> containerd-shim-runc-v2。containerd 會執(zhí)行 containerd-shim-runc-v2 二進(jìn)制文件來啟動 shim,真正啟動容器是通過 containerd-shim-runc-v2 去調(diào)用 crun 來啟動容器的。
  • --label:添加 "module.wasm.image/variant": "compat",表明表明這是一個沒有 guest OS 的 WebAssembly 應(yīng)用程序。
# 先拉取鏡像
ctr i pull docker.io/cr7258/wasm-demo-app:v1 

# 啟動容器
ctr run --rm --net-host \
--runc-binary crun \
--runtime io.containerd.runc.v2 \
--label module.wasm.image/variant=compat \
docker.io/cr7258/wasm-demo-app:v1 \
wasm-demo-app

在本地通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

如果想要停止并刪除容器,可以執(zhí)行以下命令。

ctr task kill wasm-demo-app --signal SIGKILL
5.2.3.2 Containerd + Youki

請確保按照 5.2.2.2 小節(jié)安裝好了 youki。

我們可以通過 containerd 運(yùn)行 Wasm 程序,并指定使用 youki 來啟動容器。

ctr run --rm --net-host \
--runc-binary youki \
--runtime io.containerd.runc.v2 \
--label module.wasm.image/variant=compat \
docker.io/cr7258/wasm-demo-app:v1 wasm-demo-app

在本地通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

如果想要停止并刪除容器,可以執(zhí)行以下命令。

ctr task kill wasm-demo-app --signal SIGKILL
5.2.3.3 Containerd + Runwasi

runwasi 是一個用 Rust 編寫的庫,屬于 containerd 的子項(xiàng)目,使用 runwasi 可以編寫用于對接 Wasm 運(yùn)行時(shí)的 containerd wasm shim,通過 Wasm 運(yùn)行時(shí)可以管理 Wasm 工作負(fù)載。當(dāng)前使用 runwasi 編寫的 containerd wasm shim 有以下幾個:

  • 在 runwasi [2] 倉庫中包含了 WasmEdge 和 Wasmtime 兩種 containerd wasm shim 的實(shí)現(xiàn)。
  • 在 containerd-wasm-shims [3] 倉庫中包含了 Spin, Slight (SpiderLightning), Wasm Workers Server (wws), lunatic 四種 containerd wasm shim 的實(shí)現(xiàn)。

圖片圖片

我們直接使用 runwasi 提供的 wasmedge shim 來運(yùn)行 Wasm 應(yīng)用,首先克隆 runwasi 倉庫。

git clone https://github.com/containerd/runwasi.git
cd runwasi

然后安裝編譯所需的依賴。

sudo apt-get -y install    \
      pkg-config          \
      libsystemd-dev      \
      libdbus-glib-1-dev  \
      build-essential     \
      libelf-dev          \
      libseccomp-dev      \
      libclang-dev        \
      libssl-dev

執(zhí)行以下命令編譯文件。

make build
sudo make install

然后我們使用 containerd 通過 WasmEdge shim 來運(yùn)行 Wasm 應(yīng)用:

  • --runtime: 指定使用 io.containerd.wasmedge.v1 來運(yùn)行 Wasm 應(yīng)用。
ctr run --rm --net-host \
--runtime=io.containerd.wasmedge.v1 \
docker.io/cr7258/wasm-demo-app:v1 \
wasm-demo-app

在本地通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

如果想要停止并刪除容器,可以執(zhí)行以下命令。

ctr task kill wasm-demo-app --signal SIGKILL

5.3 在編排平臺運(yùn)行 Wasm 工作負(fù)載

5.3.1 Docker Desktop 運(yùn)行 Wasm

Docker Desktop 也使用了 runwasi 來支持 Wasm 工作負(fù)載,要在 Docker Desktop 中運(yùn)行 Wasm 工作負(fù)載需要確保勾選以下兩個選項(xiàng):

  • Use containerd for storing and pulling images
  • Enable Wasm

圖片圖片

點(diǎn)擊 Apply & restart 應(yīng)用更新,Docker Desktop 會下載并安裝以下可用于運(yùn)行 Wasm 工作負(fù)載的運(yùn)行時(shí):

  • io.containerd.slight.v1
  • io.containerd.spin.v1
  • io.containerd.wasmedge.v1
  • io.containerd.wasmtime.v1

在 Docker 中運(yùn)行 WebAssembly 應(yīng)用的方式與普通的 Linux 容器沒有太大區(qū)別,只需要通過 --runtime=io.containerd.wasmedge.v1 指定使用相應(yīng)的 Wasm 運(yùn)行時(shí)即可。

docker run -d -p 8080:8080 \
--name=wasm-demo-app \
--runtime=io.containerd.wasmedge.v1 \
docker.io/cr7258/wasm-demo-app:v1

在本地通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

如果想要停止并刪除容器,可以執(zhí)行以下命令。

docker rm -f wasm-demo-app

5.3.2 在 Kubernetes 中運(yùn)行 Wasm 模塊

Kubernetes 作為容器編排領(lǐng)域的事實(shí)標(biāo)準(zhǔn),WebAssembly 正在推動云計(jì)算的第三次浪潮 [4],而 Kubernetes 正在不斷發(fā)展以利用這一優(yōu)勢。

在 Kubernetes 中運(yùn)行 Wasm 工作負(fù)載有兩種方式:

  • 1.首先,我們需要使集群中節(jié)點(diǎn)的容器運(yùn)行時(shí)支持運(yùn)行 Wasm 工作負(fù)載。接下來,可以通過使用 RuntimeClass,將 Pod 調(diào)度到指定節(jié)點(diǎn)并指定特定的運(yùn)行時(shí)。在 RuntimeClass 中通過 handler 字段指定運(yùn)行 Wasm 工作負(fù)載的 handler,可以是支持 Wasm 的低級容器運(yùn)行時(shí)(例如 crun, youki),也可以是 Wasm 運(yùn)行時(shí);通過 scheduling.nodeSelector 指定將工作負(fù)載調(diào)度到含有特定標(biāo)簽的節(jié)點(diǎn)。

圖片圖片

  • 2.將專門用于運(yùn)行 Wasm 的特殊節(jié)點(diǎn)(Krustlet)加入集群,通過標(biāo)簽選擇器在調(diào)度時(shí)將 Wasm 工作負(fù)載指定到 Krustlet 節(jié)點(diǎn)。

Kind(Kubernetes in Docker) 是一個使用 Docker 容器運(yùn)行本地 Kubernetes 集群的工具。為了方便實(shí)驗(yàn),在 5.3.2 章節(jié)中將使用 Kind 來創(chuàng)建 Kubernetes 集群。使用以下命令安裝 Kind。

[ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

Kubectl 是用于管理 Kubernetes 集群的命令行工作,執(zhí)行以下命令安裝 Kubectl。

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/kubectl
5.3.2.1 Kubernetes + Containerd + Crun

使用以下命令創(chuàng)建一個單節(jié)點(diǎn)的 Kubernetes 集群。

kind create cluster --name wasm-demo

每個 Kubernetes Node 都是一個 Docker 容器,通過 docker exec 命令進(jìn)入該節(jié)點(diǎn)。

docker exec -it  wasm-demo-control-plane bash

進(jìn)入節(jié)點(diǎn)后,請確保按照 5.2.2.1 小節(jié)安裝好了 crun。

修改 containerd 配置文件 /etc/containerd/config.toml,在文件末尾添加以下內(nèi)容。

  • 配置 crun 作為 containerd 的運(yùn)行時(shí) handler。格式是 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]。
  • pod_annotations 表示允許把 Pod metadata 中設(shè)置的 Annotation module.wasm.image/variant 傳遞給 crun,因?yàn)?crun 需要通過這個 Annotation 來判斷這是一個 Wasm 工作負(fù)載。
cat >> /etc/containerd/config.toml << EOF
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.crun]
    runtime_type = "io.containerd.runc.v2"
    pod_annotations = ["module.wasm.image/variant"]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.crun.options]
    BinaryName = "crun"
EOF

然后重啟 containerd。

systemctl restart containerd

創(chuàng)建一個名為 crun 的 RuntimeClass 資源,并使用之前在 containerd 中設(shè)置的 crun handler。接下來,在 Pod Spec 中指定 runtimeClassName 來使用該 RuntimeClass,以告知 kubelet 使用所指定的 RuntimeClass 來運(yùn)行該 Pod。此外,設(shè)置 Annotation module.wasm.image/variant: compat,告訴 crun 這是一個 Wasm 工作負(fù)載。

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: crun
handler: crun
---
apiVersion: v1
kind: Pod
metadata:
  name: wasm-demo-app
  annotations:
    module.wasm.image/variant: compat
spec:
  runtimeClassName: crun
  containers:
  - name: wasm-demo-app
    image: docker.io/cr7258/wasm-demo-app:v1

可以通過 port-forward 將端口轉(zhuǎn)發(fā)到本地進(jìn)行訪問。

kubectl port-forward pod/wasm-demo-app 8080:8080

然后在另一個終端通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

測試完畢后,銷毀該集群。

kind delete cluster --name wasm-demo
5.3.2.2 KWasm Operator

Kwasm 是一個 Kubernetes Operator,可以為 Kubernetes 節(jié)點(diǎn)添加 WebAssembly 支持。當(dāng)你想為某個節(jié)點(diǎn)增加 Wasm 支持時(shí),只需為該節(jié)點(diǎn)添加 kwasm.sh/kwasm-node=true 的 Annotation 。隨后,Kwasm 會自動創(chuàng)建一個 Job,負(fù)責(zé)在該節(jié)點(diǎn)上部署運(yùn)行 Wasm 所需的二進(jìn)制文件,并對 containerd 的配置進(jìn)行相應(yīng)的修改。

圖片圖片

使用以下命令創(chuàng)建一個單節(jié)點(diǎn)的 Kubernetes 集群。

kind create cluster --name kwasm-demo

Kwasm 提供了 Helm chart 方便用戶進(jìn)行安裝,先執(zhí)行以下命令安裝 Helm。

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

然后安裝 Kwasm Operator,為所有節(jié)點(diǎn)添加 Annotation kwasm.sh/kwasm-node=true 啟用對 Wasm 的支持。

# 添加 Helm repo
helm repo add kwasm http://kwasm.sh/kwasm-operator/
# 安裝 KWasm operator
helm install -n kwasm --create-namespace kwasm-operator kwasm/kwasm-operator
# 為節(jié)點(diǎn)添加 Wasm 支持
kubectl annotate node --all kwasm.sh/kwasm-node=true

創(chuàng)建一個名為 crun 的 RuntimeClass 資源,并使用之前在 containerd 中設(shè)置的 crun handler。接下來,在 Pod Spec 中指定 runtimeClassName 來使用該 RuntimeClass,以告知 kubelet 使用所指定的 RuntimeClass 來運(yùn)行該 Pod。此外,設(shè)置 Annotation module.wasm.image/variant: compat,告訴 crun 這是一個 Wasm 工作負(fù)載。

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: crun
handler: crun
---
apiVersion: v1
kind: Pod
metadata:
  name: wasm-demo-app
  annotations:
    module.wasm.image/variant: compat
spec:
  runtimeClassName: crun
  containers:
  - name: wasm-demo-app
    image: docker.io/cr7258/wasm-demo-app:v1

Pod 運(yùn)行成功后,可以通過 port-forward 將端口轉(zhuǎn)發(fā)到本地進(jìn)行訪問。

kubectl port-forward pod/wasm-demo-app 8080:8080

我們在另一個終端通過 curl 命令訪問該服務(wù)。

curl http://localhost:8080
Hello, World!

測試完畢后,銷毀該集群。

kind delete cluster --name kwasm-demo
5.3.2.3 Krustlet

Krustlet 是一個由 Rust 語言編寫的 kubelet,它在 Kubernetes 集群中作為一個節(jié)點(diǎn),專門用于運(yùn)行 Wasm 工作負(fù)載。當(dāng) Kubernetes 調(diào)度器將 Pod 調(diào)度到 Krustlet 節(jié)點(diǎn)時(shí),Krustlet 會利用 Wasm 運(yùn)行時(shí)來啟動相應(yīng)的 Wasm 工作負(fù)載。盡管 Krustlet 項(xiàng)目目前已經(jīng)很久沒有更新了,但是還是值得了解一番。

使用以下命令創(chuàng)建一個單節(jié)點(diǎn)的 Kubernetes 集群。這里通過 --image 參數(shù)指定創(chuàng)建 1.21.14 版本的 Kubernetes 集群,Krustlet 最近一次更新還是在去年,可能不兼容最新的 Kubernetes 版本。我在最新的 Kubernetes 集群上測試后,發(fā)現(xiàn) Krustlet 無法正常工作。

kind create cluster --name krustlet-demo --image kindest/node:v1.21.14@sha256:8a4e9bb3f415d2bb81629ce33ef9c76ba514c14d707f9797a01e3216376ba093

接下來我們需要啟動一個 Krustlet 節(jié)點(diǎn),并將它加入集群。對于普通的節(jié)點(diǎn),我們可以使用 kubeadm join 命令很方便的將節(jié)點(diǎn)加入集群。因?yàn)?kubeadm 會替你做很多事,例如生成 bootstrap token,生成 kubelet 證書等等。

對于 Krustlet 節(jié)點(diǎn)我們就需要手動處理這些事情了,我們可以使用 Krustlet 官方準(zhǔn)備的腳本。這個腳本會為我們創(chuàng)建 bootstrap token,這個 token 是 Krustlet 初始化時(shí)和 API Server 臨時(shí)通信而使用的。腳本還會根據(jù) token 生成 Krustlet 臨時(shí)的 kubeconfig 文件,默認(rèn)在 console ~/.krustlet/config/kubeconfig。

```bash
bash <(curl https://raw.githubusercontent.com/krustlet/krustlet/main/scripts/bootstrap.sh)

接著執(zhí)行以下命令安裝 Krustlet 二進(jìn)制文件。

wget https://krustlet.blob.core.windows.net/releases/krustlet-v1.0.0-alpha.1-linux-amd64.tar.gz
tar -xzvf krustlet-v1.0.0-alpha.1-linux-amd64.tar.gz
mv krustlet-wasi /usr/local/bin/krustlet-wasi

最后,運(yùn)行以下命令來啟動 Krustlet:

  • --node-ip:指定 Krustlet 的節(jié)點(diǎn) IP,通常情況下 docker0 網(wǎng)卡的地址是 172.17.0.1,我們在本機(jī)啟動的 Krustlet 要和 Kind 啟動的 Kubernetes 集群進(jìn)行通信,因此選擇將 Krustlet 程序綁定在 docker0 所在的地址上??梢允褂?nbsp;ip addr show docker0 命令來確認(rèn) docker0 網(wǎng)卡的地址。
  • --node-name:指定 Krustlet 的節(jié)點(diǎn)名。
  • --bootstrap-file:指定前面通過腳本生成的 Krustlet 臨時(shí)的 kubeconfig 的文件路徑。
  • KUBECONFIG=~/.krustlet/config/kubeconfig:執(zhí)行該命令的時(shí)候,這個 kubeconfig 文件還沒有生成,Krustlet 會在引導(dǎo)過程中生成私鑰和證書,并創(chuàng)建 CSR 資源,當(dāng) CSR 被批準(zhǔn)后,Krustlet 在該路徑創(chuàng)建長期可用的 kubeconfig 文件,其中包含密鑰和已簽名的證書。
KUBECONFIG=~/.krustlet/config/kubeconfig \
krustlet-wasi \
--node-ip 172.17.0.1 \
--node-name=krustlet \
--bootstrap-file=${HOME}/.krustlet/config/bootstrap.conf

啟動 Krustlet 后,提示我們需要手動批準(zhǔn) CSR 請求。當(dāng)然我們也可以設(shè)置自動批準(zhǔn),這里先不展開說明。

BOOTSTRAP: TLS certificate requires manual approval. Run kubectl certificate approve instance-2-tls

執(zhí)行以下命令,手動批準(zhǔn) CSR 請求。我們只需要在 Krustlet 第一次啟動時(shí)執(zhí)行此步驟,之后它會將所需的憑證保存下來。

kubectl certificate approve instance-2-tls

然后查看節(jié)點(diǎn),就可以看到 Krustlet 節(jié)點(diǎn)已經(jīng)成功注冊到 Kubernetes 集群中了。

# kubectl get nodes -o wide
NAME                          STATUS   ROLES                  AGE     VERSION         INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION    CONTAINER-RUNTIME
krustlet                      Ready    <none>                 30s     1.0.0-alpha.1   172.17.0.1    <none>        <unknown>                        <unknown>         mvp
krustlet-demo-control-plane   Ready    control-plane,master   4m17s   v1.21.14        172.18.0.2    <none>        Debian GNU/Linux 11 (bullseye)   5.19.0-1030-gcp   containerd://1.7.1

查看節(jié)點(diǎn)信息,其架構(gòu)顯示是 wasm-wasi,并且節(jié)點(diǎn)上有 kubernetes.io/arch=wasm32-wasi:NoExecute 和 kubernetes.io/arch=wasm32-wasi:NoSchedule 兩個污點(diǎn),我們在創(chuàng)建 Pod 時(shí)需要指定容忍該污點(diǎn)才能調(diào)度到 Krustlet 節(jié)點(diǎn)上。

# kubectl describe node krustlet
Name:               krustlet
Roles:              <none>
Labels:             beta.kubernetes.io/arch=wasm32-wasi
                    beta.kubernetes.io/os=wasm32-wasi
                    kubernetes.io/arch=wasm32-wasi
                    kubernetes.io/hostname=instance-2
                    kubernetes.io/os=wasm32-wasi
                    type=krustlet
Annotations:        node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Tue, 29 Aug 2023 02:55:19 +0000
Taints:             kubernetes.io/arch=wasm32-wasi:NoExecute
                    kubernetes.io/arch=wasm32-wasi:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  krustlet
  AcquireTime:     Tue, 29 Aug 2023 02:55:49 +0000
  RenewTime:       Tue, 29 Aug 2023 02:55:49 +0000
Conditions:
  Type        Status  LastHeartbeatTime                 LastTransitionTime                Reason                     Message
  ----        ------  -----------------                 ------------------                ------                     -------
  Ready       True    Tue, 29 Aug 2023 02:55:49 +0000   Tue, 29 Aug 2023 02:55:19 +0000   KubeletReady               kubelet is posting ready status
  OutOfDisk   False   Tue, 29 Aug 2023 02:55:19 +0000   Tue, 29 Aug 2023 02:55:19 +0000   KubeletHasSufficientDisk   kubelet has sufficient disk space available
Addresses:
  InternalIP:  172.17.0.1
  Hostname:    instance-2
Capacity:
  cpu:                4
  ephemeral-storage:  61255492Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             4032800Ki
  pods:               110
Allocatable:
  cpu:                4
  ephemeral-storage:  61255492Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             4032800Ki
  pods:               110
System Info:
  Machine ID:
  System UUID:
  Boot ID:
  Kernel Version:
  OS Image:
  Operating System:           linux
  Architecture:               wasm-wasi
  Container Runtime Version:  mvp
  Kubelet Version:            1.0.0-alpha.1
  Kube-Proxy Version:         v1.17.0
PodCIDR:                      10.244.0.0/24
PodCIDRs:                     10.244.0.0/24
Non-terminated Pods:          (0 in total)
  Namespace                   Name    CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----    ------------  ----------  ---------------  -------------  ---
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests  Limits
  --------           --------  ------
  cpu                0 (0%)    0 (0%)
  memory             0 (0%)    0 (0%)
  ephemeral-storage  0 (0%)    0 (0%)
  hugepages-1Gi      0 (0%)    0 (0%)
  hugepages-2Mi      0 (0%)    0 (0%)
Events:
  Type    Reason          Age   From             Message
  ----    ------          ----  ----             -------
  Normal  RegisteredNode  36s   node-controller  Node krustlet event: Registered Node krustlet in Controller

和前面直接在容器運(yùn)行時(shí)里運(yùn)行 Wasm 鏡像不同,Krustlet 只支持 media types 是 application/vnd.wasm.config.v1+json 的 OCI 鏡像,我們之前構(gòu)建的鏡像的 media types 是 application/vnd.oci.image.layer.v1.tar+gzip。詳情參見:Open Containers Initiative [5]。

因此我們需要使用 wasm-to-oci 這個工具來構(gòu)建鏡像,wasm-to-oci 是一個用于將 Wasm 模塊發(fā)布到注冊表的工具,它打包模塊并將其上傳到注冊表。執(zhí)行以下命令,安裝 wasm-to-oci。

wget https://github.com/engineerd/wasm-to-oci/releases/download/v0.1.2/linux-amd64-wasm-to-oci
mv linux-amd64-wasm-to-oci /usr/local/bin/wasm-to-oci
chmod +x /usr/local/bin/wasm-to-oci

當(dāng)前暫不支持將 Wasm 模塊直接推送到 Docker Hub 上,因此這里我們選擇使用 GitHub Package Registry [6] 來存放 Wasm 模塊。

docker login ghcr.io
Username:  # Github 用戶名
Password:  # Github Token

另外由于 Krustlet 是基于 wasmtime 來運(yùn)行 Wasm 工作負(fù)載的,并且 wasmitime 目前暫不支持 HTTP,詳情參見:WASI Proposals Support [7]。

因此我們這里寫一個簡單的打印 Hello, World 的 Rust 程序。執(zhí)行以下命令構(gòu)建一個新的 Rust 項(xiàng)目。

cargo new hello-world

然后在 main.rs 文件中添加以下代碼。

use std::thread;
use std::time::Duration;

fn main() {
    loop {
        println!("Hello, World!");
        thread::sleep(Duration::from_secs(1));
    }
}

執(zhí)行以下命令,將程序編譯為 Wasm 模塊。

cargo build --target wasm32-wasi --release

使用 wasm-to-oci 將編譯好的 Wasm 模塊上傳到 GitHub Package Registry。

wasm-to-oci push target/wasm32-wasi/release/hello-world.wasm ghcr.io/cr7258/wasm-demo-app:oci

可以看到鏡像的 media types 是 application/vnd.wasm.config.v1+json。

圖片圖片

為了方便測試,我們將鏡像設(shè)置為公開的。

圖片圖片

然后創(chuàng)建 Pod 使用該鏡像,添加容忍運(yùn)行調(diào)度到 Krustlet 節(jié)點(diǎn)上,由于我們的 Kubernetes 集群中只有一個節(jié)點(diǎn),因此不用設(shè)置節(jié)點(diǎn)選擇器。

apiVersion: v1
kind: Pod
metadata:
  name: wasm-demo-app
spec:
  containers:
    - name: wasm-demo-app
      image: ghcr.io/cr7258/wasm-demo-app:oci
  tolerations:
    - key: "kubernetes.io/arch"
      operator: "Equal"
      value: "wasm32-wasi"
      effect: "NoExecute"
    - key: "kubernetes.io/arch"
      operator: "Equal"
      value: "wasm32-wasi"
      effect: "NoSchedule"

查看 Pod 日志可以看到每隔 1s 打印 Hello, World!。

kubectl logs wasm-demo-app

Hello, World!
Hello, World!
Hello, World!

測試完畢后,銷毀該集群。

kind delete cluster --name krustlet-demo

6 總結(jié)

本文首先闡述了 WebAssembly 基本概念以及其相較于傳統(tǒng)容器的優(yōu)勢,然后介紹了使用 Rust 開發(fā) Wasm 應(yīng)用的流程。接著,為讀者詳細(xì)展示了在各種環(huán)境中運(yùn)行 Wasm 工作負(fù)載的方法,涵蓋了在 Linux 容器、支持 Wasm 的容器運(yùn)行時(shí),以及編排平臺上的運(yùn)行方法。

本文使用到的代碼以及配置文件可以在我的 Github 上找到:https://github.com/cr7258/hands-on-lab/tree/main/wasm/runtime 。

7 附錄

7.1 關(guān)于 compat 和 compat-smart 注解

本文中使用 "module.wasm.image/variant": "compat" Annotation 來告訴容器運(yùn)行時(shí)這是 Wasm 工作負(fù)載,當(dāng)前 crun 支持了一個新的 Annotation "module.wasm.image/variant": "compat" 。詳情參見:WasmEdge issue: Add crun "-smart" annotation [8]。

當(dāng)使用 compat-smart 注解時(shí),crun 可以根據(jù)工作負(fù)載是 Wasm 還是普通 OCI 容器來智能地選擇容器的啟動方式。這種選擇只會在標(biāo)準(zhǔn) OCI 容器和 Wasm 應(yīng)用程序位于同一個 pod 中時(shí)產(chǎn)生影響。下面是一個示例的 Pod 資源文件,其中包含一個 Wasm 應(yīng)用程序和一個普通的 Linux 應(yīng)用程序。

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: crun
handler: crun
---
apiVersion: v1
kind: Pod
metadata:
  name: wasm-demo-app
  annotations:
    module.wasm.image/variant: compat-smart
spec:
  runtimeClassName: crun
  containers:
  - name: wasm-demo-app
    image: docker.io/cr7258/wasm-demo-app:v1
  - name: linux-demo-app
    image: nginx:1.20

7.2 Krustlet 報(bào)錯

在啟動 Krustlet 的時(shí)候可能會遇到以下報(bào)錯:

libssl.so.1.1: cannot open shared object file: No such file or directory

原因是 Krustlet 依賴 openssl 1.1 版本,可以參考該鏈接解決:解決報(bào)錯 libssl.so.1.1 [9]。

7.3 WasmEdge 報(bào)錯

在用容器運(yùn)行時(shí)啟動容器的時(shí)候可能會出現(xiàn)以下報(bào)錯。

FATA[0000] failed to create shim task: OCI runtime create failed: could not load `libwasmedge.so.0`: `libwasmedge.so.0: cannot open shared object file: No such file or directory`: unknown

重新執(zhí)行 WasmEdge 安裝命令。

curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash

8 參考資料

  • [1] WebAssembly vs Linux Container: https://wasmedge.org/wasm_linux_container/
  • [2] runwasi: https://github.com/containerd/runwasi
  • [3] containerd-wasm-shims: https://github.com/deislabs/containerd-wasm-shims
  • [4] WebAssembly 正在推動云計(jì)算的第三次浪潮: https://nigelpoulton.com/webassembly-the-future-of-cloud-computing
  • [5] Open Containers Initiative: https://github.com/opencontainers/artifacts/blob/main/artifact-authors.md#visualizing-artifacts
  • [6] GitHub Package Registry: https://github.com/features/packages
  • [7] WASI Proposals Support: https://docs.wasmtime.dev/stability-wasi-proposals-support.html
  • [8] 解決報(bào)錯 libssl.so.1.1: https://blog.csdn.net/estelle_belle/article/details/111181037
  • [9] WasmEdge issue: Add crun "-smart" annotation: https://github.com/WasmEdge/WasmEdge/issues/1338
  • [10] WasmEdge Docs: https://wasmedge.org/docs/
  • [11] Kwasm: https://kwasm.sh/
  • [12] 各種容器運(yùn)行時(shí)都解決了什么問題: https://www.zeng.dev/post/2020-container-runtimes/
  • [13] Container Runtimes Part 3: High-Level Runtimes: https://www.ianlewis.org/en/container-runtimes-part-3-high-level-runtimes
  • [14] WebAssembly and its platform targets: https://snarky.ca/webassembly-and-its-platform-targets/
  • [15] WebAssembly: Docker without containers!: https://wasmlabs.dev/articles/docker-without-containers/
  • [16] Standardizing WASI: A system interface to run WebAssembly outside the web: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/
  • [17] Manage WebAssembly Apps Using Container and Kubernetes Tools: https://www.secondstate.io/articles/manage-webassembly-apps-in-wasmedge-using-docker-tools/
  • [18] Build and Manage Wasm Applications using Container Tools - Michael Yuan, WasmEdge: https://www.youtube.com/watch?v=kOvoBEg4-N4
  • [19] Executing WebAssembly (Wasm) modules in containers using crun, podman, and MicroShift: https://www.youtube.com/watch?v=3fudsMOkRCM
  • [20] What's New in Docker + Wasm Technical Preview 2?: https://kodekloud.com/blog/whats-new-in-docker-wasm-technical-preview-2/#
  • [21] Running WebAssembly Applications on Kubernetes with WasmEdge | Mirantis Labs - Tech Talks: https://www.youtube.com/watch?v=--T-JFFNGlE
  • [22] Running Wasm in a container: https://atamel.dev/posts/2023/06-29_run_wasm_in_docker/
  • [23] Cloud Native Apps with Server-Side WebAssembly - Liam Randall, Cosmonic: https://www.youtube.com/watch?v=2OTyBxPyW7Q
  • [24] Containerd Adds Support for a New Container Type: Wasm Containers: https://www.infoq.com/news/2023/02/containerd-wasi/
  • [25] Using WebAssembly and Kubernetes in Combination: https://alibaba-cloud.medium.com/using-webassembly-and-kubernetes-in-combination-7553e54ea501
  • [26] Run WASM applications from Kubernetes: https://msazure.club/run-wasm-applications-from-kubernetes/
  • [27] A First Look at Wasm and Docker: https://dev.to/docker/a-first-look-at-wasm-and-docker-5dg0
  • [28] What is runwasi: https://nigelpoulton.com/what-is-runwasi/
  • [29] Getting started with Docker + Wasm: https://nigelpoulton.com/getting-started-with-docker-and-wasm/
  • [30] Wasm and Kubernetes – Working Together: https://collabnix.com/wasm-and-kubernetes-working-together/
  • [31] Rust microservices in server-side WebAssembly: https://blog.logrocket.com/rust-microservices-server-side-webassembly
  • [32] What is cloud native WebAssembly: https://nigelpoulton.com/what-is-cloud-native-webassembly/
  • [33] Compile Rust & Go to a Wasm+Wasi module and run in a Wasm runtime
  • [34] Cloud Native Wasm Day EU 2023: Summaries, Insights, and Opinions: https://cosmonic.com/blog/industry/cloud-native-wasm-day-2023-wrap-up


責(zé)任編輯:武曉燕 來源: Se7en的架構(gòu)筆記
相關(guān)推薦

2021-08-02 09:40:57

Dapr阿里云Service Mes

2023-05-05 17:20:04

2024-07-19 14:14:37

2022-12-23 09:29:52

大數(shù)據(jù)

2023-01-14 22:59:34

2017-03-07 10:00:01

定義實(shí)踐DevOps

2025-01-26 11:30:07

2023-07-18 18:14:51

云原生軟件架構(gòu)

2023-12-06 15:21:16

Java云原生

2024-05-13 08:00:00

2023-08-30 16:22:03

云原生云計(jì)算

2022-05-02 15:11:15

Bytedoc云原生數(shù)據(jù)庫服務(wù)

2020-09-18 13:09:15

云原生云安全網(wǎng)絡(luò)安全

2020-03-04 09:56:56

網(wǎng)絡(luò)安全云原生容器

2021-06-15 09:57:23

云計(jì)算云原生云開發(fā)

2021-05-07 09:40:26

云計(jì)算云原生WebAssembly

2022-08-21 07:25:09

Flink云原生K8S

2018-09-20 20:46:51

云原生CNBPS靈雀云

2023-02-15 21:39:30

匯編語言軟件開發(fā)

2024-04-23 10:16:29

云原生
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號