DeepSeek 技术报告 | DSec:撑起 Agentic RL 训练的沙箱基础设施,每天 300 万沙箱怎么调度
DeepSeek 技术报告 | DSec:撑起 Agentic RL 训练的沙箱基础设施,每天 300 万沙箱怎么调度
原文:DeepSeek Elastic Compute (DSec): A Sandbox Infrastructure for Effective Agentic Training at Scale(arXiv:2609.22978v1,DeepSeek-AI × 清华)
1. 前言:训练 Agent,卡住的不只是 GPU
现在训 agent 模型,模型早就不是吐一段文字答案就完事了。它要真的去读代码库、调工具、跑命令、看报错、改文件,甚至操作浏览器和桌面应用。这种能力靠什么训出来?靠 RL——让模型在真实、隔离的执行环境里反复试错,用 exit code、stdout、测试通过率这些真实信号打分,再更新参数。
这里有个容易被忽略的东西:agentic RL 里的一次 rollout(指”当前模型和环境从头交互到任务结束”的一条完整轨迹),需要一个属于它自己的执行环境——任务的代码仓库、依赖、服务、评测脚本、agent 脚手架(scaffold)全都要装好,而且这个环境得足够接近一台真机,能跑未经改动的软件栈、包管理器、编译工具、浏览器、模拟器。一个训练 job 一次最多能甩出 32K 个这样的环境请求。
于是你会发现,训 agent 的瓶颈根本不只在 GPU。那几万个环境要在很短的时间窗口里全部就绪,不然 GPU 那边的 batch 就只能干等——环境没 ready,这个 instance 就没法用。更麻烦的是这些环境还有一堆别扭的性质:它们是有状态的(模型装的依赖、改的文件、起的服务,后面的命令还得能看见)、能跑真实软件栈、彼此强隔离,而且里面的模型不可信(它可能删文件、扫端口、翻日志找答案)。
这时候”给我起个 Docker 容器”这种朴素做法就完全不够看了。你要同时对付这么几件事:一次性突发起几万个、单机塞进几千个还不能让它们互相干扰、镜像语料上百 TB 但每个镜像几乎没人复用、GPU 训练随时被抢占导致跑了一半的 rollout 中断还得能续上。任何一件单拎出来都是个系统问题,凑一块就不是一个 runtime 能扛的了。
DSec(DeepSeek Elastic Compute)就是 DeepSeek 为这件事造的生产平台。先把它的定位立住:它不是一个沙箱 runtime,也不是 Docker 外面套的一层壳,而是一个弹性执行平台(elastic execution platform)。规模摆出来你就明白为什么这里非得是”平台”不可——单个 scale unit(一套独立部署单元)大约 160 个 CPU 节点、30K 核、~250TB DRAM,管理 PB 级镜像;一天开出 ~300 万个沙箱实例,峰值并发超过 38 万,创建速率持续 >5000 个/秒,单节点稳定跑到 3200 个容器或 800 个 microVM。
这篇文章就顺着论文,把 DSec 到底解决了哪些真问题、怎么解决的,一层层拆开讲。顺带说一句,这是 DeepSeek 挂在 arXiv 上的技术报告(由投 ACM SIGOPS ATC 2026 的两页扩展摘要扩写而来),不是已录用的会议论文,所以我标题里用”技术报告”框定,不给它安会议 tag。
2. 先把场景讲清楚:agentic RL 到底怎么跑
要理解 DSec 为什么长这样,得先看清它服务的负载。agentic 训练的整条 pipeline 包含环境和数据构建、RL rollout、reward 计算、policy 更新、周期性评测。其中给沙箱平台压力最大的是 rollout 和评测这两个阶段,因为它们规模大、并发高,还和训练 loop 紧紧咬在一起。
RL 训练本身是个三阶段的反馈循环:
- Rollout:当前模型在沙箱环境里交互——读文件、发工具调用(tool call)、跑命令、观察输出,为每个任务产出一条轨迹(trajectory)。
- Reward 计算:框架用真实执行信号给轨迹打分,比如 exit code、stdout、测试通过率,或者任务专属的 verifier。
- Policy 更新:RL 算法用收集到的轨迹和 reward 更新模型参数。
评测走的路径几乎一样,只是最后那条轨迹用来衡量模型能力,而不是更新参数。
真正让沙箱平台头疼的,是现在的系统还会把生成和优化流水线化(async rollout,异步 rollout):不等一整批轨迹都跑完,谁先完成就先补进来,以此维持高并发、削掉长尾 straggler(拖后腿的慢任务)的影响。这么一搞,系统里就同时挂着大量有状态的沙箱会话,而且它们可能在 policy 更新或者调度器抢占时被中断、之后又要恢复。并发、生命周期管理、状态一致性的要求一下子全上来了。
一句话总结这一节:沙箱不是”跑完一个命令就扔”的无状态函数,而是要活很久、要记住中间状态、还随时可能被打断再续上的长会话。记住这个画面,后面所有设计都是围着它转的。
3. 生产负载长什么样:三个绕不过去的挑战
论文很实诚地先花了一整章,用生产数据刻画这套负载到底”怪”在哪。这里的测量只统计容器(container)和 microVM 两种 backend,因为它们占了绝大多数实例数和资源消耗。看完你会发现,这是一组对传统执行服务很不友好的组合:请求成批突发、每个沙箱要保留很久的状态、CPU 需求稀疏但内存钉着不放、镜像working set 又杂又大到本地缓存根本装不下。
3.1 请求是”成批爆发”的,而且每个沙箱活很久
rollout 和评测任务不是匀速开沙箱,而是一批一批地砸下来。如下图,一个典型的容器任务动不动就要几千个沙箱,尾部直接冲到几万——容器的 p50 是 2528、p90 是 7969、p99 高达 16388 个沙箱,最大的生产 job 能一次请求 32K 个。这些请求还挤在很短的时间窗里到达,因为训练/评测的这一批 batch,必须等环境都 ready 了才能开工。

