数据截至 (上游 commit aae05d825bb5)
Microsandbox — 总览
30 秒导读: Microsandbox 是一个本地运行时,把不可信的代码(AI agent 生成的代 码、插件、CI 任务、爬虫……)塞进一个硬件级隔离的微型虚拟机(microVM)里跑。用起来像 Docker——同样是
image / exec / stop那套工作流、同样吃标准 OCI 镜像——但底座换成了微VM:平均启动 < 100ms、没有长驻守护进程、可以直接嵌进你的应用代码。这一章只给全景与路由:讲清它是什么、大盘怎么转、每个部件干什么、主线怎么走一遍,然后把你送到对应的深入章节。
1. 这是什么(零基础也能懂)
一句话定义: Microsandbox 是一个用微VM运行不可信负载的本地运行时——你给它一个容器镜像和一条命令,它在一个和你主机内核隔离的小虚拟机里把命令跑起来,再把结果交回给你。
它要解决的问题: 你在写一个 AI agent,想让模型生成的代码真的跑起来看看结果。但这段代码不可信——可能删你的文件、读你的密钥、把机器当跳板。传统选择要么不安全(直接在主机跑),要么太重、太慢(开一台完整虚拟机、或依赖一个常驻的容器守护进程)。
它的取舍: 用 microVM 拿到硬件级隔离(比容器强的边界),同时靠 libkrun 这类轻量 VMM 把启动压到毫秒级、把整个 VM 变成你进程的一个子进程——不需要先起服务器、不需要后台 daemon。README 把这几点列为核心卖点(README.md:28、README.md:36-38)。
它能做什么(功能一览):
| 能力 | 说明 |
|---|---|
| 硬件隔离 | microVM 技术,隔离边界在硬件层(README.md:32) |
| 跨平台 | Linux(KVM)、macOS(Apple Silicon/HVF)、Windows(WHP) |
| OCI 兼容 | 直接跑 Docker Hub / GHCR 等标准镜像(README.md:34) |
| 毫秒启动 | M1 上客机启动平均 < 100ms(README.md:36) |
| 可嵌入 | Sandbox::builder(...).create() 在你代码里直接开 VM,无 daemon(README.md:37) |
| 不泄露的密钥 | 密钥在主机侧注入网络流量,从不进入 VM(README.md:38,见 05) |
| 长驻会话 | 沙箱可 detached 后台运行,适合长生命周期会话 |
用起来什么样: 一段 Rust SDK 代码就是它最直观的样子——建 builder、create()(此时才真正开一台 microVM 子进程)、exec() 在里面跑命令、stop() 关掉:
use microsandbox::Sandbox;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let sandbox = Sandbox::builder("my-sandbox") // 起一个名字
.image("python") // 用哪个 OCI 镜像
.cpus(1).memory(512) // 资源上限
.create().await?; // ← 这一步才真正 boot 一台 microVM
let output = sandbox
.exec("python", ["-c", "print('Hello from a microVM!')"])
.await?; // 在客机里跑命令
println!("{}", output.stdout()?);
sandbox.stop().await?; // 关机
Ok(())
}
来源:README.md:133-155。Python / TypeScript / Go 也有对等 SDK(README.md:160-244),命令行则是 msb run python -- ...(README.md:262)。
一句话类比: 把它当"进程级的 Docker,但每个容器其实是一台一秒就开机的迷你虚拟机"。 你熟悉的 image / exec / volume 心智全部适用,只是隔离边界从"共享内核的 namespace"升级成了"独立内核的 microVM"。
2. 顶层全景(它大概怎么转)
怎么读这张图
从上到下是三层:你的代码(SDK/CLI)→ 一个叫 msb sandbox 的主机进程(它一个人同时扛着 VMM 和 relay)→ 微VM 客机里的 agentd(客机的 PID 1)。左右两侧是三个支撑子系统(镜像、文件系统、网络),它们为这条主线准备"能启动的根文件系统""能读写的盘""能受控上网的栈"。虚线是控制/数据流。
你的应用 / 终端
┌───────────────────────────┐
│ SDK (Rust/Py/TS/Go) │ Sandbox::builder(...).create().exec().stop()
│ 或 msb CLI │
└─────────────┬─────────────┘
│ fork+exec 隐藏子命令 `msb sandbox`,读回启动 JSON 拿 PID
│ 之后经 Unix socket 收发"帧"
▼
┌──────────────────────────────────────────────┐ 主机侧,单进程
│ `msb sandbox` 进程 │
│ ┌───────────────┐ ┌──────────────────┐ │
│ │ VMM (libkrun) │◄────►│ AgentRelay │ │
│ │ vm::enter() │ ring │ 搬运帧的中转 │ │
│ └───────┬────────┘ 缓冲 └────────┬─────────┘ │
└──────────┼───────────────────────┼────────────┘
│ virtio 设备 │ virtio-console "agent" 口
▼ ▼
┌──────────────── ──────────────────────────────┐ 客机内,microVM
│ agentd (Linux, PID 1) │
│ init 挂载 → 会话循环:收帧、起子进程、回帧 │
└──────────────────────────────────────────────┘
支撑子系统(为主线备料,不在热路径上按需参与):
image ──► OCI 拉取/缓存/合并 ──► 只读根镜像(EROFS)+ 可写上层(ext4)
filesystem ──► virtio-fs 后端(passthrough/memfs/dualfs):主机目录穿透 + 写时复制
network ──► smoltcp 用户态网络栈:DNS 过滤 / TLS 拦截 / 端口发布 / 密钥注入
关键点:VMM 和 relay 在同一个进程里。 运行时 crate 的文档原话是"运行在单个 sandbox 进程内的、统一的 VM + relay 逻辑"(crates/runtime/lib/lib.rs:1-3)。所以主机侧不是"VMM 一个进程 + 网络代理一个进程",而是一个 msb sandbox 子进程一肩挑。
主机↔客机之间不是普通 socket,是 virtio-console。 relay 从 console 后端的环形缓冲区读 agentd 写来的帧、往回写主机的帧(crates/runtime/lib/relay.rs:1-4),协议本身是 CBOR 编码的帧,跑在一个名为 agent 的 virtio-console 口上(crates/protocol/lib/lib.rs:1-3、AGENT_PORT_NAME 见 crates/protocol/lib/lib.rs:79)。细节见 02。
部件一句话职责
| 部件 | 一句话职责 | 所在 crate / 路径 |
|---|---|---|
| SDK | 对外 API:builder → create/exec/stop,把命令翻成帧发给 relay | sdk/rust(sdk/rust/lib/lib.rs) |
| runtime | msb sandbox 进程的内核:vm::enter() 起 VMM + AgentRelay 中转 | crates/runtime(crates/runtime/lib/lib.rs) |
| agentd | 客机里的 PID 1:先 init 挂载,再进会话循环起子进程、回结果 | crates/agentd(crates/agentd/lib/lib.rs) |
| protocol | 主机↔客机共享的帧类型与时序常量(CBOR-over-virtio-serial) | crates/protocol(crates/protocol/lib/lib.rs) |
| image | OCI 镜像拉取、内容寻址缓存、层合并,产出 EROFS 只读根 + ext4 上层 | crates/image(crates/image/lib/lib.rs) |
| filesystem | virtio-fs 后端(passthrough/memfs/dualfs)+ 内嵌的 agentd 二进制 | crates/filesystem(crates/filesystem/lib/lib.rs) |
| network | smoltcp 用户态网络栈:策略、DNS、TLS、端口发布、密钥注入 | crates/network(crates/network/lib/lib.rs) |
| cli | msb 命令:create/exec/stop/pull/ls…,以及隐藏的 msb sandbox 入口 | crates/cli(crates/cli/lib/lib.rs) |
| utils | 共享常量(~/.microsandbox 目录布局、二进制名、版本)与小工具 | crates/utils(crates/utils/lib/lib.rs) |
还有一批非主线的支撑 crate:
db(sea-orm,记沙箱/卷/运行状态)、migration(数据库迁移)、metrics/metrics-collector(CPU/内存/网络实时指标)、packages/agent-client(SDK 底层用的帧客户端)、packages/microsandbox-types(跨语言共享类型,ts-rs 导出)。它们在总库里点到为止,不单独开章。
主线走一遍(高层,不进代码)
一次 Sandbox::builder("my-sandbox").image("python").create().await? 到 exec 再到 stop,大致经历:
- 准备料。 SDK 解析 builder 配置。若镜像没缓存,
image子系统先拉 OCI 镜像、合并层,产出只读根(EROFS)+ 可写上层(ext4)(03)。 - 开进程。 SDK 把配置拼成参数,fork+exec 一个隐藏子命令
msb sandbox,并从它 stdout 读回一段启动 JSON 拿到 PID(sdk/rust/lib/runtime/spawn.rs:1-5、:257、:259spawn_sandbox)。 - VMM 接管。
msb sandbox调microsandbox_runtime::vm::enter(config),这个函数永不返回——它变身成 VMM,把 microVM 拉起来(crates/cli/lib/sandbox_cmd.rs:295、crates/runtime/lib/vm.rs:493pub fn enter(config: Config) -> !)。 - 客机启动。 客机内核起来后,agentd 作为 PID 1 先做同步 init(挂 virtio-fs 根、runtime 目录、网络),再进入异步会话循环(
crates/agentd/bin/main.rs:1-4、crates/agentd/lib/init.rs:28、crates/agentd/lib/session.rs)。 - 执行命令。
sandbox.exec(...)(sdk/rust/lib/sandbox/mod.rs:1097)把请求编成帧,经主机的AgentRelay(crates/runtime/lib/relay.rs:97)搬过 virtio-console;agentd 起子进程、把 stdout/stderr 帧回传(02)。 - 关机。
sandbox.stop()(sdk/rust/lib/sandbox/mod.rs:801)触发协议约定的关机时序:agentd 同步落盘、卸载、请求内核 poweroff,主机侧有兜底超时窗口(crates/protocol/lib/lib.rs:16-63)。生命周期全貌见 01。
3. 阅读地图(建议顺序)
各章由浅入深,建议顺着读;也可按任务直接跳。
| 顺序 | 章节 | 讲什么 | 什么时候读它 |
|---|---|---|---|
| 0 | index.md(本章) | 全景 + 路由 | 先读:建立大盘心智 |
| 1 | 01-lifecycle.md | 进程模型与沙箱生命周期:SDK 一行 → msb sandbox → VMM 启动 → 执行 → 退出 | 想搞懂"一次 create/exec/stop 到底发生了什么" |
| 2 | 02-protocol-relay.md | 主机↔客机通信:二进制帧协议、virtio-console relay、agentd 会话循环 | 关心 exec 的数据怎么流、协议怎么编 |
| 3 | 03-image-rootfs.md | OCI 镜像 → 可启动根:拉取、缓存、层合并、EROFS 只读镜像 | 关心镜像从哪来、根文件系统怎么造 |
| 4 | 04-guest-filesystem.md | 客机文件系统:virtio-fs 后端、写时复制、主机穿透 | 关心客机里读写怎么落到主机、卷怎么工作 |
| 5 | 05-network-security.md | 用户态网络栈与安全边界:smoltcp、DNS 过滤、TLS 拦截、不泄露的密钥 | 关心网络隔离、策略、密钥注入 |
推荐路线: 先 01 建立生命周期骨架 → 02 补上"通信"这条主动脉 → 之后按兴趣挑 03/04/05 三个支撑子系统(彼此独立,任意顺序)。