精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

WebAssembly 在云原生中的實踐指南

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

1 WebAssembly 介紹

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

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

圖片圖片

2019 年 3 月,Mozilla 推出了 WebAssembly 系統接口(Wasi),以標準化 WebAssembly 應用程序與系統資源之間的交互抽象,例如文件系統訪問、內存管理和網絡連接,該接口類似于 POSIX 等標準 API。Wasi 規范的出現極大地擴展了 WebAssembly 的應用場景,使得 Wasm 不僅限于在瀏覽器中運行,而且可以在服務器端得到應用。同時,平臺開發者可以針對特定的操作系統和運行環境提供 Wasi 接口的不同實現,允許跨平臺的 WebAssembly 應用程序運行在不同的設備和操作系統上。

圖片圖片

2 WebAssembly 會取代容器嗎?

Docker 的創始人 Solomon Hykes 是這樣評價 WASI 的:

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

圖片圖片

Solomon Hykes 后續還發布了一條推文,表示  WebAssembly 將與容器一起工作,而不是取代它們。WebAssembly 可以成為一種容器類型,類似于 Linux 容器或 Windows 容器。它將成為標準的跨平臺應用程序分發和運行時環境。

圖片圖片

3 WebAssembly 的優勢

WebAssembly 相較于傳統的容器有著許多顯著的優勢:

體積更小:WebAssembly 應用程序比容器小,以下是兩個簡單的用于輸出文檔的應用程序,都是使用標準工具構建的,從下圖可以看出,Wasm 應用程序比容器化應用程序小了近 10 倍。

圖片圖片

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

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

4 使用 Rust 開發 Wasm 應用

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

4.1 安裝 Rust

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

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

4.2 為 Rust 添加 wasm32-wasi 編譯目標

前面提到過,Wasi(WebAssembly System Interface)是用于 WebAssembly 的系統級接口,旨在實現 WebAssembly 在不同環境中與宿主系統交互。它提供標準化的方式,使得 WebAssembly 可以進行文件 I/O、網絡操作和系統調用等系統級功能訪問。

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

執行以下命令,為 Rust 編譯器添加 wasm32-wasi 目標。

rustup target add wasm32-wasi

4.3 編寫 Rust 程序

首先執行以下命令構建一個新的 Rust 項目。

cargo new http-server

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

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

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

編寫一個簡單的 HTTP Server,在 8080 端口暴露服務,當接收到請求時返回 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;
}

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

cargo build --target wasm32-wasi --release

4.4 安裝 WasmEdge

編譯完成的 Wasm 模塊需要使用相應的 Wasm 運行時來運行。常見的 Wasm 運行時包括 WasmEdge、Wasmtime 和 Wasmer 等。

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

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

運行以下命令以使已安裝的二進制文件在當前會話中可用。

source $HOME/.wasmedge/env

4.5 運行 Wasm 模塊

使用 wasmedge 來運行前面編譯好的 Wasm 模塊。

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

在本地通過 curl 命令訪問該服務。

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

5 運行 Wasm 工作負載

5.1 在 Linux 容器中運行 Wasm 工作負載

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

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

接下來根據前面編寫的 Rust 代碼構建出 Linux 容器鏡像。首先在前面創建的 http-server 項目根目錄下創建一個名為 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"]

執行以下命令構建容器鏡像。

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 命令訪問該服務。

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

5.2 在支持 Wasm 的容器運行時中運行 Wasm 工作負載

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

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

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

以下是一個概念圖,可以幫助你了解高級和低級運行時是如何協同工作的。

圖片圖片

接下來將會分別介紹如何通過高級和低級容器運行時來運行 Wasm 模塊,首先構建一個 Wasm 鏡像。

5.2.1 構建鏡像

在前面創建的 http-server 項目根目錄下創建一個 Dockerfile 文件,這次我們直接使用 scratch 空鏡像來構建,scratch 是 Docker 中預留的最小的基礎鏡像。

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

執行以下命令構建容器鏡像。

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

將鏡像推送到 Docker Hub 上,方便后續的實驗使用。

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

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