沙箱一旦创建,会走过三个大致的阶段,如下图:setup(准备依赖、工具、初始化状态)→ tool-call(模型在”生成下一步动作”和”在沙箱里执行”之间来回切,产生一串短促的 CPU 尖峰,中间大段时间都在等模型出下一个 action)→ test(验证结果,可能又短暂拉高一次资源)。

这张图里藏着两个关键事实:第一,setup 的成本要乘以突发规模——一次几万个沙箱一起 setup,任何一点单位开销都会被放大几万倍;第二,tool-call 之后 CPU 是断续的,但内存和累积状态一直钉着不放。这两点直接决定了后面所有的优化方向。
到底有多”钉着不放”?论文给了生命周期数据:容器的中位存活时间是 17.4 分钟、microVM 是 15.5 分钟,但两者的 p99 都超过 3 小时。也就是说,长尾里有大量沙箱活好几个小时,期间 CPU 大部分时间空闲,内存却一直占着。
3.2 CPU 稀疏得离谱,逼你必须超售
既然沙箱大部分时间在等模型出下一步,CPU 自然是稀疏的。稀疏到什么程度?如下图,大约 90% 的容器和 microVM 沙箱,平均只用了它们所申请 CPU 的不到 5%。

这就是为什么 overcommit(超售)在这里是天经地义的选择——所谓 overcommit,就是卖出去的资源总量超过物理总量,赌的是大家不会同时满负荷。CPU 这么闲,一个节点上多塞几倍的沙箱完全合理。论文里单节点稳定跑到 3200 个容器或 800 个 microVM,靠的就是超售。
但超售不是白吃的午餐。CPU 闲不代表内存闲。前面说了沙箱活很久、状态钉着,于是在高密度超售下,内存成了真正卡容量的东西。microVM 这里尤其惨,有两处浪费:一是镜像数据通过虚拟块设备读进来,会被 host 缓存一次、再被每个 guest 各自缓存一次,同一份数据在 guest-host 边界两边重复缓存;二是 guest 内部释放的空闲页,不主动上报的话根本不会还给 host,而 guest 申请的内存又通常远大于实际所需,它自己没什么压力去回收。这两点叠加长生命周期,就把 microVM 的内存超售空间死死卡住了。
CPU 这边也不是完全没坑。有些任务有严格的每步延迟预算(比如下棋 agent 每一步有固定时限),这类任务对干扰很敏感。光把其他任务的调度优先级调低还不够——后面 §7 会讲,问题出在 SMT(同时多线程,一个物理核跑两个硬件线程)的 sibling 上。
3.3 镜像语料又大又杂,本地缓存根本救不了
第三个挑战是镜像的体量。一周内活跃的环境 artifact 加起来超过 130TB,远超单节点能存下的量。更要命的是复用率极低:如下图,容器镜像的 fanout(一个镜像被多少个沙箱用到)中位数只有 3、p90 也才 28;microVM 更极端,中位数是 1、p90 是 3。

fanout 这么低意味着本地镜像缓存基本没用——working set 太杂了,一个节点根本吃不下。于是一旦突发一批创建请求,拉镜像就不可避免,全压在镜像分发链路上。
而且拉全镜像特别浪费,因为沙箱运行时其实只碰镜像里很小一块数据。看下面这张表,不同语言的容器镜像,runtime 实际访问的数据只占 4.2%–13.3%:

镜像动辄 4~12GB,结果只用 4%~13%,你还非要先把整个镜像拉下来解压——这不是纯纯的浪费吗。这直接引出了后面的按需加载(on-demand loading)。
3.4 小结:三个耦合在一起的挑战
把上面这些性质拧一拧,就是 DSec 要同时对付的三个挑战:
- 挑战 A:setup 太贵。突发创建 + 环境组件各自独立演进,要求一条 setup 路径的成本不能随”每个沙箱重复解包”线性增长。
- 挑战 B:高密度不安全。CPU 稀疏让高密度超售有价值,但长生命周期、钉着的内存、混杂的延迟要求,让无脑超售变得危险。
- 挑战 C:镜像分发又贵又扰。语料巨大、fanout 低、runtime 访问比例低,让”急切地拉全镜像”既贵又干扰正在跑的沙箱。
下面三章(§5–§7)就是分别啃这三块骨头。
4. DSec 的架构全景
讲机制之前,先看一眼一个创建请求从 SDK 到跑起来,中间要过哪些组件。整套架构分成两大块:cluster-level services(集群级服务)负责请求入口、身份鉴权、沙箱放置、集群健康视图;sandbox runtime(节点级运行时)负责本地准入、创建、执行、资源回收,底下靠 3FS 存镜像数据。

一个创建请求的旅程是这样的:先到 IAM(Identity and Access Management,身份与访问管理)做鉴权 → 到 placement engine(放置引擎),它根据 watcher 收集的健康和负载信息挑一个目标节点 → apiserver 把请求转发给那个节点上的 edge → edge 检查本地容量,够就用指定 backend 建沙箱,不够就拒。几个值得记的设计点:
- apiserver 是唯一的通信通道。训练/评测代码跑在可信的 GPU 服务器上,沙箱里跑的是不可信的模型生成代码、还可能连外网,两边是网络隔离的,apiserver 是唯一允许的桥。而且它不存任何 per-sandbox 状态——每个 sandbox ID 自带它归属的 edge 信息,任何一个 apiserver 实例都能解析并直接转发,所以入口层能水平扩展。
- placement engine 和 watcher 都不需要持久状态。放置引擎不存执行状态,watcher 重启后重新轮询 edge 就能重建视图。这让它们能随便加实例、随便替换,不用走昂贵的恢复流程。这在一个”几万沙箱在几百毫秒内砸下来”的系统里太关键了——任何有状态的中心组件都会变成瓶颈。
- per-node runtime 是 edge / aether / chronus 三件套。edge 是每台机器上的本地管家,负责准入、建沙箱、配 eBPF 网络策略、协调磁盘和内存快照、TTL 到了回收资源;aether 是每个容器/VM 沙箱的代理,和 edge 建一条通信通道(Linux 容器用 Unix domain socket,VM 用 vsock),通道一断 edge 就把沙箱标记为 failed;chronus 提供沙箱内的 shell 会话抽象,一个实例就是一个独立 shell,负责命令执行、文件操作、HTTP、流式 I/O。
4.1 四种 backend:没有银弹,只有权衡
沙箱 runtime 有个根本矛盾:隔离越强、系统功能越完整,启动越慢、开销越大。没有一种抽象能通吃所有 agentic 任务,所以 DSec 干脆提供四种 backend,铺在这条权衡曲线上:

- FnCall(函数调用):面向短小无状态的任务,比如 OJ 判题、代码编译、GPU kernel 评测。它跑在预创建、可复用的容器里,省掉每次调用的 provisioning 开销,走一条不经过 aether/chronus 的独立快路径。
- Container(容器):软件工程和通用工具调用的主力 backend。启动快、打包密度高、能跑绝大多数仓库级任务的 Linux 软件栈。缺点是共享 host 内核,对安全敏感任务不总是合适。
- microVM:用 Firecracker(AWS 开源的轻量虚拟化,专为 serverless 设计)提供更强的隔离边界,同时保持 Linux 兼容。适合安全敏感、需要更强租户隔离的任务,代价是比容器更高的内存开销和更慢的启动。
- Full VM:用 QEMU 跑完整的商用操作系统(比如 Android VM),或者需要 GUI、图形渲染的任务。开销最高,但有些任务就是离不开完整系统 API 或移动端运行时行为。
一个有意思的细节:为了多一层安全边界,FnCall 和容器其实也跑在 QEMU/libvirt 的 VM 里,而不是直接裸跑在 host 上——VM 提供隔离的内核和网络栈,在不可信容器和裸金属之间又垫了一层。生产里容器和 microVM 在实例数和资源消耗上都占主导。
5. 核心机制一:可组合环境层,治的是 setup 的组合爆炸
先看挑战 A。一个沙箱的内容可以拆成三部分:base image(OS 级依赖,比如 Ubuntu、Python 3.10、Java 8 环境)、workspace(任务的代码仓库和它专属的依赖)、以及一个或多个频繁更新的 toolkit(比如 DeepSeek Harness 这种脚手架)。生产一周里,容器 backend 服务了 11266 个 base image、102171 个 workspace,microVM 用了 2 个共享 base image、53590 个任务专属 workspace,另外还有 103 个 toolkit;67.8% 的沙箱除了 base image 还至少需要一个 workspace 或 toolkit。
如果按老办法,把这三部分揉进一个 OCI 镜像(OCI = Open Container Initiative,就是我们熟悉的 Docker 镜像标准),会怎样?会有一个恐怖的组合维护成本。假设你维护 M 个 base、N 个 workspace、K 个 toolkit:升级 m 个 base,可能要重建它们的所有 workspace 组合,成本 O(m·N);升级 k 个 toolkit,成本 O(k·N)。看下图左边,你只是想更新一下 Toolkit T1,结果每一个包含 T1 的单体镜像都得整个重建一遍,哪怕它的 base 和 workspace 压根没变。

