ข้ามไปยังเนื้อหา

wasmtime & wasmer

สองรันไทม์อิสระระดับ production ที่ครองระบบนิเวศ WASI: wasmtime (ดูแลโดย Bytecode Alliance, เขียนด้วย Rust) และ wasmer (เขียนด้วย Rust, ข้ามแพลตฟอร์ม) ทั้งคู่ implement WASI spec, รองรับ ahead-of-time (AOT) compilation และเปิดเผย embedding API สำหรับ Rust, Python, Go และภาษาอื่น ๆ

Terminal window
# wasmtime — ผ่าน installer script
curl https://wasmtime.dev/install.sh -sSf | bash
# wasmer — ผ่าน installer script
curl https://get.wasmer.io -sSfL | sh
# ตรวจสอบ
wasmtime --version
wasmer --version
Terminal window
# รันพื้นฐาน
wasmtime my_app.wasm
# ส่ง command-line arguments (-- คั่น runtime flag จาก module arg)
wasmtime my_app.wasm -- hello world
# ให้สิทธิ์เข้าถึง directory
wasmtime --dir /tmp my_app.wasm
# ให้สิทธิ์ด้วย path alias (host path::guest path)
wasmtime --dir /host/data::/data my_app.wasm
# ส่ง environment variable
wasmtime --env DATABASE_URL=postgres://localhost/db my_app.wasm
# รวม: dir + env + args
wasmtime --dir /data --env LOG_LEVEL=info my_app.wasm -- --config /data/cfg.toml
Terminal window
# รันพื้นฐาน
wasmer my_app.wasm
# mount directory
wasmer --dir /tmp my_app.wasm
# ส่ง environment variable
wasmer --env KEY=value my_app.wasm
# รันจาก Wasmer registry (wapm packages)
wasmer run python/python -- --version

พลังที่แท้จริงของรันไทม์อิสระคือการ embed — โหลด plugin .wasm ขณะ runtime โดยไม่ต้องคอมไพล์ host application ใหม่

use wasmtime::{Engine, Linker, Module, Store};
use wasmtime_wasi::WasiCtxBuilder;
fn run_plugin(wasm_bytes: &[u8]) -> anyhow::Result<()> {
let engine = Engine::default();
let module = Module::new(&engine, wasm_bytes)?;
// สร้าง WASI context ขั้นต่ำ — ไม่มี filesystem, ไม่มี env var
let wasi = WasiCtxBuilder::new().inherit_stdio().build();
let mut store = Store::new(&engine, wasi);
let mut linker = Linker::new(&engine);
wasmtime_wasi::add_to_linker_sync(&mut linker, |s| s)?;
let instance = linker.instantiate(&mut store, &module)?;
let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?;
start.call(&mut store, ())?;
Ok(())
}

pattern นี้คือวิธีที่ระบบ plugin ทำงาน: host application โหลดไฟล์ .wasm ตามอำเภอใจขณะ runtime และเรียก exported function โดยมี sandbox บังคับ isolation อย่างเข้มงวด

sequenceDiagram
  participant Host as Host App (Rust)
  participant WT as wasmtime Engine
  participant M as Plugin (.wasm)
  Host->>WT: Engine::default()
  Host->>WT: Module::new(wasm_bytes)
  Host->>WT: Linker + WasiCtxBuilder
  Host->>WT: linker.instantiate()
  WT->>M: instantiate + link imports
  Host->>M: typed_func("process").call(args)
  M-->>Host: return value
Embedding wasmtime เพื่อรัน plugin module
ตัวเลือกBenefitCost
WASI capability-based security (--dir/--env)ปลอดภัยกว่ามาก — module ที่ wasmtime/wasmer รันได้เฉพาะสิทธิ์ที่ flag ระบุไว้อย่างชัดเจนยังไม่ compatible กับ POSIX เต็มรูปแบบ binary ที่ port มาจาก native code ซึ่ง assume ambient access จะรันไม่ได้ตรง ๆ
Embed wasmtime/wasmer สำหรับ edge/serverlessCold-start ระดับ microsecond เมื่อเทียบกับการ spin container ใหม่ทุกครั้งContainer ecosystem ยังมี tooling, orchestration และ library support ที่ mature และกว้างกว่า
  • ลืมส่ง --dir หรือ --env ตอนรัน แล้วแปลกใจว่าทำไม module เข้าถึง filesystem หรือ environment variable ไม่ได้ — ต้อง grant capability ให้ runtime อย่างชัดเจนเสมอ
  • คิดว่า SIMD หรือ threads proposal รองรับเหมือนกันทุก runtime — wasmtime และ wasmer อาจ support level ต่างกัน ต้อง check ความสามารถของ runtime version ที่ใช้ก่อน deploy
  • Embed wasmtime version เก่าเข้ากับ component ที่ build ด้วย Component Model รุ่นใหม่กว่า — WIT interface ที่ยังพัฒนาอยู่ทำให้ binding code เก่าพังตอน link หรือ instantiate

💡 ตัวอย่างจากของจริง

Fermyon Spin build บน wasmtime เพื่อ embed และรัน serverless Wasm module ส่วน Fastly Compute@Edge และ Cloudflare Workers ใช้หลักการ embedding และ sandboxing แบบเดียวกันเพื่อ isolate tenant code ด้วย cold-start ที่ต่ำมาก

flag ใดที่ให้สิทธิ์ WASI module เข้าถึง host directory ใน wasmtime?
จุดประสงค์ของ WasiCtxBuilder เมื่อ embed wasmtime ใน Rust คืออะไร?
AOT compilation ใน wasmtime ให้ประโยชน์อะไร?