圖片圖片

5.2.2 低級容器運行時

在 5.2.2 章節中將會介紹使用 crun 和 youki 這兩種低級容器運行時在不依賴高級容器運行時的情況下,使用準備好的 config.json 和 rootfs 文件來直接啟動 Wasm 應用。

5.2.2.1 Crun

crun 是用 C 編寫的快速輕量的 OCI 容器運行時,并且內置了對 WasmEdge 的支持。本小節將演示如何通過 crun 來運行 Wasm 模塊。

請確保按照 4.4 小節安裝好了 WasmEdge。

然后在 Ubuntu 系統上從源代碼來構建它,執行以下命令安裝編譯所需的依賴。

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

接下來,配置、構建和安裝支持 WasmEdge 的 crun 二進制文件。

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

接下來,運行 crun -v 檢查是否安裝成功。看到有 +WASM:wasmedge,說明已經在 crun 中安裝了 WasmEdge 了。

crun -v

# 返回結果
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

創建一個目錄來存放運行容器所需的文件。

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

使用 crun spec 命令生成默認的 config.json 配置文件,然后進行修改:

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

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

{
 "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 命令訪問該服務。

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

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

crun kill wasm-demo-app SIGKILL
5.2.2.2 Youki

youki 是一個使用 Rust 編寫的符合 OCI 規范的容器運行時。相較于 C,Rust 的使用帶來了內存安全的優勢。和 crun 一樣,Youki 同樣支持了 WasmEdge。

請確保按照 4.1 小節安裝好了 Rust。

然后 Ubuntu 系統上從源代碼來構建它,執行以下命令安裝編譯所需的依賴。

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

執行以下命令編譯支持 WasmEdge 的 youki 二進制文件。

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

指定 wasm-wasmedge 參數將在 $HOME/.wasmedge 目錄中安裝 WasmEdge 運行時庫。要使該庫在系統中可用,請運行以下命令:

export LD_LIBRARY_PATH=$HOME/.wasmedge/lib

或者:

source $HOME/.wasmedge/env

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

mv youki /usr/local/bin

創建一個目錄來存放運行容器所需的文件。

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

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

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

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

{
  "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 命令訪問該服務。

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

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

youki kill wasm-demo-app SIGKILL

5.2.3 高級容器運行時

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

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

圖片圖片

5.2.3.1 Containerd + Crun

請確保按照 5.2.2.1 小節安裝好了 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 運行 Wasm 程序:

  • --runc-binary:指定使用 crun 來啟動容器。
  • --runtime:指定 shim 的版本和名稱,這將由 containerd 轉換為 shim 的二進制名稱,io.containerd.runc.v2 -> containerd-shim-runc-v2。containerd 會執行 containerd-shim-runc-v2 二進制文件來啟動 shim,真正啟動容器是通過 containerd-shim-runc-v2 去調用 crun 來啟動容器的。
  • --label:添加 "module.wasm.image/variant": "compat",表明表明這是一個沒有 guest OS 的 WebAssembly 應用程序。
# 先拉取鏡像
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 命令訪問該服務。

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

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

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

請確保按照 5.2.2.2 小節安裝好了 youki。

我們可以通過 containerd 運行 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 命令訪問該服務。

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

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

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

runwasi 是一個用 Rust 編寫的庫,屬于 containerd 的子項目,使用 runwasi 可以編寫用于對接 Wasm 運行時的 containerd wasm shim,通過 Wasm 運行時可以管理 Wasm 工作負載。當前使用 runwasi 編寫的 containerd wasm shim 有以下幾個:

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

圖片圖片

我們直接使用 runwasi 提供的 wasmedge shim 來運行 Wasm 應用,首先克隆 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

執行以下命令編譯文件。

make build
sudo make install

然后我們使用 containerd 通過 WasmEdge shim 來運行 Wasm 應用:

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

在本地通過 curl 命令訪問該服務。

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

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

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

5.3 在編排平臺運行 Wasm 工作負載

5.3.1 Docker Desktop 運行 Wasm

Docker Desktop 也使用了 runwasi 來支持 Wasm 工作負載,要在 Docker Desktop 中運行 Wasm 工作負載需要確保勾選以下兩個選項:

  • Use containerd for storing and pulling images
  • Enable Wasm

圖片圖片

點擊 Apply & restart 應用更新,Docker Desktop 會下載并安裝以下可用于運行 Wasm 工作負載的運行時:

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

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

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

在本地通過 curl 命令訪問該服務。

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

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

docker rm -f wasm-demo-app

5.3.2 在 Kubernetes 中運行 Wasm 模塊

Kubernetes 作為容器編排領域的事實標準,WebAssembly 正在推動云計算的第三次浪潮 [4],而 Kubernetes 正在不斷發展以利用這一優勢。

在 Kubernetes 中運行 Wasm 工作負載有兩種方式:

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

圖片圖片

  • 2.將專門用于運行 Wasm 的特殊節點(Krustlet)加入集群,通過標簽選擇器在調度時將 Wasm 工作負載指定到 Krustlet 節點。

Kind(Kubernetes in Docker) 是一個使用 Docker 容器運行本地 Kubernetes 集群的工具。為了方便實驗,在 5.3.2 章節中將使用 Kind 來創建 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 集群的命令行工作,執行以下命令安裝 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

使用以下命令創建一個單節點的 Kubernetes 集群。

kind create cluster --name wasm-demo

每個 Kubernetes Node 都是一個 Docker 容器,通過 docker exec 命令進入該節點。

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

進入節點后,請確保按照 5.2.2.1 小節安裝好了 crun。

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

  • 配置 crun 作為 containerd 的運行時 handler。格式是 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]。
  • pod_annotations 表示允許把 Pod metadata 中設置的 Annotation module.wasm.image/variant 傳遞給 crun,因為 crun 需要通過這個 Annotation 來判斷這是一個 Wasm 工作負載。
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

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

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 將端口轉發到本地進行訪問。

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

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

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

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

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

Kwasm 是一個 Kubernetes Operator,可以為 Kubernetes 節點添加 WebAssembly 支持。當你想為某個節點增加 Wasm 支持時,只需為該節點添加 kwasm.sh/kwasm-node=true 的 Annotation 。隨后,Kwasm 會自動創建一個 Job,負責在該節點上部署運行 Wasm 所需的二進制文件,并對 containerd 的配置進行相應的修改。

圖片圖片

使用以下命令創建一個單節點的 Kubernetes 集群。

kind create cluster --name kwasm-demo

Kwasm 提供了 Helm chart 方便用戶進行安裝,先執行以下命令安裝 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,為所有節點添加 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
# 為節點添加 Wasm 支持
kubectl annotate node --all kwasm.sh/kwasm-node=true

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

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 運行成功后,可以通過 port-forward 將端口轉發到本地進行訪問。

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

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

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

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

kind delete cluster --name kwasm-demo
5.3.2.3 Krustlet

Krustlet 是一個由 Rust 語言編寫的 kubelet,它在 Kubernetes 集群中作為一個節點,專門用于運行 Wasm 工作負載。當 Kubernetes 調度器將 Pod 調度到 Krustlet 節點時,Krustlet 會利用 Wasm 運行時來啟動相應的 Wasm 工作負載。盡管 Krustlet 項目目前已經很久沒有更新了,但是還是值得了解一番。

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

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

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

對于 Krustlet 節點我們就需要手動處理這些事情了,我們可以使用 Krustlet 官方準備的腳本。這個腳本會為我們創建 bootstrap token,這個 token 是 Krustlet 初始化時和 API Server 臨時通信而使用的。腳本還會根據 token 生成 Krustlet 臨時的 kubeconfig 文件,默認在 console ~/.krustlet/config/kubeconfig。

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

接著執行以下命令安裝 Krustlet 二進制文件。

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

最后,運行以下命令來啟動 Krustlet:

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

啟動 Krustlet 后,提示我們需要手動批準 CSR 請求。當然我們也可以設置自動批準,這里先不展開說明。

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

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

kubectl certificate approve instance-2-tls

然后查看節點,就可以看到 Krustlet 節點已經成功注冊到 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

查看節點信息,其架構顯示是 wasm-wasi,并且節點上有 kubernetes.io/arch=wasm32-wasi:NoExecute 和 kubernetes.io/arch=wasm32-wasi:NoSchedule 兩個污點,我們在創建 Pod 時需要指定容忍該污點才能調度到 Krustlet 節點上。

# 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

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

因此我們需要使用 wasm-to-oci 這個工具來構建鏡像,wasm-to-oci 是一個用于將 Wasm 模塊發布到注冊表的工具,它打包模塊并將其上傳到注冊表。執行以下命令,安裝 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

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

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

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

因此我們這里寫一個簡單的打印 Hello, World 的 Rust 程序。執行以下命令構建一個新的 Rust 項目。

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));
    }
}

執行以下命令,將程序編譯為 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。

圖片圖片

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

圖片圖片

然后創建 Pod 使用該鏡像,添加容忍運行調度到 Krustlet 節點上,由于我們的 Kubernetes 集群中只有一個節點,因此不用設置節點選擇器。

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 總結

本文首先闡述了 WebAssembly 基本概念以及其相較于傳統容器的優勢,然后介紹了使用 Rust 開發 Wasm 應用的流程。接著,為讀者詳細展示了在各種環境中運行 Wasm 工作負載的方法,涵蓋了在 Linux 容器、支持 Wasm 的容器運行時,以及編排平臺上的運行方法。

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

7 附錄

7.1 關于 compat 和 compat-smart 注解

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

當使用 compat-smart 注解時,crun 可以根據工作負載是 Wasm 還是普通 OCI 容器來智能地選擇容器的啟動方式。這種選擇只會在標準 OCI 容器和 Wasm 應用程序位于同一個 pod 中時產生影響。下面是一個示例的 Pod 資源文件,其中包含一個 Wasm 應用程序和一個普通的 Linux 應用程序。

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 報錯

在啟動 Krustlet 的時候可能會遇到以下報錯:

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

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

7.3 WasmEdge 報錯

在用容器運行時啟動容器的時候可能會出現以下報錯。

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

