docker build、docker run或 docker 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
experiments/docker-socket.ts
/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的默认值。
方案二:Docker-in-Docker
DinD让每个外层 Sandbox拥有自己的 inner daemon。先创建镜像:sandbox/Dockerfile
FROM钉到审核过的 digest,并把被测 Agent需要的固定 CLI烘焙进镜像。
NiceEval 如何启动 DinD
选择dockerAccess: { mode: "dind", ... } 就同时选择了 NiceEval 的 DinD 镜像协议。
provider 会覆盖派生镜像的原有 ENTRYPOINT 和 CMD,先检查 docker-init、
node、docker、dockerd-entrypoint.sh、timeout 和 tail,再启动自己的
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:node 或 chmod 666 socket。如果镜像缺少工具、
用户不在 docker 组、daemon 提前退出或 readiness 超时,NiceEval 会在删除创建失败的
容器前收集有界日志尾部并报出可操作的原因。
派生镜像不要设置 DOCKER_HOST 或 DOCKER_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
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
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:
可用内存 ÷ memoryBytes 估算保守上限,同时为宿主 Docker、NiceEval 和其它进程留余量。
Experiment 只服务这类重型 Sandbox 时,也可以直接设置 maxConcurrency。
定位 DinD 创建失败
创建失败后使用终端给出的 Attempt 定位符,不要直接翻运行产物:在 Eval里验证 Docker任务
三种模式对 Eval暴露相同的 Docker CLI用法:evals/docker-compose.eval.ts
sandbox也可以直接写在 defineEval({ sandbox: ... })里。多条 Eval共用配置时写在 Experiment上。