架构
此页面解释了 Docker Sandboxes 的工作原理及其背后的设计决策。
为什么选择 microVM?
AI 编码代理需要构建镜像、运行容器并使用 Docker Compose。如果让代理访问你的主机 Docker 守护进程,它就能看到你的容器、拉取镜像,并直接在系统上运行工作负载。对于自主代码执行来说,这种访问权限过大。
将代理运行在容器中并不能解决这个问题。容器共享主机内核(或者在 Docker Desktop 的情况下,共享同一个虚拟机),无法安全地隔离需要自己 Docker 守护进程的东西。Docker-in-Docker 方法要么会损害隔离性(特权模式并挂载主机套接字),要么会产生嵌套守护进程的复杂性。
MicroVM 提供了所需的隔离边界。每个沙箱都获得一个带有私有 Docker 守护进程的虚拟机。代理可以在不访问你的主机 Docker 环境的情况下构建镜像、启动容器和运行测试。当你移除沙箱时,其中的所有内容——镜像、容器、软件包——都会被清除。
隔离模型
每个沙箱一个私有 Docker 守护进程
每个沙箱在其虚拟机中运行一个完整的 Docker 守护进程。该守护进程与你的主机和其他沙箱隔离。
主机系统(你的 Docker Desktop)
├── 你的容器和镜像
│
├── 沙箱虚拟机 1
│ ├── Docker 守护进程(隔离)
│ ├── 代理容器
│ └── 其他容器(由代理创建)
│
└── 沙箱虚拟机 2
├── Docker 守护进程(隔离)
└── 代理容器当代理运行 docker build 或 docker compose up 时,这些命令在沙箱内使用私有守护进程执行。代理只能看到它自己创建的容器。它无法访问你的主机容器、镜像或卷。
这种架构解决了一个基本约束:自主代理需要完整的 Docker 功能,但不能安全地共享你的 Docker 守护进程。
虚拟机监控程序级别的隔离
沙箱使用你系统的原生虚拟化技术:
- macOS:virtualization.framework
- Windows:Hyper-V 实验性
这提供了沙箱与主机之间的虚拟机监控程序级别的隔离。与共享主机内核的容器不同,虚拟机有各自独立的内核,无法访问定义边界之外的宿主资源。
这对安全意味着什么
虚拟机边界提供了:
- 进程隔离 - 代理进程运行在独立的内核中
- 文件系统隔离 - 只有你的工作区可访问
- 网络隔离 - 沙箱之间无法相互通信
- Docker 隔离 - 无法访问主机守护进程、容器或镜像
网络过滤为 HTTP/HTTPS 流量增加了一个额外的控制层。有关该机制的详细信息,请参见网络策略。
工作区同步
双向文件同步
你的工作区以相同的绝对路径同步到沙箱:
- 主机:
/Users/alice/projects/myapp - 沙箱:
/Users/alice/projects/myapp
更改会双向同步。在主机上编辑文件,代理就能看到它。代理修改文件,你就能在主机上看到更改。
这是文件同步,而不是卷挂载。文件在主机和虚拟机之间复制。这种方法适用于不同的文件系统,并能保持一致的路径,不受平台差异的影响。
路径保留
保留绝对路径意味着:
- 错误消息中的文件路径在主机和沙箱之间匹配
- 配置文件中的硬编码路径能正确工作
- 构建输出引用的路径你可以在主机上找到
代理看到与你相同的目录结构,这减少了调试问题或审查更改时的困惑。
存储和持久性
什么会持久化
当你创建沙箱时,以下内容会一直保留,直到你移除它:
- Docker 镜像和容器 - 由代理构建或拉取
- 安装的软件包 - 使用 apt、yum 等添加的系统软件包
- 代理状态 - 凭据、配置、历史记录
- 工作区更改 - 创建或修改的文件会同步回主机
什么是临时的
沙箱是轻量级的,但不是无状态的。它们在运行之间保持持久性,但彼此隔离。每个沙箱维护自己的:
- Docker 守护进程状态
- 镜像缓存
- 软件包安装
当你使用 docker sandbox rm 移除沙箱时,整个虚拟机及其内容都会被删除。在沙箱内构建的镜像、安装的软件包,以及未同步到你工作区的任何状态都会消失。
磁盘使用
每个沙箱消耗磁盘空间用于:
- 虚拟机磁盘镜像(随着你构建镜像和安装软件包而增长)
- 在沙箱内拉取或构建的 Docker 镜像
- 容器层和卷
多个沙箱不共享镜像或层。每个沙箱都有自己隔离的 Docker 守护进程和存储。
网络
互联网访问
沙箱通过你主机的网络连接具有出站互联网访问权限。代理可以安装软件包、拉取镜像和访问 API。
一个 HTTP/HTTPS 过滤代理运行在你的主机上,可通过 host.docker.internal:3128 访问。代理会自动使用此代理进行出站 Web 请求。你可以配置网络策略来控制允许哪些目标。参见网络策略。
沙箱隔离
沙箱之间无法相互通信。每个虚拟机都有自己的私有网络命名空间。一个沙箱中的代理无法访问另一个沙箱中的服务或容器。
沙箱也无法访问你主机的 localhost 服务。虚拟机边界阻止了对运行在你主机上的服务的直接访问。
生命周期
创建和运行
docker sandbox run 初始化一个带有指定代理工作区的虚拟机,并在现有沙箱中启动代理。你可以停止和重启代理而无需重新创建虚拟机,从而保留已安装的软件包和 Docker 镜像。
docker sandbox create 初始化带有工作区的虚拟机,但不会自动启动代理。这可以将环境设置与代理执行分开。
状态管理
沙箱会一直保留,直到被显式移除。停止代理不会删除虚拟机。这意味着:
- 已安装的软件包仍然可用
- 构建的镜像保持缓存状态
- 环境设置在运行之间持续存在
使用 docker sandbox rm 删除沙箱并回收磁盘空间。
与其他方案的比较
了解何时使用沙箱与其他方法:
| 方法 | 隔离级别 | 代理 Docker 访问 | 对主机的影响 | 使用场景 |
|---|---|---|---|---|
| 沙箱 (microVM) | 虚拟机监控程序级别 | 私有守护进程 | 无 - 完全隔离 | 自主代理构建/运行容器 |
| 带套接字挂载的容器 | 内核命名空间 | 主机守护进程(共享) | 代理可以看到所有主机容器 | 需要 Docker CLI 的可信工具 |
| Docker-in-Docker | 嵌套容器 | 私有守护进程(复杂) | 中等 - 需要特权模式 | CI/CD 环境 |
| 主机执行 | 无 | 主机守护进程 | 完全 - 直接系统访问 | 可信人员的手动开发 |
沙箱以更高的资源开销(虚拟机 + 守护进程)换取完全的隔离。当你需要轻量级打包但不需要 Docker 访问时,使用容器。当你需要给某个自主实体完整的 Docker 功能但又不能信任它访问你的主机环境时,使用沙箱。