重新執行 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 正在推動云計算的第三次浪潮: 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] 解決報錯 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] 各種容器運行時都解決了什么問題: 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


責任編輯:武曉燕 來源: Se7en的架構筆記
相關推薦

2021-08-02 09:40:57

Dapr阿里云Service Mes

2024-07-19 14:14:37

2023-05-05 17:20:04

2022-12-23 09:29:52

大數據

2023-01-14 22:59:34

2017-03-07 10:00:01

定義實踐DevOps

2023-07-18 18:14:51

云原生軟件架構

2025-01-26 11:30:07

2023-12-06 15:21:16

Java云原生

2023-08-30 16:22:03

云原生云計算

2024-05-13 08:00:00

2021-06-15 09:57:23

云計算云原生云開發

2020-03-04 09:56:56

網絡安全云原生容器

2022-05-02 15:11:15

Bytedoc云原生數據庫服務

2020-09-18 13:09:15

云原生云安全網絡安全

2022-08-21 07:25:09

Flink云原生K8S

2018-09-20 20:46:51

云原生CNBPS靈雀云

2021-05-07 09:40:26

云計算云原生WebAssembly

2022-03-01 18:27:18

云原生日志監控

2024-04-23 10:16:29

云原生
點贊
收藏

51CTO技術棧公眾號

