Menjalankan WASM di Docker

· 5 min read
Menjalankan WASM di Docker
Photo by Dominik Malinowski / Unsplash

Menjalankan WebAssembly adalah fitur beta dan memerlukan penggunaan containerd. Untuk mengaktifkan containerd, buka dasbor Docker Desktop, lalu Setelan > Fitur dalam pengembangan > Fitur beta > Gunakan containerd untuk menyimpan dan menarik gambar.

Perlu diingat bahwa mengaktifkannya containerdsebelumnya merusak salah satu demo Kubernetes saya . Bermainlah dengan WASM sepuasnya, tetapi ingat untuk segera mengembalikan konfigurasi setelahnya, atau ada kemungkinan kontainer yang diunduh tidak akan berjalan lagi.

Kita ingin membandingkan gambar biasa dengan WebAssembly; oleh karena itu, kita memerlukan proyek yang dapat dikompilasi ke kode asli dan WASM. Karena alasan ini, kami memilih untuk menggunakan bahasa Rust . Saya akan memiliki satu proyek sederhana dengan dua Dockerfile: satu yang dikompilasi ke asli, dan yang lainnya yang dikompilasi ke WASM.

Membangun secara lokal

Berikut ini adalah Rust yang diharapkan Hello World:

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

Kita dapat memasang target Webassembly dan membangun secara lokal untuk tujuan perbandingan:

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

Berkasnya relatif kecil:

-rwxr-xr-x  1 nico  staff   2.0M Jun  4 15:44   wasm-native.wasm

Membangun image Docker dasar

Yang Dockerfilemembangun citra Webassembly adalah sebagai berikut:

FROM rust:1.70-slim-bullseye as build                                    #1

COPY Cargo.toml .
COPY Cargo.lock .
COPY src src

RUN rustup target add wasm32-wasi                                        #2

RUN cargo build --target wasm32-wasi --release                           #3

FROM scratch                                                             #4

COPY --from=build /target/wasm32-wasi/release/wasm-native.wasm wasm.wasm #5

ENTRYPOINT [ "/wasm.wasm" ]
  1. Mulai dari image Rust Docker terakhir
  2. Tambahkan target WASM
  3. Membangun, menargetkan Webassembly
  4. Gunakan build multi-tahap. Mulai dari awal
  5. Salin file Webassembly yang dihasilkan pada tahap sebelumnya

Materi referensi menggunakan --platform wasi/wasm32argumen tersebut saat membangun citra Docker. Argumen tersebut tidak berfungsi di komputer saya. Mungkin karena saya menggunakan Mac M1, atau dokumentasinya perlu diperbarui. Bagaimanapun, saya membangun "secara normal":

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

Sekarang kita dapat menjalankannya, dengan menentukan runtime WASM yang didukung:

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

Untuk membandingkan, kita dapat membuat gambar asli dengan kode yang sama :

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 #1

FROM scratch                                                        #2

COPY --from=build /target/release/wasm-native native
  1. Jadikan biner mandiri
  2. Bisa memulai dari awal

Sekarang kita dapat membandingkan ukuran gambar:

REPOSITORY         TAG      IMAGE ID       CREATED       SIZE
docker-native      1.0      0c227194910a   7 weeks ago   7.09MB
docker-wasm        1.0      f9a88747f798   4 weeks ago   2.61MB

Citra Webassembly sekitar sepertiga dari paket biner asli.

Kami sedikit curang karena kami menambahkan runtime WASM... pada saat runtime.

Membangun gambar yang lebih kompleks

Mari kita lihat bagaimana kita dapat menambahkan parameter ke biner dan memperbarui kode sebagaimana mestinya:

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() > 1 {
        println!("Hello, world!");
    } else {
        println!("Hello, {}!", args[1]);
    }
}

Mari kita buat ulang gambarnya dan bandingkan lagi:

REPOSITORY         TAG      IMAGE ID       CREATED          SIZE
docker-native      1.0      0c227194910a   7 weeks ago      7.09MB
docker-native      1.1      3ae029030e83   39 minutes ago   7.1MB
docker-wasm        1.0      f9a88747f798   4 weeks ago      2.61MB
docker-wasm        1.1      41e38b68f4e4   39 minutes ago   2.63MB

Menjalankan panggilan HTTP?

Dengan ini, mudah untuk terbawa suasana dan mulai berpikir besar: bagaimana jika kita dapat menjalankan panggilan HTTP?

Kita akan menggunakan peti reqwest karena saya sudah mengenalnya. reqwestBergantung pada Tokio.

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

Sekarang kita dapat memperbarui kode untuk membuat permintaan ke httpbin.org dan mencetak hasilnya:

#[tokio::main]
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,
}

Mengompilasi kode ini mengungkap keterbatasan WASM:

#0 12.40 error: Only features sync,macros,io-util,rt,time are supported on wasm.
#0 12.40    --> /usr/local/cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.28.2/src/lib.rs:488:1
#0 12.40     |
#0 12.40 488 | compile_error!("Only features sync,macros,io-util,rt,time are supported on wasm.");
#0 12.40     | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

WASM tidak multi-threaded, sedangkan Tokio bersifat default . Namun, kita dapat mengonfigurasi Tokio untuk bekerja dalam lingkungan single-thread. Mari kita mulai dengan menggunakan fitur-fitur yang kita butuhkan: macrosuntuk mainfungsi dan rtuntuk runtime tokio.

tokio = { version = "1.28", features = ["rt", "macros"] }

Sekarang, kita dapat membatasi Tokio ke satu thread yang unik:

#[tokio::main(flavor = "current_thread")]
async fn main() {}

Kompilasi sekarang berfungsi. Namun, saya mengalami masalah saat menjalankan:

[2023-06-05 12:22:11.986] [error] instantiation failed: unknown import, Code: 0x62
[2023-06-05 12:22:11.986] [error]     When linking module: "__wbindgen_placeholder__" , function name: "__wbindgen_object_drop_ref"
[2023-06-05 12:22:11.986] [error]     At AST node: import description
[2023-06-05 12:22:11.986] [error]     At AST node: import section
[2023-06-05 12:22:11.986] [error]     At AST node: module
docker: Error response from daemon: Others("unknown import"): unknown.

Peti tersebut reqwesttidak berfungsi dengan lingkungan WASI. Hingga berfungsi, ada cabang yang diberi nama reqwest_wasi . tokio_wasi adalah peti yang kompatibel dengan WASI untuk tokio. Perhatikan bahwa versi yang terakhir perlu diperbarui. Mari kita ganti peti:

[dependencies]
reqwest_wasi = { version = "0.11", features = ["json"] }
tokio_wasi = { version = "1.25", features = ["rt", "macros"] }

Dengan peti baru, kompilasi berfungsi, begitu juga dengan eksekusi. Di sisi lain, citra asli berfungsi dengan sempurna, dengan sedikit perubahan pada Dockerfile:

#docker build -f Dockerfile-native -t docker-native:1.2 .
FROM rust:1.70-slim-bullseye as build

COPY Cargo.toml .
COPY Cargo.lock .
COPY src src

RUN apt-get update && apt-get install -y pkg-config libssl-dev   #1

RUN cargo build --release

FROM debian:bullseye-slim                                        #2

COPY --from=build /target/release/wasm-native native

ENTRYPOINT [ "/native" ]
  1. Instal pustaka yang diperlukan untuk SSL
  2. Ubah ke gambar dasar yang lebih lengkap untuk menghindari pemasangan pustaka tambahan

Berikut perbandingan akhirnya:

REPOSITORY         TAG      IMAGE ID       CREATED          SIZE
docker-native      1.0      0c227194910a   7 weeks ago      7.09MB
docker-native      1.1      3ae029030e83   22 hours ago     7.1MB
docker-native      1.2      4ff64cf9de46   7 hours ago      123MB
docker-wasm        1.0      1cc78a392477   23 hours ago     2.61MB
docker-wasm        1.1      41e38b68f4e4   22 hours ago     2.63MB
docker-wasm        1.2      6026f5bd789c   18 seconds ago   5.34MB

Kami tidak mengutak-atik pengoptimalan gambar asli. Namun, akan sulit untuk mengalahkan gambar WASM, karena ukurannya di bawah 6MB!

Tidak ada peluang untuk mengimplementasikan server Axum.

Kesimpulan

Kita mengimplementasikan beberapa gambar Docker WASM dalam posting ini, dari Hello World yang paling mudah hingga klien HTTP.

Meskipun ekosistemnya masih bisa ditingkatkan, dukungan WASM Docker sudah bisa dimanfaatkan. Ukuran gambar WASM yang kecil merupakan nilai tambah yang besar.

Kode sumber lengkap untuk postingan ini dapat ditemukan di GitHub .

Untuk melangkah lebih jauh: