在 Docker 上玩 WASM

字節碼可以在任何地方運行的想法可以追溯到 JVM 的初始階段,WebAssembly 是舊思想的新實現。WebAssembly 的目的是在瀏覽器中運行,而 Docker 宣佈它可以在不需要容器的情況下運行 WASM 代碼。在這篇文章中,我們來探索它是如何工作的。

先決條件

運行 WebAssembly 是一個測試版功能,需要使用 containerd。要啓用 containerd,請轉到 Docker Desktop 儀表板,然後單擊 “設置> 開發中的功能> Beta 功能>使用 containerd 存儲和提取鏡像”。

爲了比較常規鏡像與 WebAssembly,因此,需要一個既可以編譯爲本機代碼又可以編譯爲 WASM 的項目。出於這個原因,我們使用 Rust 語言創建一個帶有兩個 dockerfile 的簡單項目:一個編譯爲本地代碼,另一個編譯爲 WASM。

在本機構建

創建一個 Rust 項目:

cargo new wasm-docker-example

src/main.rs 文件中代碼如下:

fn main() {
    println!("Hello, world!");
}

爲了便於比較,我們可以在本地構建 Webassembly 目標:

rustup target add wasm32-wasi
cargo build --target wasm32-wasi --release

文件相對較小:

-rwxr-xr-x   1 Justin  staff  2093838  6 19 17:39 wasm-docker-example.wasm

構建簡單的 Docker 鏡像

在項目根目錄下創建 Dockerfile-wasm 文件,內容如下:

FROM rust:1.70-slim-bullseye as build                                    

COPY Cargo.toml .
COPY Cargo.lock .
COPY src src
RUN rustup target add wasm32-wasi                                        
RUN cargo build --target wasm32-wasi --release                           
FROM scratch                                                             
COPY --from=build /target/wasm32-wasi/release/wasm-docker-example.wasm wasm.wasm
ENTRYPOINT [ "/wasm.wasm" ]

1,從一個 Rust Docker 映像開始

2,添加 WASM 目標

3,構建 Webassembly

4,從 scratch 開始使用多級構建。

5,複製上一階段生成的 Webassembly 文件

使用如下命令構建鏡像:

docker build -f Dockerfile-wasm -t docker-wasm:1.0 .

使用如下命令運行 WASM:

docker run --runtime=io.containerd.wasmedge.v1 docker-wasm:1.0

Hello, world!

爲了進行比較,我們可以用相同的代碼創建一個本地鏡像,在項目根目錄下創建 Dockerfile-native 文件,內容如下:

FROM rust:1.70-slim-bullseye as build

COPY Cargo.toml .
COPY Cargo.lock .
COPY src src
RUN RUSTFLAGS='-C target-feature=+crt-static' cargo build --release 
FROM scratch                                                        
COPY --from=build /target/release/wasm-docker-example native

從 scratch 開始構建鏡像並生成二進制文件。

使用如下命令構建鏡像:

docker build -f Dockerfile-native -t docker-native:1.0 .

現在我們可以比較鏡像的大小:

REPOSITORY      TAG       IMAGE ID       CREATED         SIZE
docker-native   1.0       f48a37ac1a66   6 seconds ago   7.32MB
docker-wasm     1.0       332c53bb03e0   5 minutes ago   2.61MB

Webassembly 鏡像大約是本機鏡像的三分之一。

構建複雜的 Docker 鏡像

在 Cargo.toml 文件中加入以下依賴項:

[dependencies]
reqwest = { version = "0.11"features = ["json"] }
tokio = { version = "1.28"features = ["full"] }
serde = { version = "1.0"features = ["derive"] }

在 src/main.rs 中更新代碼來發出請求並打印結果:

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use reqwest::get;

#[tokio::main(flavor = "current_thread")]
async fn main() {
    match get("http://httpbin.org/get").await {
        Ok(response) ={
            let result = response.json::<GetBody>().await;
            match result {
                Ok(json) ={
                    println!("{:#?}", json);
                }
                Err(err) ={
                    println!("{:#?}", err)
                }
            }
        }
        Err (err) ={
            println!("{:#?}", err)
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
struct GetBody {
    args: HashMap<String, String>,
    headers: HashMap<String, String>,
    origin: String,
    url: String,
}

使用如下命令構建鏡像:

docker build -f Dockerfile-wasm -t docker-wasm:1.1 .

使用如下命令運行 WASM:

docker run --runtime=io.containerd.wasmedge.v1 docker-wasm:1.1

GetBody {
    args: {},
    headers: {
        "X-Amzn-Trace-Id""Root=1-64905d7f-2371d2807e7f74780d60ab96",
        "Host""httpbin.org",
        "User-Agent""Go-http-client/1.1",
        "Accept""*/*",
    },
    origin: "103.82.4.2",
    url: "http://httpbin.org/get",
}

總結

在這篇文章中,實現了幾個 WASM Docker 鏡像,從最簡單的 Hello World 到 HTTP 客戶端。雖然 Docker 關於 WASM 的生態系統還有改進的空間,但 Docker 對 WASM 的支持已經可以讓我們從中受益。WASM 鏡像的尺寸小是一個巨大的優點。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/MfwwrsxWHM7BU97_Upgx9A