Appearance
Harness 怎么打包而不把状态塞进容器:Image、Dockerfile、Layer 与 Volume
前置:Harness API、Worker 与 Sandbox 应暴露什么:Linux Permission、Network 与 Port
核心问题:Image、Container、Layer、Volume 生命周期不同,哪些东西可以打进镜像,哪些 Runtime 状态绝不能依赖容器层保存?
先把四种状态分开
text
Image
→ 程序代码、锁定依赖、启动入口
Container writable layer
→ 可丢的运行时临时文件
Workspace / Artifact volume
→ Agent 操作的文件、构建产物(按 session policy 管理)
Durable Runtime Store
→ Run/Node/Checkpoint/Approval/Event/Idempotency facts最常见的错误是看到 Volume 能持久化,就把 SQLite checkpoint、Queue state、Workspace 全塞进同一个目录。单机 demo 能跑,多实例、回滚和恢复会迅速失去边界。
Image / Container
Image 是不可变部署工件;Container 是 Image 的运行实例。容器被替换应当是正常操作,因此一个 active Run 不能依赖“这台容器的内存/可写层永远存在”。
这也是 graceful shutdown、checkpoint、worker lease 存在的原因。
Dockerfile 要为可复现和最小运行面服务
Harness worker 常需要 Python build dependency,但生产镜像不需要编译器。使用 multi-stage build:
text
builder
→ install/build wheel
→ runtime image 只复制产物与必要 runtime dependency同时:
- pin base image / lock dependency;
- 非 root user;
- 明确 WORKDIR/ENTRYPOINT;
- 不把 secret bake 进 build arg/layer;
- 记录最终 image digest 到 deployment/run manifest。
Layer / Build Cache 只优化构建,不保存运行事实
常见顺序:
dockerfile
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen ...
COPY src ./src这样源码变化不会让依赖层每次失效。
但 Build Cache 可能保留历史输入,所以 API key、credential 不应通过普通 COPY/RUN 写入 layer。
Volume 用于文件生命周期,不等于数据库一致性
Workspace/Sandbox 可能需要 volume 让后续 Run 继续看到文件,但要定义:
- session/workspace identity;
- 多 worker 是否允许同时挂载;
- snapshot/cleanup policy;
- artifact 与 source 的权限;
- volume 丢失后的恢复策略。
对于 Run state,优先使用真正的 durable store;Volume 不是 event log、transaction 或 distributed coordination substitute。
Failure Modes
Container local file 是唯一 Checkpoint
后果: reschedule 到另一个节点后无法 resume。
处理: checkpoint 进入 durable store;本地文件只能做 cache/temporary workspace。
Mutable Tag 无法复现旧 Run
处理: deployment 使用 immutable digest,并把 image/config revision 纳入 RunManifest。
如何验证
| 场景 | 必须成立 |
|---|---|
| 删除并重建 worker container | paused Run 仍能恢复 |
| 同 release 在另一节点启动 | image digest/config 相同 |
| 清空 container writable layer | durable Runtime facts 不丢 |
自测
- 把 Runtime checkpoint 写进容器可写层,容器重建后会发生什么?哪些状态应放 Volume/外部 Store?
- Dockerfile 把 secret 写进某一层后即使下一层删除,为什么镜像历史里仍可能泄露?