Skip to main content
当评估任务要求 Agent执行 docker builddocker rundocker compose时,先按任务的信任边界 选择 Docker access: 三种模式都要求镜像预装 Docker CLI。两种 DinD模式只接受从官方 docker:<version>-dind派生的兼容镜像。镜像需要 Docker daemon、Node和评估工具, 但不需要自定义 NiceEval ENTRYPOINT。NiceEval负责启动与监督 inner daemon、 Sandbox保活、容器内 TTL和 docker info探活,不会向任意镜像动态安装 Docker。 不能直接把未经派生的 docker:<version>-dind写成 source.image。该镜像没有 NiceEval supervisor 需要的 Node,也没有被测 Agent的工具。创建时会以 dind-image-incompatible: missing node失败。请使用下面的 Dockerfile,或先发布等价的派生镜像, 再让 source引用它。

方案一:直接挂 Docker socket

先准备只有 CLI、Node和评估工具的镜像:
sandbox/Dockerfile
在 Experiment里显式填写宿主 Unix socket。NiceEval不会从进程环境或 Docker context猜这个路径:
experiments/docker-socket.ts
NiceEval会解析 symlink、确认最终目标是 Unix socket,把它挂到 Sandbox内固定的 /var/run/docker.sock,并按 socket的数值 GID给 node添加补充组。启动 Agent前,NiceEval会确认 镜像没有预设 Docker endpoint或 context,而且默认 docker info与显式访问这个 Unix socket得到同一个 daemon。CLI缺失、用户无法访问 socket或镜像把默认 endpoint改到别处时,Sandbox创建失败。 这项检查只固定 Agent启动时的默认用法,不是安全边界。Agent之后仍可显式传 docker --host访问另一个 endpoint。不可信任务需要依赖 managed模式的网络策略,而不是依赖 Docker CLI的默认值。
这不是隔离方案。Agent可以通过 socket创建 privileged容器、挂宿主目录,并操作同一 daemon上的 其它容器、image、volume和 network。不要给不可信 Agent挂 rootful宿主 socket。

方案二:Docker-in-Docker

DinD让每个外层 Sandbox拥有自己的 inner daemon。先创建镜像:
sandbox/Dockerfile
生产评估应把 FROM钉到审核过的 digest,并把被测 Agent需要的固定 CLI烘焙进镜像。

NiceEval 如何启动 DinD

选择 dockerAccess: { mode: "dind", ... } 就同时选择了 NiceEval 的 DinD 镜像协议。 provider 会覆盖派生镜像的原有 ENTRYPOINTCMD,先检查 docker-initnodedockerdockerd-entrypoint.shtimeouttail,再启动自己的 supervisor。supervisor 同时监督官方 dockerd-entrypoint.sh dockerd与 Sandbox 保活进程。 任一进程提前退出都会停止 outer container,而不会留下一台看似存活但无法使用 Docker 的 Sandbox。 inner daemon 只监听 /var/run/docker.sock,不开放 2375/2376 TCP endpoint。 Agent 仍以 user: "node" 运行。上面 Dockerfile 在构建期把 node 加入 docker 组,因此不需要 chown root:nodechmod 666 socket。如果镜像缺少工具、 用户不在 docker 组、daemon 提前退出或 readiness 超时,NiceEval 会在删除创建失败的 容器前收集有界日志尾部并报出可操作的原因。 派生镜像不要设置 DOCKER_HOSTDOCKER_CONTEXT。NiceEval 会先确认默认 context, 再核对不带 endpoint 选项的 docker info 与显式 /var/run/docker.sock 是否到达同一个 daemon。 这项 compatibility check 通过后,才会执行作者声明的 readiness。 这个协议不承诺保留任意 service image 的启动语义。如果你要评测一个必须依赖自己 ENTRYPOINT / CMD 的服务组,应该使用 Compose Sandbox,由 Compose 声明服务进程。

在 setup 中准备 Attempt 运行时

Dockerfile 只烘焙固定工具、只读归档和项目初始文件。需要 inner daemon 的操作放进 Sandbox 级 setup,不要放进镜像 ENTRYPOINT。下面的 setup 可以导入离线 image、把项目初始文件复制到可写 workspace,并运行项目自己的 smoke check:
lib/dind-sandbox.ts
NiceEval 会先启动并验证 inner daemon,再执行这个 setup,最后运行 Agent。setup 非零退出时, Attempt 在 sandbox.create 阶段记为 errored,不会把半成品 Sandbox 交给 Agent。固定内容留在 image layer, 每个 Attempt 只复制或导入必须写入 tmpfs 或 inner data-root 的状态,可以减少重复安装和网络漂移。

Raw privileged DinD

在一次性 VM或专用 runner上,可以显式选择 raw privileged:
experiments/dind-raw.ts
必填的 raw-privileged字面量是风险授权。NiceEval不会把 raw模式描述成 rootless,也不会在失败时 自动改用 managed profile。

Managed rootless DinD

共享宿主或不可信 Agent使用 managed模式。它在 inner daemon之外增加 profile attestation、单容器 资源限制、跨进程容量准入、独占外层网络和 watchdog恢复:
experiments/dind-managed.ts
profile缺失、拼错、attestation失败或容量不足时,NiceEval会在模型调用前失败,绝不降级成 raw privileged。宿主部署完成后先检查 profile:
doctor --smoke会在该 profile上启动一次 DinD容器,并实际运行内层 docker run --rm alpine:3.20 true。它证明 profile支持 nested Docker,但不会检查项目自己的 Dockerfile。项目镜像仍由默认 docker info readiness验证。

为 DinD 设置运行并发

resources.memoryBytes 限制一台 Sandbox,不会自动设置 Run 的并发数。DinD 还会占用 inner image、BuildKit、tmpfs 和 page cache,默认并发对本机可能过高。先用较小并发完成 smoke run:
再根据宿主的可用内存、CPU 和磁盘吞吐逐步增加。可以先用 可用内存 ÷ memoryBytes 估算保守上限,同时为宿主 Docker、NiceEval 和其它进程留余量。 Experiment 只服务这类重型 Sandbox 时,也可以直接设置 maxConcurrency

定位 DinD 创建失败

创建失败后使用终端给出的 Attempt 定位符,不要直接翻运行产物:
常见错误与处理方式:

在 Eval里验证 Docker任务

三种模式对 Eval暴露相同的 Docker CLI用法:
evals/docker-compose.eval.ts
socket模式的命令操作显式选择的外层 daemon。两种 DinD模式操作 Sandbox内的 inner daemon。 sandbox也可以直接写在 defineEval({ sandbox: ... })里。多条 Eval共用配置时写在 Experiment上。