久久久久久久有限公司| 久久久久久久国产精品| 日本xxxx黄色| 1区2区3区在线视频| 国产ts人妖一区二区| 69久久夜色精品国产69| 国产91丝袜美女在线播放| av日韩一区| 天天av天天翘天天综合网色鬼国产| 欧美色欧美亚洲另类七区| 91成人国产综合久久精品| 欧美久久99| 亚洲一品av免费观看| 少妇欧美激情一区二区三区| 日本综合字幕| 亚洲黄色小说网站| 奇米888一区二区三区| 国产精品久久久久久69| 午夜在线精品| 欧美激情极品视频| 性の欲びの女javhd| 成人春色在线观看免费网站| 在线日韩av片| 国产真人做爰毛片视频直播| 视频三区在线| 久久夜色精品国产欧美乱极品| 成人在线播放av| 亚洲国产av一区二区三区| 欧美特黄一级| 日韩在线观看免费高清完整版| 中文乱码人妻一区二区三区视频| 国产视频一区二| 色欧美日韩亚洲| 69堂免费视频| 91视频欧美| 亚洲精品videosex极品| 亚洲亚洲精品三区日韩精品在线视频 | 中文字幕日韩国产| 最新亚洲一区| 欧美韩国理论所午夜片917电影| ass极品国模人体欣赏| 蜜桃一区av| 日韩欧美亚洲国产精品字幕久久久 | 影音先锋亚洲电影| 欧美巨猛xxxx猛交黑人97人| 国产福利在线导航| 国产午夜一区| 亚洲欧洲国产精品| 欧美黑人欧美精品刺激| 国产一区丝袜| 亚洲第一视频网站| 在线观看免费视频黄| 日本亚洲视频| 日韩精品一区二区三区swag| 91丨porny丨九色| 高清久久一区| 日韩欧美一级二级三级久久久| 加勒比av中文字幕| 亚洲成人1区| 欧美日韩高清影院| 特级黄色片视频| 国产在线不卡一区二区三区| 91精品国产免费久久综合| 国内自拍第二页| 国产亚洲精aa在线看| 91精品国产综合久久久久久久久久 | 久久久精品有限公司| 天堂在线观看免费视频| 不卡一区二区在线| 久久riav二区三区| 欧美高清电影在线| 国产精品网站在线播放| 中文字幕一区二区三区5566| 中文字幕有码在线视频| 亚洲一区二区综合| 精品少妇一区二区三区在线| 69久成人做爰电影| 欧洲国产伦久久久久久久| 中文字幕第38页| 欧美日韩中出| 日韩经典中文字幕在线观看| 亚洲AV无码国产成人久久| 精品国产一区二区三区香蕉沈先生 | 国产精品一二三在线| 国产夫妻性生活视频| 成人精品电影在线观看| 日本不卡久久| 麻豆传媒在线完整视频| 一区二区三区不卡视频 | 天堂影院一区二区| 国产日韩视频在线观看| 精品人妻久久久久一区二区三区| 成人av影院在线| 日韩国产在线一区| 在线观看电影av| 狠狠躁夜夜躁人人爽天天天天97| 久久久精品麻豆| 欧美大片91| 精品在线观看国产| 免费三级在线观看| 亚洲一区二区成人| 91在线观看欧美日韩| 性xxxxbbbb| 亚洲欧洲精品天堂一级| 欧美视频在线播放一区| 久久91视频| 日韩电影网在线| 神马久久精品综合| 国产精品日本| 91在线看www| 国产www.大片在线| 无码av中文一区二区三区桃花岛| 色播五月激情五月| 男男gay无套免费视频欧美| 久久国产一区二区三区| 黄色片中文字幕| 国产电影一区二区三区| 四虎一区二区| 涩涩网在线视频| 日韩精品在线一区二区| 国产成人精品视频免费| 一本色道久久综合亚洲精品不卡| 92看片淫黄大片欧美看国产片| 国外av在线| 亚洲成精国产精品女| 免费精品99久久国产综合精品应用| 欧美挤奶吃奶水xxxxx| 久久av中文字幕| 伊人网视频在线| 久久久久国产精品麻豆| 全黄性性激高免费视频| 9999精品| 色妞久久福利网| 无码人妻精品一区二区三区不卡 | 国产农村妇女毛片精品久久莱园子| 1卡2卡3卡精品视频| 午夜视频在线免费观看| 在线亚洲免费视频| 37p粉嫩大胆色噜噜噜| 99精品久久| 国内精品二区| 九色porny视频在线观看| 精品日韩在线一区| 欧美日韩成人免费观看| 国产乱码精品一区二区三区五月婷| 亚洲国产精品一区二区第一页| 依依综合在线| 亚洲摸下面视频| 日韩 国产 欧美| 久久精品视频一区二区三区| 久久久久久久少妇| 欧美猛男男男激情videos| 欧美中文字幕在线播放| 色视频在线观看福利| 欧美日韩免费网站| 偷拍夫妻性生活| 日韩va亚洲va欧美va久久| 日本一区视频在线观看免费| 成人一区视频| 久久的精品视频| av手机免费看| 亚洲午夜激情网站| 一区二区三区免费在线观看视频| 宅男噜噜噜66国产日韩在线观看| 免费在线国产精品| 三上悠亚亚洲一区| 国产一区二区黄| 国产精品视频久久久久久| 亚洲激情一二三区| av2014天堂网| 日本特黄久久久高潮| 日本福利视频导航| www国产精品| 欧美一级片久久久久久久| 伦理片一区二区三区| 欧美日韩三级一区| 欧美丰满熟妇bbbbbb| 成人黄页在线观看| 欧美日韩在线免费播放| 欧美好骚综合网| 99精品国产一区二区| 午夜伦理福利在线| 中文字幕亚洲欧美日韩在线不卡 | 久久99久久久久久久久久久| 欧美少妇一区二区三区| 欧美午夜18电影| 国产精品毛片a∨一区二区三区|国 | 91av在线免费视频| 久久久精品蜜桃| 99999精品| 西西人体一区二区| 手机福利在线视频| 九九热hot精品视频在线播放 | 久久国产黄色片| 国产精品超碰97尤物18| 182在线视频| 久久精品国产亚洲高清剧情介绍| 国产性生活免费视频| 精品国产精品| 成人精品一二区| 欧美日韩精品一区二区三区视频| 欧美乱人伦中文字幕在线| 色播色播色播色播色播在线 | 国精品人妻无码一区二区三区喝尿 | 成人午夜精品一区二区三区| 国产麻花豆剧传媒精品mv在线| 91日韩免费| 久久久久九九九| 欧美午夜网站| 国产精品视频26uuu| 91制片在线观看| 久久精品91久久久久久再现| 日韩国产福利| 精品免费国产二区三区| 中文字幕人妻一区二区三区视频| 精品久久久久久久久久久久久久| 国产一区二区精彩视频| 国产午夜亚洲精品不卡| 国产吃瓜黑料一区二区| 国产综合色在线| mm131亚洲精品| 国产精品久久国产愉拍| 国产黄色激情视频| 99久久精品费精品国产| 日韩欧美亚洲精品| 亚洲成在人线免费观看| 国产91精品一区二区绿帽| 超碰国产精品一区二页| 日本精品久久久久影院| 1区2区在线| 欧美激情免费在线| 51xtv成人影院| 久久久91精品| wwwxxx在线观看| 国产亚洲欧美另类中文| 全色精品综合影院| 日韩成人黄色av| 熟妇人妻中文av无码| 精品捆绑美女sm三区| 国产色片在线观看| 在线播放欧美女士性生活| 亚洲图片小说视频| 欧美日韩综合在线免费观看| 波多野结衣电车痴汉| 一道本成人在线| 亚洲大片免费观看| 91久久精品日日躁夜夜躁欧美| 中文人妻av久久人妻18| 日本久久一区二区三区| 国产污视频网站| 欧美午夜不卡在线观看免费| 进去里视频在线观看| 欧美午夜一区二区三区| 亚洲视频一区在线播放| 欧美日韩成人在线一区| 国产精品久久久久久免费免熟| 欧美日韩视频在线观看一区二区三区| 中文字幕一区二区免费| 欧美精品在线观看一区二区| 国产片高清在线观看| 日韩欧美久久久| 亚洲欧美高清视频| 亚洲免费小视频| 自拍视频在线播放| 欧美不卡视频一区发布| 国产黄色大片在线观看| 97免费在线视频| 精品网站在线| 成人午夜两性视频| av在线亚洲色图| 精品欧美日韩在线| 欧美日韩国产一区二区三区不卡| 水蜜桃一区二区| 欧美日韩岛国| 91国视频在线| 久久精品国产99| 苍井空张开腿实干12次| 久久免费视频色| 中文字幕无码日韩专区免费| 一区二区在线免费观看| av大全在线观看| 欧美日韩亚洲国产综合| 亚洲女人18毛片水真多| 亚洲视频第一页| 在线午夜影院| 日韩av日韩在线观看| 国产精品欧美一区二区三区不卡 | 久久久久久久久久国产| 日本电影欧美片| 91丝袜脚交足在线播放| 免费精品国产| 大地资源第二页在线观看高清版| 亚洲青涩在线| 男生操女生视频在线观看| jvid福利写真一区二区三区| 人与嘼交av免费| 亚洲一区二区五区| 91超薄丝袜肉丝一区二区| 亚洲加勒比久久88色综合| 午夜激情视频在线| 91精品国产精品| 国产区一区二| 日韩区国产区| 一本色道久久综合亚洲精品不卡 | 精品久久久久久久久久久久久久久久久久| 欧美日韩国产天堂| 西西人体44www大胆无码| 日韩在线观看精品| 韩国久久久久久| 国产成人精品福利一区二区三区 | 最新的欧美黄色| 亚洲欧美韩国| eeuss一区二区三区| 欧美xxxxx视频| 久久精品视频91| 99视频在线观看一区三区| 欧美成人手机视频| 欧美调教femdomvk| 日本一区高清| 久久久这里只有精品视频| 国产一区二区| 欧美性受xxxx黑人猛交88| 免费观看久久久4p| 免费一级做a爰片久久毛片潮| 亚洲二区在线观看| 精品国产18久久久久久| 色婷婷久久av| 成人免费毛片嘿嘿连载视频…| 九色视频成人porny| 黄色免费成人| 国产精品91av| 夜夜嗨av一区二区三区网页| 88av在线视频| 久久影视电视剧免费网站| 国产成人福利夜色影视| 欧洲精品码一区二区三区免费看| 9久re热视频在线精品| 北京富婆泄欲对白| 亚洲国产一二三| 精品人妻无码一区二区色欲产成人| 日韩在线视频一区| 欧美黑粗硬大| 中文字幕一区二区三区乱码| 麻豆精品一区二区三区| gv天堂gv无码男同在线观看| 欧洲国产伦久久久久久久| 国产黄在线观看| 国产精品国产三级国产专播精品人 | 中国美女黄色一级片| 欧美性大战久久久久久久| 自拍视频在线网| 亚洲精品免费网站| 女人香蕉久久**毛片精品| 爱情岛论坛亚洲自拍| 一区二区三区精品在线| 国产香蕉在线观看| 97在线观看视频| 国产99久久| 激情视频免费网站| 亚洲欧洲性图库| 午夜精品久久久久久久91蜜桃| 欧美极度另类性三渗透| 国产成人精品亚洲线观看| 欧美 日韩 国产在线观看| 久久久亚洲精品石原莉奈| 在线观看免费观看在线| 久久精品在线视频| 国产精品久av福利在线观看| 中文字幕无码精品亚洲35| 国产午夜精品理论片a级大结局| 亚洲免费视频二区| 精品国产欧美一区二区三区成人| 美女日韩一区| 天天夜碰日日摸日日澡性色av| 久久久国产精华| 国产精品视频在线观看免费| 久久久久久亚洲| 精品国产一区探花在线观看| 777一区二区| 亚洲韩国一区二区三区| 嫩草精品影院| 91久久精品国产91久久性色| 亚洲性感美女99在线| www.狠狠爱| 欧美一级日韩免费不卡| 蜜桃视频在线观看播放| 亚洲va久久久噜噜噜久久狠狠 | 不卡精品视频| 999久久欧美人妻一区二区| 91丝袜呻吟高潮美腿白嫩在线观看| 伊人久久久久久久久久久久| 久久久成人精品| 亚洲bt欧美bt精品777| 可以看污的网站| 婷婷成人综合网| 麻豆传媒在线观看| 精品91免费| 激情综合一区二区三区| 精品国产免费观看| 久久精品视频在线播放|