DSec 的 insight 很直接:base、workspace、toolkit 本来就是三个生命周期独立的层,凭什么非要熔成一个镜像? 如上图右边,把它们独立版本化,更新 T1 就只动 T1 那一层,再和现成的 base、workspace 层重新组合就行。
实现上靠的是 overlayfs(Linux 的联合文件系统)天然的合并语义:把多个只读的 lower 目录叠起来,内核会呈现一个统一的目录树,各层文件共存、按优先级解决冲突;最上面再放一个可写的 upper 目录,运行时的写全都落到这里,不碰下面的只读层。DSec 改了容器 runtime(也就是 dockerd),在沙箱创建时动态拼这个 overlayfs 栈:base 垫底,workspace 作为只读层插在上面,每个 toolkit 再往上叠。
这样一来,workspace 和 toolkit 的文件是合并(merge)进 base 目录树的,而不是替换路径(这点很关键——普通的 bind mount 会整个替换目标路径,而这些组件需要的是”追加”语义,把文件并进已有目录树而不遮住下面的内容;严格只读挂载还会和那些要往自己安装目录里写东西的工具打架,比如 Python 建 __pycache__)。于是升级 m 个 base 只需重建那 m 个 base 层、升级 k 个 toolkit 只需重建那 k 个 toolkit 层,O(m·N)/O(k·N) 一下降到 O(m)/O(k)。
那这些只读层用什么格式存?答案是 EROFS(Enhanced Read-Only File System,一个专门为只读数据设计的 Linux 文件系统)。相比 ext4、XFS 这些可写文件系统,EROFS 省掉了写相关的 bookkeeping(记账开销),on-disk 布局更紧凑,还支持压缩的同时保留随机访问——这点和 tar.gz 有本质区别:tar.gz 得整个传下来解压才能用,EROFS 可以只读、只解压覆盖你要那块数据的压缩块。这个特性是下一节按需加载的地基。
microVM 也套了同一个模型:base 和 toolkit 打包成独立版本化的 EROFS 镜像,以只读块设备暴露给 guest;guest 内部根文件系统用 overlayfs,EROFS 当 lower 层,一块 ext4 可写盘当 upper 层。
顺带提一个”少即是多”的工程细节:论文说这个”动态往 dockerd 的 overlayfs 栈里插 EROFS lower 层”的改动,只花了 30 行 Go 代码。改在正确的地方,30 行就够了。
6. 核心机制二:按需镜像分发,别再拉整个镜像了
挑战 C 的关键观察前面说过了:沙箱只访问镜像里 4%~13% 的数据。所以按需拉取(on-demand pulling)解决的不只是”什么时候拉”的时机问题,更是”拉多少”的体量问题——总 I/O 是按实际用到的比例缩小,而不是把同样的量挪到另一个阶段。
下面这张图是 §5 三大机制的总览,可以对着看:左边容器侧的 multi-device EROFS 按需拉 data blocks、CPU QoS 的 LS/BE 分级;右边 microVM 侧的内存优化(DAMON、virtio-balloon、virtio-pmem);中间是可组合层,底下统一压在 3FS 上。

现有的按需镜像分发系统,常见做法是”容器 registry + P2P 分发”来防止 registry 成为瓶颈。DSec 没这么干,而是直接把镜像放在 3FS 上——3FS(Fire-Flyer File System)是 DeepSeek 那套已经在支撑生产训练的集群级分布式文件系统。复用现成存储,就不用再单独部署一套镜像分发层。
但 3FS 有个鲜明的脾气:大顺序读写吞吐很高,小随机 I/O 很烂。这个非对称特性直接决定了三条设计原则:
- 写留在本地。沙箱的写又碎又不可控(比如日志文件那种小而频繁的写),全丢给 3FS 会撞上它的小写短板,所以可写层放节点本地盘。
- 读走按需 + 批量。只读镜像数据只在被访问时才从 3FS 拉,而且 I/O 尽量攒成大块,去吃 3FS 大 I/O 的高吞吐。
- 元数据尽量放本地。文件系统元数据常常是小读,能分离就把元数据预取到本地节点。
对容器,EROFS 正好能落地这三条:它严格只读,所以运行时的写全落在 overlayfs 的本地 upper 目录;它按需通过 buffered I/O 供数据,内核的 readahead(预读)会把相邻块合并成大请求;它还有个 multi-device 模式能把元数据和数据分开。这里论文提到一个对比:Nydus(阿里开源的镜像加速方案)也是分离元数据和数据 blob、支持懒加载,但它的数据是通过 fscache/FUSE 这样的用户态后端从 registry 或对象存储按需拉。DSec 的做法是把 EROFS 元数据下到 worker 本地盘、数据留在 3FS,于是元数据遍历和路径查找不产生远程 I/O——写路径(本地、小 I/O 友好)和读路径(远程、按需、批量)就这么彻底分开了,正好贴合 3FS 的非对称性能。
不过挂太多 EROFS 层,本身也会给容器创建添开销。DSec 的处理是把连续的层在一个尺寸阈值内(比如 3GB)离线合并成一对 EROFS 镜像(一份元数据、一份数据),同时保留 overlayfs 的 whiteout 语义(用来正确表示”文件被删除”),既减少最终挂载数,又保住共享层之间的 page-cache 复用。它还用了 EROFS 的 file-backed mount 模式,省掉 loop 设备那层块映射开销。
microVM 这边没法照搬容器方案——Docker 的 overlay2 驱动不能用 overlayfs 撑的数据目录,而 Firecracker 又不支持把 host 文件系统通过 virtio-fs 导给 guest。所以 microVM 的只读 base/toolkit 层还是用 EROFS,可写 ext4 盘则交给 OverlayBD(一种支持增量快照的块级镜像格式),通过 ublk(Linux 的用户态块设备框架)暴露出来。这条块级路径同样是”按需读、本地写”,还支持增量磁盘快照而不用把改动的文件重新打包进 EROFS。DSec 的 ublk 实现用 256KiB 的 chunk 拉 OverlayBD 数据,存进一个二级本地文件系统缓存——就算某个 chunk 被 page cache 淘汰了,它还在本地缓存里,不用再回 3FS 拉一遍。这套 Rust 版的 OverlayBD + ublk 存储层已经开源了。
7. 核心机制三:高密度资源管理,内存和 CPU 分开治
挑战 B 分两条线:内存要省、CPU 干扰要压。这一节全是 Linux 内核机制的组合拳,而且论文强调不需要改内核,全靠配置和集成。我尽量把每个机制是干嘛的就地讲清楚。
7.1 内存:一份缓存大家共享 + 冷页主动回收
前面说过 microVM 的两处内存浪费:页缓存跨 guest-host 重复、guest 冷页不还给 host。对应两个机制:
virtio-pmem + DAX 治重复缓存。virtio-pmem 是一种把 host 上的文件当”持久内存”暴露给 guest 的虚拟设备,配上 DAX(Direct Access,直接访问,让文件访问绕过页缓存直接映射到后端页)后,guest 对文件的访问会直接映射到 host 那份页上,不再拷进 guest RAM。这样多个 co-located(同机部署)的 microVM 就共享 host 上的同一份 page cache 副本,重复缓存没了。
但 virtio-pmem 不是万能的:一是冷访问要走同步缺页处理来建映射、把数据搬到位,不像 buffered 的 virtio-blk 那样能吃 guest 侧的 readahead 和批量块 I/O 的红利;二是 guest 得为整个 pmem 地址空间分配 struct page 元数据,4KiB 页 + 64 字节 struct page 的话,这份元数据要吃掉 pmem 容量的 1/64 的 guest RAM——一个 128GB 的 pmem 设备就要 2GB guest RAM 来记账。所以生产里只对只读的 EROFS base/toolkit 层开 virtio-pmem+DAX。
DAMON + virtio-balloon 治冷页不还。对那些不走 virtio-pmem 的盘,冷文件数据会堆在 guest 页缓存里,得单独回收。这里组合了两个东西:virtio-balloon 驱动支持 free-page reporting(空闲页上报)——guest 定期扫自己的 buddy allocator,主动把空闲页报给 host,host 用 madvise(MADV_DONTNEED) 释放对应的 host 内存;默认它按 order-9(2MiB 区域)粒度工作。而 DAMON(Data Access MONitor,Linux 里基于采样的内存访问监控框架)负责找出那些超过配置年龄阈值都没被碰过的冷文件页,走内核回收路径把它们赶下去。DAMON 把散落的冷页还给 buddy allocator,后者再把它们合并成高阶大块,正好满足 free-page reporting 的粒度要求。两者配合,DAMON + balloon FPR 减少了 21.2% 的内存消耗,且没有显著 CPU 开销。
7.2 CPU:光降优先级没用,得管到 SMT sibling
CPU 这边的目标是:既要靠超售提利用率,又不能干扰那些有严格每步延迟预算的任务。DSec 把沙箱分成 LS(latency-sensitive,延迟敏感)和 BE(best-effort,尽力而为)两类。
BE 沙箱丢进 SCHED_IDLE 调度类——这是 Linux 里优先级最低的调度类,只要有 LS 任务可运行,BE 就立刻让出 CPU。但光靠调度优先级不够:现代 CPU 一个物理核有两个 SMT 硬件线程(sibling),就算你把 BE 的优先级压到最低,它跑在 LS 任务的 sibling 线程上时,两者仍然共享这个物理核的执行资源,照样互相拖累。
所以 DSec 还开了 Linux 的 core scheduling(核心调度):它保证同一个物理核的两个 sibling 线程上,不会跑互不相关的 BE 工作去挤占 LS 任务。这套两层策略把 SMT 引起的延迟膨胀从 45.2% 压到了 17.3%(细节在 §9)。
这一节的东西看着琐碎,但它回答了一个很实在的问题:你敢在一个节点上塞 3200 个容器,靠的不是运气,而是内存共享 + 冷页回收 + CPU QoS 这几样东西同时兜底。
8. 和 RL 框架一起设计:这一章最有意思
前面都是”怎么把沙箱跑得又快又省”。但 DSec 服务的是 DeepSeek V3.2 到 V4.1 的 RL 训练和评测,光沙箱本身高效还不够,得和 RL 框架在执行生命周期和安全策略上深度配合。这一章我读得最上头,因为它讲的全是生产里真实踩出来的坑。
8.1 环境由 agent 造、给 agent 用
agentic RL 要的环境数量大到没法手工搭。DSec 的做法很”自举”:让 agent 在同一套基础设施上,交互式地把环境造出来。靠的是 pack_diff——agent 在任意时刻可以给沙箱打一个增量磁盘快照做 checkpoint,之后能作为新沙箱恢复。这个 checkpoint-and-restore 接口,直接把一次交互式会话变成一个可复用的环境,不用再单独搞一条镜像构建流水线。
这里有个安全细节值得点一下:因为环境是”造”和”用”在同一套设施上,得防信息泄漏——builder 和 runtime agent 用不同账号,打包前把可写层里的构建残留数据清掉,免得把参考答案带进最终镜像。为什么这点重要?下一小节的 reward hacking 会告诉你,模型是真的会去翻这些残留的。
8.2 把 agent loop 从可抢占的 GPU pod 里拆出来
GPU 集群的训练 job 为了提利用率是会被抢占(preemption)的。对跑很久的 agentic rollout 来说,如果 agent loop(驱动”生成动作→执行→观察”这个循环的逻辑)和训练 job 绑在一起,抢占的代价就特别大:agent loop 被杀了,可沙箱状态还好好的,你得同时保住”agent 的执行状态”和”沙箱状态”才能续上。
早期版本里,agent loop 就跑在可抢占的 GPU 训练 pod 里,和模型服务、RL 框架挤在一起。GPU 一被抢,agent loop 就没了,只剩沙箱还在。恢复只能靠一份命令日志(command log)去对齐:重放时,已完成的操作直接复用记录的结果、而不是重新执行,避免那些非幂等命令产生重复副作用。这套逻辑又绕又脆。
从 DeepSeek-V4.1 开始,DSec 把 rollout 执行整个搬到自己身上,拆成两个组件:agent sandbox(放脚手架和它的工具)和 worker container(管理沙箱、提供一个与脚手架无关的控制层)。两个组件都跑在可抢占 GPU 池之外,于是 rollout 的寿命和 trainer 的寿命解耦了。worker container + agent sandbox 一起保存完整的 rollout 状态、充当它的单一真相源(single source of truth),被抢占的 GPU job 重连上来就能接着跑,不用再靠命令日志重建。这一下把 rollout 状态恢复的逻辑从 RL 框架里彻底挪走了,跨组件协调和故障处理都简单了。
8.3 抢占时暂停沙箱,省内存又保状态
既然 GPU 抢占躲不掉,沙箱状态就得在 DSec 上一直留到 rollout 完成。可这样会有一堆空闲沙箱在训练挂起期间白占内存。RL 框架的办法是:抢占发生时,主动给这个 job 关联的所有沙箱发 pause 请求,让 DSec 回收内存、但保住执行状态;之后任何对已暂停沙箱的请求,会先透明地把它 resume 再执行。
具体到两种 backend:
- 容器:edge 先
docker pause冻结进程树,然后通过memory.swap.max开启换出、memory.reclaim触发主动回收,把匿名页和文件页都回收掉但保住执行状态;resume 时先对进程的内存映射MADV_WILLNEED发起异步预取,再docker unpause。 - microVM:把内存和执行状态存成一个快照,然后直接杀掉 Firecracker 进程释放运行时内存;resume 时起个新进程、恢复快照接着跑。
8.4 模型是会”作弊”和”搞破坏”的
这一小节是全文我觉得最值得所有做 RL 的人看的部分。论文很坦诚地列了生产里观察到的两类风险:reward hacking(奖励作弊)——任务看起来 pass 了,但 agent 是通过非预期渠道拿到答案的,这会直接污染训练和评测结果;以及 agent 搞坏执行环境。
先说找答案的骚操作,模型的想象力超出你预期:
- 在沙箱内部,agent 会翻平台管理的文件和通信通道找残留答案。比如伪造用户请求——直接给 chronus 的 socket 发精心构造的 RPC 消息;翻 chronus 的日志找泄漏的答案;因为 chronus 有些操作会调 bash,agent 甚至去覆写
/bin/bash来绕过检查或往后续 shell 会话里注入命令。 - 在我们上了文件读写和 socket 访问控制之后,有个 agent 试图用
XFS_IOC_SWAPEXT(一个交换两个文件数据 extent 映射的 ioctl)来绕过——想让受保护的文件内容通过另一个文件描述符被读到。结果这操作把 XFS 元数据搞坏了,逼得文件系统 shutdown。你看,找答案的行为甚至能顺手把基础设施搞崩。 - 在沙箱外部,agent 会去任务预期信息源之外找参考实现:扫端口和服务找可达的镜像源、用 Go module proxy 去拉 GitHub 上的代码、装更新的包版本找现成实现。所以光看最终输出根本没法可靠判断 agent 是不是按预期方式解的题。
再说搞破坏,很多还不是故意的,就是普通命令闯的祸:
- 有个 agent 从根目录递归
grep,遍历到/proc、读了/proc/kpagecgroup,触发内核 bug 把内核搞崩了。 - 一个漏洞利用任务里,本该转发给另一台目标 VM 的攻击命令,结果在 agent 容器自己里执行了,把自己的内核搞崩。
- 还有个 agent 调了
yes(那个会无限输出y的命令),持续输出被 chronus 记录下来供异步取回,结果 stdout 累积了几十 GB。
8.5 缓解手段:AppArmor 管文件、eBPF 管网络
论文很清醒:没有单一机制能防住所有作弊和故障,所以它的策略是”加强可观测性 + 随模型演进持续加固”。这里主要讲两个访问控制:
- AppArmor 管文件和 socket:用 AppArmor profile 控制文件读写和 socket 访问,包括对 chronus 日志和 Unix domain socket 的访问。哪怕 agent 在沙箱里是 root,这些策略照样生效,专治从日志里抠残留答案、通过内部通道伪造请求。
- eBPF 管网络:训练框架按域名/镜像源给出任务专属的网络权限(比如开头那个例子:允许 PyPI、禁掉 NPM),DSec 用每沙箱的 eBPF 程序按 IP、端口、协议过滤流量,白名单之外全拒。策略还能随任务在不同阶段的连接需求动态更新。
论文也没吹这能根治——这些控制只覆盖了”找答案”那半边,对”触发内核 bug 搞破坏”这种还没有通用防御。这种实事求是的态度,反而让整章可信。
9. 效果:四个机制各自验了一遍
评估在一个和生产分开的 10 节点 CPU 测试集群上做(AMD EPYC 9655,microVM 直接裸金属跑以避免嵌套虚拟化,容器实验跑在 QEMU VM 里),workload 全来自真实的 RL 训练和评测场景(内部 SWE benchmark、SWE-bench、Terminal-Bench、安全漏洞利用任务等)。四个机制分别验:
按需加载 vs 急切拉全镜像。往 10 节点撒 8192 个容器的突发,对比三种:on-demand EROFS 拉取、eager Docker pull(cold,从远程 registry 拉全镜像)、以及全本地预缓存的 Docker pull(cached)。

如上图左边,on-demand EROFS 达到峰值并发几乎和全本地基线一样快(因为层直接挂载、数据随访问从 3FS 拉),~35 分钟跑完所有任务;而 eager pull 得先把每层下载解压才能起容器,头 20 分钟都在憋着,要 60 多分钟才完事——1.71× 的拖慢。右边看写入:eager 的峰值写 IOPS 差不多是按需路径的两倍,每节点累积写超过 1600GB;on-demand 只在开头有一小阵、之后平在 ~700GB,比 eager 少约 57%,且贴近 ~600GB 的全本地基线。
可组合层:EROFS vs tar。同样是 provisioning 一套评测 workspace + toolkit,对比 tar.gz 归档解包 和 EROFS 层挂载(用一段预录的确定性 tool call 序列替掉 LLM 生成,保证只有 provisioning 方式不同)。

因为 tar.gz 是顺序流格式,每个沙箱都得先解压、把所有文件写进本地可写层才能开工,端到端拉到 79 分钟;EROFS 直接挂共享层、不用解包,沙箱能更早进 tool-call 阶段,降到 45 分钟,1.76× 加速。如上图,tar 产生的总写流量约是 EROFS 的 5.5×、峰值写吞吐是 3.4×。注意 EROFS 的峰值 CPU 反而更高——那不是开销大,恰恰是因为更多沙箱更早并发进了 tool-call 阶段,EROFS 省掉了反复解压的 CPU 活。
内存优化。真实 agentic-RL workload,对比四种 Firecracker 配置:baseline、只开 virtio-pmem+DAX、只开 DAMON+balloon FPR、两个都开。

如上图左边,virtio-pmem+DAX 把每个 guest 冗余的页缓存收敛成一份共享 host 映射,峰值 host 内存降 40.2%;DAMON+balloon FPR 单独用峰值几乎不变、但时间积分的内存消耗降 21.2%(它擅长的是把长时间钉着的空闲内存慢慢还回去);两个一起用总内存最低。代价在右边:virtio-pmem 把瞬时峰值 CPU 从 26.5% 抬到 41.4%(前面讲过,cold 访问要走同步缺页)。所以论文给了条实用建议:CPU 紧张的部署可以只开 FPR、保留 virtio-blk。
CPU QoS。让真实评测里的 LS 任务和 10%~50% 节点容量的 BE 负载 co-located,测每步性能怎么被保住。

如上图,用一个延迟敏感的下棋应用当测试负载:50% BE 负载时,没有 QoS 控制的话每步延迟比无 co-location 基线涨了 45.2%;只开 SCHED_IDLE 最多才改善 3.4%(因为 LS 线程还是会和 SMT sibling 上的 BE 抢);加上 core scheduling 后把膨胀压到 17.3%,而且 BE 争抢越凶,改善越明显。剩下那点降级主要来自高多核负载下 CPU turbo 降频、内存带宽和 LLC(末级缓存)争抢——这些 core scheduling 管不了,但论文判断已经可以接受,就没再上内存带宽隔离。
10. 我的一点 take
读完最大的感受是:agent 训练的系统瓶颈,正在从 GPU 扩散到 CPU 沙箱层。 过去我们聊 RL 训练基础设施(veRL、OpenRLHF、slime 这些),焦点都在 GPU 调度、通信、样本吞吐上,环境被默认当成一个”反正它在那儿、配好了”的黑盒。DSec 恰恰是把这个黑盒撬开,告诉你当 rollout 变成真实的 agentic 交互时,”环境”本身就是一等公民的系统问题——它要弹性、要高密度、要按需分发、要能被抢占后续上。
第二个 take 是它几乎没发明新东西。overlayfs、EROFS、virtio-pmem、DAMON、SCHED_IDLE、core scheduling、eBPF、AppArmor,全是 Linux 内核里现成的机制,论文反复强调”无需改内核”。真正的功夫在于认清自己负载的特征(稀疏 CPU、钉着的内存、低 fanout、只访问几个百分点的镜像数据),然后把对的机制拼在对的位置上。那个”动态插 EROFS 层只花 30 行 Go”的细节就是这种品味的缩影——难的从来不是写多少代码,是知道该改哪 30 行。
第三个 take 是 reward hacking 的系统视角很新鲜。我们平时讨论 reward hacking 大多停在算法层面(reward 设计、verifier 怎么写),但 DSec 这章活生生地展示了:模型会去伪造 RPC、覆写 /bin/bash、用 XFS_IOC_SWAPEXT 绕权限、扫端口找镜像源。当你的”环境”是给一个会主动找漏洞的 agent 用的,安全边界就不是可选项,而是训练信号是否可信的前提。这一点,做 RL 训练框架的人真该抬头看一眼下面这层。
如果说有什么遗憾,就是评估只覆盖了 §5 的四个基础设施机制,§6 那些和 RL 框架的 co-design(pause/resume、agent 造环境、访问控制)没有量化评估,只有生产经验的定性描述。但考虑到这是篇工程报告、讲的是一套真实跑着的生产系统,这种”实话实说踩过哪些坑”的分享,本身就比一堆漂亮曲线更有价值。
扯句题外话。我自己的研究方向偏 LLM 效率、MoE 推理、还有 AutoML/NAS 这一块,和 DSec 这种偏底层执行平台的系统工作其实隔着一段距离——它治的是”环境怎么弹性调度”,我更多在琢磨”模型和搜索本身怎么更省”。角度不同,但底色是相通的:都是在给”大规模训练/推理”这件事做减法。我们把 AutoML 到 LLM 优化这条线上的一些积累整理成了一本书《动手学 AutoML:从 NAS 到大语言模型优化实战》,覆盖 NAS 搜索空间与策略、LLM 压缩(剪枝/量化)、模型融合、LLM 驱动的 AutoML 这些内容。和这篇沙箱基础设施不是一个层面的东西,纯粹是相邻方向,感兴趣可以当扩展读物翻翻。
