2023 年 3 月,Docker 宣布支持 Wasm 容器——你可以用 docker run 运行一个 .wasm 文件,就像运行 Linux 容器一样。这不是噱头,而是容器运行时演进的真实方向。
WebAssembly(Wasm)最初为浏览器设计,但它的沙箱隔离、跨平台、快速启动、小体积等特性,恰好解决了 Linux 容器的痛点:启动慢(需要创建 Namespace/Cgroup)、体积大(需要完整 rootfs)、安全弱(共享内核)。WASI(WebAssembly System Interface)定义了 Wasm 访问操作系统的标准接口,让 Wasm 可以在服务器端运行。
本章将深入 Wasm 容器的架构和实现,理解它如何与 OCI 规范集成,以及与 Linux 容器的对比。
WebAssembly 的故事始于浏览器。2017 年,W3C 将 WebAssembly 推荐为 Web 标准,它作为一种低级字节码格式,让 C/C++/Rust 等语言编译后的程序能在浏览器中以接近原生的速度运行。但 Wasm 的沙箱隔离、跨平台、快速启动等特性很快引起了服务端开发者的注意——如果 Wasm 能跑在浏览器之外呢?
2019 年,Fastly 的 Luke Wagner 提出 WASI(WebAssembly System Interface),定义了 Wasm 访问文件系统、网络、时钟等操作系统资源的标准接口。WASI 的出现让 Wasm 从浏览器走向了服务器。此后,WasmEdge、Fermyon Spin、runwasi 等运行时和工具相继涌现,让 Wasm 模块可以像容器一样运行。2023 年 3 月,Docker 官方宣布支持 Wasm 容器——docker run 可以直接运行 .wasm 文件,标志着容器运行时的边界从”Linux 进程”扩展到了”Wasm 模块”。容器不再只是 Linux 进程的隔离单元,Wasm 容器证明了:只要遵循 OCI 规范,任何运行时都可以成为容器。
前置知识
- Ch05 OCI 规范:Wasm 容器遵循 OCI 镜像规范和运行时规范,只是 payload 从 Linux rootfs 变成了 Wasm 模块
- WebAssembly 基础概念:栈式虚拟机(基于操作数栈执行指令)、线性内存(一段连续的可寻址字节数组,Wasm 模块唯一的内存模型)、模块化(Wasm 程序以模块为单位,可导入/导出函数和内存)
如果你不熟悉 WebAssembly,可以先阅读 Mozilla 的 WebAssembly 概念指南,了解 Wasm 的基本执行模型和核心概念后再继续阅读。
一、WebAssembly 基础
1.1 Wasm 的核心特性
| 特性 | 说明 | 对容器的意义 |
|---|---|---|
| 沙箱隔离 | Wasm 模块在独立沙箱中运行 | 安全隔离,无需 Namespace |
| 跨平台 | Wasm 字节码与平台无关 | 一次编译,到处运行 |
| 快速启动 | Wasm 模块即时编译启动 | 毫秒级启动 |
| 小体积 | Wasm 二进制比原生二进制更小 | 镜像体积极小 |
| 确定性 | Wasm 执行是确定性的 | 可重现的执行 |
| 线性内存 | Wasm 使用线性内存模型 | 内存安全 |
1.2 Wasm 的执行模型
1.3 Wasm vs Linux 容器
| 维度 | Linux 容器 | Wasm 容器 |
|---|---|---|
| 隔离方式 | Namespace + Cgroup | Wasm 沙箱 |
| 内核依赖 | 依赖 Linux 内核 | 不依赖(WASI 接口) |
| 启动时间 | 50-200ms | 1-10ms |
| 镜像大小 | MB~GB 级 | KB~MB 级 |
| 语言支持 | 任意语言 | Rust/C/C++/Go/Python/… |
| 系统调用 | 完整 Linux 系统调用 | WASI 子集 |
| 兼容性 | 完整 Linux 兼容 | 有限(无 fork/exec 等) |
二、WASI:WebAssembly System Interface
2.1 WASI 的设计目标
WASI 定义了 Wasm 模块访问操作系统的标准接口,遵循最小权限原则——默认不允许任何操作,必须显式授权:
// Rust 编译为 Wasm + WASIuse std::fs::File;use std::io::{Read, Write};
fn main() { // 读取文件(需要 fs 权限) let mut file = File::open("/app/data/input.txt").unwrap(); let mut content = String::new(); file.read_to_string(&mut content).unwrap();
// 写入文件(需要 fs 权限) let mut output = File::create("/app/data/output.txt").unwrap(); output.write_all(content.as_bytes()).unwrap();}2.2 WASI 的核心接口
| 接口 | 功能 | 类似 Linux |
|---|---|---|
wasi_snapshot_preview1.fd_read | 读取文件描述符 | read() |
wasi_snapshot_preview1.fd_write | 写入文件描述符 | write() |
wasi_snapshot_preview1.path_open | 打开文件 | openat() |
wasi_snapshot_preview1.path_filestat_get | 获取文件状态 | fstat() |
wasi_snapshot_preview1.sock_accept | 接受连接 | accept() |
wasi_snapshot_preview1.sock_recv | 接收数据 | recv() |
wasi_snapshot_preview1.sock_send | 发送数据 | send() |
wasi_snapshot_preview1.clock_time_get | 获取时间 | clock_gettime() |
wasi_snapshot_preview1.environ_get | 获取环境变量 | getenv() |
wasi_snapshot_preview1.proc_exit | 退出进程 | exit() |
2.3 WASI 的权限模型
# WasmEdge 的权限控制wasmedge --env "KEY=VALUE" \ --dir /app/data:/host/data \ # 文件系统权限 --net host \ # 网络权限 myapp.wasm
# 只授予必要的权限(最小权限原则)wasmedge --dir /app/data:/host/data:ro \ # 只读 myapp.wasm2.4 WASI 能力与限制对比
WASI 各子规范提供了不同的能力,但也有明确的边界限制:
| WASI 子规范 | 能力 | 限制 | 状态 |
|---|---|---|---|
| wasi-fs | 文件读写、目录遍历 | 无 inotify/fanotify,无 mmap 写 | 稳定 |
| wasi-clock | 系统时钟、随机数 | 无高精度定时器 | 稳定 |
| wasi-sockets | TCP/UDP 连接 | 无 raw socket,无 packet socket | 稳定 |
| wasi-http | HTTP 请求/响应处理 | 仅 HTTP/1.1,无 HTTP/2 推送 | 稳定 |
| wasi-io | 异步 IO 流 | 需 Component Model 支持 | 草案 |
| wasi-nn | 神经网络推理 | 仅推理,无训练;依赖运行时插件 | 稳定 |
| wasi-crypto | 加密签名/哈希 | 无 TLS 握手,无密钥管理 | 草案 |
| wasi-threads | 多线程共享内存 | 无 fork/exec,无信号量 | 稳定 |
WASI 最大的限制是没有进程管理能力——不能 fork、exec、signal,这意味着数据库(依赖 fork 做并发控制)和 Shell 工具(依赖 exec 调用子进程)无法在 Wasm 容器中运行。这是架构层面的取舍,不是实现缺陷。
2.5 WASI 的演进
三、Wasm 运行时
3.1 WasmEdge
WasmEdge 是 CNCF 孵化项目,专为云原生场景设计的 Wasm 运行时:
# 安装 WasmEdgecurl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash
# 编译 Rust 为 Wasmrustup target add wasm32-wasip2cargo build --target wasm32-wasip2
# 运行 Wasm 模块wasmedge target/wasm32-wasip2/debug/myapp.wasm
# 运行 HTTP 服务器wasmedge --net host target/wasm32-wasip2/debug/myserver.wasm# 服务器监听在 0.0.0.0:80803.2 WasmEdge 的架构
3.3 其他 Wasm 运行时
| 运行时 | 特点 | 适用场景 |
|---|---|---|
| WasmEdge | CNCF 孵化,云原生 | 服务器端应用 |
| Wasmtime | Bytecode Alliance,标准实现 | 通用 Wasm 运行 |
| Wasmer | 多后端(Cranelift/LLVM) | 通用 Wasm 运行 |
| Wamr | Intel 开源,轻量级 | 嵌入式/IoT |
| V8 | Google,浏览器级性能 | Node.js/Deno |
四、Wasm 容器与 OCI 集成
4.1 runwasi:containerd 的 Wasm shim
runwasi 是 containerd 的 Wasm shim,让 Wasm 模块可以通过 containerd/CRI 管理:
4.2 Docker 的 Wasm 支持
# 安装 Docker Wasm 集成docker run --rm --runtime=io.containerd.wasmedge.v1 \ -wasi-dir /app/data \ myapp-wasmimage:latest
# Wasm 镜像的 DockerfileFROM scratchCOPY --from=builder /app/target/wasm32-wasip2/release/myapp.wasm /app.wasmENTRYPOINT ["/app.wasm"]4.3 Wasm 镜像的 OCI 格式
Wasm 镜像遵循 OCI Image Spec,但使用不同的媒体类型:
| 组件 | Linux 容器 | Wasm 容器 |
|---|---|---|
| Config 媒体类型 | oci.image.config.v1+json | oci.image.config.v1+json |
| Layer 媒体类型 | oci.image.layer.v1.tar+gzip | application/wasm |
| 层内容 | tar 归档(文件系统) | Wasm 二进制 |
| rootfs | 完整 Linux 文件系统 | 不需要(Wasm 自包含) |
五、Wasm 容器的应用场景
5.1 场景对比
| 场景 | Linux 容器 | Wasm 容器 | 推荐 |
|---|---|---|---|
| 微服务 | 成熟 | 快速启动 | Wasm |
| Serverless/FaaS | 冷启动慢 | 毫秒级启动 | Wasm |
| 插件系统 | 需要隔离 | 天然沙箱 | Wasm |
| 数据库 | 完整兼容 | 无 fork/exec | Linux |
| AI 推理 | GPU 支持 | wasi-nn | 看场景 |
| 全栈应用 | 完整兼容 | 有限兼容 | Linux |
5.2 Wasm 容器的性能
# 启动时间对比# Linux 容器time docker run --rm alpine echo hello# real 0m0.350s
# Wasm 容器time wasmedge myapp.wasm# real 0m0.005s ← 快 70x
# 镜像大小对比# Linux 容器: alpine = 7.3MB# Wasm 容器: myapp.wasm = 2MB六、动手实践
6.1 构建和运行 Wasm 容器
Wasm 容器镜像通常只有几 MB(对比 Alpine 容器的 5MB+ 和 Ubuntu 容器的 70MB+),因为 Wasm 二进制不需要包含操作系统库。启动时间在毫秒级,因为不需要创建 Namespace 和 Cgroup——Wasm 沙箱本身就是隔离边界。
#!/bin/bash# 构建和运行 Wasm 容器
# 1. 创建 Rust 项目cargo new --name myapp myapp-wasmcd myapp-wasm
# 2. 修改 Cargo.tomlcat > Cargo.toml << 'EOF'[package]name = "myapp"version = "0.1.0"edition = "2021"
[lib]crate-type = ["cdylib"]
[dependencies]wasi = "0.11"EOF
# 3. 编写代码cat > src/lib.rs << 'EOF'use wasi::http::*;
#[no_mangle]pub extern "C" fn _start() { println!("Hello from Wasm container!");}EOF
# 4. 编译为 Wasmrustup target add wasm32-wasip2cargo build --target wasm32-wasip2 --release
# 5. 运行wasmedge target/wasm32-wasip2/release/myapp.wasm# Hello from Wasm container!
# 6. 构建 Docker Wasm 镜像cat > Dockerfile << 'EOF'FROM scratchCOPY --chmod=755 target/wasm32-wasip2/release/myapp.wasm /app.wasmENTRYPOINT ["/app.wasm"]EOF
docker buildx build --platform wasm32-wasip2 -t myapp-wasm .6.2 性能基准测试
#!/bin/bash# Wasm vs Linux 容器性能对比
echo "=== 启动时间 ==="echo -n "Linux 容器: "time (for i in {1..100}; do docker run --rm alpine echo hi > /dev/null; done) 2>&1 | grep real
echo -n "Wasm 容器: "time (for i in {1..100}; do wasmedge myapp.wasm > /dev/null; done) 2>&1 | grep real
echo ""echo "=== 镜像大小 ==="echo -n "Linux 容器: "docker images alpine --format "{{.Size}}"echo -n "Wasm 容器: "ls -lh myapp.wasm | awk '{print $5}'七、Wasm 容器在 Kubernetes 中的部署
7.1 使用 runwasi 部署 Wasm 工作负载
# Kubernetes RuntimeClass for WasmapiVersion: node.k8s.io/v1kind: RuntimeClassmetadata: name: wasmtimehandler: wasmtimeoverhead: podFixed: memory: "10Mi" cpu: "50m"---
apiVersion: v1kind: Podmetadata: name: wasm-appspec: runtimeClassName: wasmtime containers: - name: app image: ghcr.io/example/myapp-wasm:latest env: - name: RUST_LOG value: info7.2 Wasm 容器的生态工具链
| 工具 | 功能 | 项目 |
|---|---|---|
| wasmtime | Wasm 运行时 | Bytecode Alliance |
| WasmEdge | 云原生 Wasm 运行时 | CNCF |
| runwasi | containerd Wasm shim | containerd |
| spin | Wasm 微应用框架 | Fermyon |
| wasm-pack | Rust→Wasm 打包工具 | wasm-pack |
| wasmer | 通用 Wasm 运行时 | Wasmer |
7.3 Wasm 组件模型(Component Model)
Wasm Component Model 是 Wasm 的下一代模块化架构:
Component Model 的关键特性:
- 跨语言互操作:不同语言编写的组件可以互相调用
- 类型化接口:组件之间的接口有明确的类型定义
- 版本化:组件可以独立版本化和升级
- 可组合:小组件可以组合成大应用
7.4 Wasm 容器的未来方向
| 方向 | 说明 | 进展 |
|---|---|---|
| WASI HTTP | 原生 HTTP 服务器支持 | 已实现 |
| WASI NN | 神经网络推理 | 已实现 |
| WASI Crypto | 加密操作 | 草案 |
| WASI Threads | 多线程支持 | 已实现 |
| Garbage Collection | GC 语言支持 | 已实现 |
| Component Model | 组件化架构 | 推进中 |
八、本章小结
上一章深入探讨了镜像构建与 BuildKit。
| 特性 | Wasm 容器 | Linux 容器 |
|---|---|---|
| 隔离方式 | Wasm 沙箱 | Namespace + Cgroup |
| 启动时间 | 1-10ms | 50-200ms |
| 镜像大小 | KB~MB | MB~GB |
| 内核依赖 | 不依赖 | 依赖 Linux 内核 |
| 兼容性 | 有限(WASI 子集) | 完整 Linux |
| 语言支持 | Rust/C/C++/Go/… | 任意语言 |
| OCI 兼容 | (扩展媒体类型) |
混合部署 Wasm 和 Linux 容器是当前最务实的方案——用 Wasm 容器跑 Serverless 函数和插件(利用快速启动+强隔离),用 Linux 容器跑数据库和有状态服务(利用完整兼容性)。Kubernetes 的 RuntimeClass 机制让这种混合部署非常自然。
Wasm 容器不是要取代 Linux 容器,而是补充。在 Serverless、插件系统、边缘计算等场景中,Wasm 容器的快速启动、小体积、强隔离是独特优势。但在需要完整 Linux 兼容性的场景(数据库、系统工具),Linux 容器仍是最佳选择。
参考
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






