Skip to content

Latest commit

Β 

History

History
225 lines (164 loc) Β· 10.9 KB

File metadata and controls

225 lines (164 loc) Β· 10.9 KB

πŸ“– Reading 12 (Bonus) β€” WebAssembly Containers: A Different Kind of Portability

🎁 This is a bonus reading, paired with Lab 12. Optional, for students chasing the frontier of portable runtimes.


1. The Problem WebAssembly Solves (on the Server)

Containers solved "ships across machines". But they did not solve:

  • 🐌 Cold-start time β€” a Go container starts in ~1-2 s. A WASM module starts in ~1 ms
  • πŸ“¦ Size β€” even with multi-stage, a Go container is ~15 MB. A WASM module is ~1-3 MB
  • πŸ›οΈ Isolation β€” containers share the host kernel (Lecture 5). WASM modules run in a sandbox with no system calls by default β€” stronger isolation than containers, lighter than VMs
  • 🌐 CPU portability β€” linux/amd64 vs linux/arm64 vs Apple Silicon. One WASM module runs on all of them, unchanged

For high-fan-out, latency-sensitive workloads (edge functions, plugin systems, multi-tenant SaaS), these wins matter.

πŸ€” Think: Cloud Run cold-started QuickNotes in ~1.5 s in Lab 10. What workloads would benefit from booting in 1 ms instead?


2. A Compressed History

  • 🌐 2015 β€” Mozilla, Google, Microsoft, Apple announce WebAssembly as a successor to asm.js
  • πŸ“œ March 2017 β€” WebAssembly 1.0 specification published
  • πŸͺŸ 2017-2019 β€” Adopted by all four major browsers as a stable feature
  • πŸ–₯️ 2019 β€” WASI (WebAssembly System Interface) proposed β€” a POSIX-like ABI for running WASM outside the browser (CLI tools, server-side modules)
  • πŸš€ 2020 β€” Fermyon founded; Spin framework launches the "WASM containers" pattern
  • 🧩 2022-2023 β€” containerd gains WASM shims; Kubernetes can schedule a crun-wasmtime "container" that's actually a WASM module
  • 🌍 2024-2026 β€” Cloudflare Workers, Fastly Compute@Edge, Fermyon Cloud β€” mainstream edge-compute platforms ship WASM-first

3. WASM 101

graph LR
    S["πŸ“œ source<br/>Go / Rust / C / AssemblyScript"] -- "compile" --> W["πŸ“¦ .wasm binary<br/>(stack-based VM bytecode)"]
    W -- "run" --> R1["🌐 Browser"]
    W -- "run" --> R2["πŸ–₯️ wasmtime CLI"]
    W -- "run" --> R3["🐳 containerd-wasm"]
    W -- "run" --> R4["☁️ Spin / Cloudflare Worker"]
Loading
  • 🧠 WebAssembly is a stack-based virtual machine instruction set β€” like a portable, compact CPU
  • πŸ”’ Capability-based sandbox: by default no filesystem, no network, no clock. You grant only what's needed
  • πŸ“¦ Multiple source languages compile to it: Rust (best support), C/C++, Go (via TinyGo), AssemblyScript
.wasm = bytecode + module imports/exports + type signatures

4. WASI: WebAssembly System Interface

In the browser, WASM accesses the world through JavaScript bindings. On the server, WASI is the standard ABI:

WASI API What it gives
wasi:cli/stdio stdin/stdout/stderr
wasi:filesystem Pre-opened directory handles only (no /)
wasi:sockets TCP/UDP (limited)
wasi:clocks Monotonic + wall clocks
wasi:random Entropy source
  • πŸ”’ Capability model: the runtime mounts a directory like --dir=.::./data β€” the module sees ./data but cannot see /etc/passwd or /
  • πŸͺΆ No syscalls smuggled in β€” every interaction with the OS is an explicit WASI import

5. Spin: The "WASM Containers" Pattern

Spin (created by Fermyon, donated to the CNCF in 2024 β†’ SDK now lives under the spinframework org) is the easiest entry to server-side WASM. You write a normal-looking Go HTTP handler; the Spin SDK adapts it to a wasi-http component:

// main.go β€” Spin Go SDK
package main

import (
    "fmt"
    "net/http"
    spinhttp "github.com/spinframework/spin-go-sdk/v2/http"
)

func init() {
    spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintln(w, `{"status":"ok"}`)
    })
}

func main() {}
# spin.toml
spin_manifest_version = 2
[application]
name = "quicknotes-wasm"
version = "0.1.0"
[[trigger.http]]
route = "/time"
component = "moscow-time"
[component.moscow-time]
source = "main.wasm"
allowed_outbound_hosts = []
[component.moscow-time.build]
command = "tinygo build -target=wasip1 -buildmode=c-shared -no-debug -o main.wasm ."
$ spin new -t http-go moscow-time   # scaffold the CURRENT template
$ spin build                        # runs tinygo under the hood
$ spin up                           # serves on :3000
$ spin deploy                       # β†’ Fermyon Cloud (free tier)
  • ⚑ Spin instantiates a fresh WASM instance per request β€” true serverless. Cold starts in single-digit milliseconds
  • 🌐 Each component carries its own capability list β€” it cannot reach code or hosts it wasn't granted
  • πŸ”‘ -buildmode=c-shared is required: it makes TinyGo export the handler symbols the Spin host calls (omit it β†’ HTTP 500)

⚠️ Tooling churn warning. WASM server tooling moves fast. The SDK import path changed from github.com/fermyon/spin/sdk/go/v2 to github.com/spinframework/spin-go-sdk/v2 with the CNCF donation. Always spin new -t http-go to get the current template rather than copy-pasting an old spin.toml.


6. The Older Model: WAGI ("CGI for WASM") β€” Deprecated

Before the wasi-http Component Model matured, Spin offered a WAGI executor that mapped HTTP onto stdin/stdout, CGI-style: the module read request info from environment variables, wrote the response to stdout. It was the simplest possible Go-to-WASM port.

# ❌ DEPRECATED β€” removed in Spin 3.x
[component.quicknotes]
source = "main.wasm"
executor = { type = "wagi" }      # `spin up` rejects this field in 3.x
  • πŸͺ¦ Spin 3.x removed the inline WAGI executor. Modern Spin uses the wasi-http component model (Section 5)
  • 🧰 The WAGI pattern still lives on in bare wasmtime run: a standalone WASI module reads env + stdin, writes stdout. Lab 12's Bonus uses exactly this to contrast the two execution models β€” Spin's persistent wasi-http server vs wasmtime's per-invocation CLI

7. Compared with Traditional Containers

Dimension Docker container (Lab 6) WASM module (Lab 12)
Cold start ~200 ms – 2 s ~1 ms
Size 15-200 MB 1-3 MB
CPU portability Per-arch image Single artifact
Isolation Linux kernel namespaces Capability sandbox (stronger than namespaces, weaker than VM)
Mature ecosystem βœ… massive 🟑 growing fast, still rough edges
Multi-tenant safety OK Excellent
Long-running, stateful workloads βœ… ⚠️ (single-process model)
apt install arbitrary OS deps βœ… ❌ (no shell, no syscalls beyond WASI)
  • 🎯 Sweet spot for WASM: edge, plugin systems, multi-tenant SaaS request handlers, IoT, browser companion code
  • πŸͺ€ Bad fit for WASM (today): heavy DB clients, persistent processes, anything needing arbitrary syscalls

8. WASM in Kubernetes: containerd Shims

graph TB
    K["☸️ Kubernetes Pod"] --> CR["🐳 containerd"]
    CR --> R1["πŸ₯ͺ runc (Linux containers)"]
    CR --> R2["πŸ§ͺ containerd-shim-wasmtime<br/>or -wasmedge -spin"]
    R2 --> W["πŸ“¦ .wasm artifact"]
Loading
  • 🧩 A RuntimeClass: wasmtime (Kubernetes feature) lets a pod's containers run as WASM modules instead of Linux containers
  • 🌐 Major cloud K8s providers (GKE, AKS, EKS) support this in preview as of 2024-2025
  • 🎁 K8s scheduling + WASM runtime = put microservice plugins next to their users at the edge, with strong isolation

9. The Cloudflare / Fastly / Vercel Edge Model

These platforms run WASM modules at 300+ POPs worldwide:

  • 🌍 Your request hits the nearest POP (typically <30 ms RTT)

  • ⚑ A fresh WASM instance starts per request β€” measured in microseconds

  • πŸ’Έ Pricing is per-request, not per-instance-hour β€” perfect for spiky traffic

  • πŸ›‘οΈ Each tenant's WASM is sandboxed from every other tenant's β€” multi-tenant by design

  • 🎯 This is where WASM-on-server has decisively won β€” edge platforms, where Cold-Start latency dominates the user experience


10. The Honest Trade-offs (2026)

βœ… WASM wins ⚠️ WASM struggles
Cold start, size, portability Library ecosystem (esp. databases)
Multi-tenant isolation Long-running stateful workloads
Browser + server with same artifact Debugging tools (still maturing)
Sandbox is "deny by default" TinyGo (the Go-to-WASM compiler) doesn't support all of Go's stdlib
Standard ABI (WASI) growing Cgo, reflection, large goroutine fleets

πŸ’‘ The Go-specific gotcha: standard go build -o main.wasm produces a WASM module that runs only in browsers (with the Go runtime + JS glue). For server-side WASM you typically want TinyGo (tinygo build -target=wasi), which produces a smaller, WASI-compliant binary with a stripped-down stdlib.


11. Lab 12 Preview

Lab 12 is the WASM bonus lab β€” itself worth 10 pts, structured 4+4+2:

  • πŸ”¨ Task 1 (4 pts): Build a minimal time endpoint (returns current Moscow time as JSON) in Go β†’ WASM via TinyGo β†’ packaged as a Spin SDK wasi-http component, served by spin up. Compare deployed size vs the QuickNotes Docker image
  • 🏎️ Task 2 (4 pts): Benchmark request latency: warm Lab 6 Docker container vs warm spin up. Then compare cold-starts. Explain what dominates each curve
  • 🎁 Bonus (2 pts): Rebuild the same logic as a standalone WASI CLI module, run under bare wasmtime run, and contrast the two execution models (Spin's persistent wasi-http server vs wasmtime's per-invocation CLI)
  • πŸ“œ Deliverable: submissions/lab12.md β€” spin.toml, build sizes, perf numbers, written reflection

12. Resources

🎯 Remember: Containers won the 2014-2020 era by making "ship anywhere on Linux" trivial. WebAssembly is making a credible bid for the 2025-2030 era by adding "ship to any CPU, with capability-based safety, in 1 millisecond". Whether it dethrones containers or coexists with them is the open question of the decade.