docker build、docker run 或 docker compose 时,先按任务的信任边界
选择 Docker access:
完整的 Docker profile 依赖 Linux cgroup v2、systemd 和支持 project quota 的文件系统。NixOS 可以用
NiceEval module 部署;Ubuntu、Debian 与其它 systemd Linux 需要安装等价的宿主服务。
macOS 上的实际选择是 OrbStack 或 Apple
container:
- OrbStack 的 Docker engine 可以用于可信任务的 Docker socket 模式。需要完整 profile 时,在 OrbStack Linux machine 内运行 NiceEval,并按该发行版的 Linux 安装方式部署宿主服务。
- Apple 官方
container在 Apple silicon 和 macOS 26 上把 Linux container 作为轻量 VM 运行。当前 NiceEval Docker Provider 不操作它的 CLI 或 API,因此不能直接选择container;这需要独立 Provider。
container 使用 Containerization framework,不是 containerd。直接安装 containerd 也不会让 Darwin
获得 Linux cgroup 或 project quota;它仍需要 Linux VM。
三种模式都要求镜像预装 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 声明服务进程。
用 Action 准备 Attempt 运行时
Dockerfile 只烘焙固定工具和项目初始文件。需要 inner daemon 的操作放进 Sandbox 级.before(action),不要放进镜像 ENTRYPOINT。下面这个声明式 Action 会创建可写 workspace 并验证 inner daemon;只有镜像确实烘焙了对应归档和文件时,才在这里加入 docker load、文件复制或项目 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。storageProfile 为 inner /var/lib/docker 分配磁盘配额;不要把该路径放进
tmpfs。这个 profile 只增加磁盘准入、配额和强杀恢复,不会把 raw privileged 变成安全隔离方案。
在 macOS 上使用 OrbStack
可信的本地评估可以复用 OrbStack Docker engine。先让 OrbStack 创建 Docker context,再读取实际 Unix socket:unix:// 路径去掉 scheme 后填入 dockerAccess.socketPath。这仍是 Docker socket 模式;Agent
可以完全控制 OrbStack daemon,不适合不可信任务。
需要 managed profile 时,使用 OrbStack 的完整 Ubuntu machine,而不是把 profile 服务装进 macOS:
Managed rootless DinD
共享宿主或不可信 Agent 使用 managed 模式。它在 inner daemon 之外增加 profile attestation、单容器 资源限制、跨进程容量准入、独占外层网络和 watchdog 恢复: NixOS 宿主先按照在 NixOS 上配置 Managed DinD部署并验证 profile, 再配置下面的 Experiment。 下面假设 Dockerfile 已把固定的 runtime 归档复制到/opt/images/runtime.tar。load-fixed-runtime
只写 inner /var/lib/docker,所以它准确声明 sandboxState.dockerData。如果 Action 还写 workspace、
home、tmpfs、挂载或外部系统,就不能使用这个 state。
experiments/dind-managed.ts
dockerDataBytes 是每个 Attempt 的 inner Docker data 硬上限。watchdog 从磁盘配额池中授予一份私有
allocation,并把它挂到 /var/lib/docker。Docker image layer 与 BuildKit cache 因而占磁盘,不计入宿主
Shmem;workspace、/tmp 等确实需要内存语义的路径仍可使用有界 tmpfs。
doctor 会在该 profile 上启动一次 DinD 容器,并实际运行内层
docker run --rm alpine:3.20 true。它证明 profile 支持 nested Docker,但不会检查项目自己的
Dockerfile。项目镜像仍由默认 docker info readiness 验证。
为 DinD 设置运行并发
resources.memoryBytes 限制一台 Sandbox,dockerDataBytes 限制它的 inner Docker data。profile 会对
所有 NiceEval 进程执行共享容量准入;当前没有足够的内存或磁盘 allocation 时,新 Attempt 会等待。先用
较小并发完成 smoke run:
maxConcurrency 仍负责限制单次 Run 的并行度。两者不能互相替代。
定位 DinD 创建失败
创建失败后使用终端给出的 Attempt 定位符,不要直接翻运行产物:在评估用例里验证 Docker 任务
三种模式对评估用例暴露相同的 Docker CLI 用法:evals/docker-compose.eval.ts
sandbox 也可以直接写在 defineEval({ sandbox: ... }) 里。多条评估用例共用配置时写在 Experiment 上。