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 containerd
sebelumnya 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 Dockerfile
membangun 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" ]
- Mulai dari image Rust Docker terakhir
- Tambahkan target WASM
- Membangun, menargetkan Webassembly
- Gunakan build multi-tahap. Mulai dari awal
- Salin file Webassembly yang dihasilkan pada tahap sebelumnya
Materi referensi menggunakan --platform wasi/wasm32
argumen 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
- Jadikan biner mandiri
- 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. reqwest
Bergantung 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: macros
untuk main
fungsi dan rt
untuk 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 reqwest
tidak 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" ]
- Instal pustaka yang diperlukan untuk SSL
- 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: