ORBIT:远程 GPU 运行
· 12 分钟

ORBIT 任务 bundle:日志、产物与依赖来源

目录
  1. 瓶颈:薛定谔的依赖环境
  2. 架构重组:固定目录拓扑与分层日志
  3. 原理推演:精准的来路记录 (Provenance)
  4. 硬核落地:放弃修改的权力

可用的实验 bundle 应记录提交了什么、实际运行了什么,以及收回了哪些产物。ORBIT 将 job.json、输入、脚本、运行状态和产物分开,并记录实际导入的依赖路径与固定版本。这些记录帮助排查运行;数值级精确复现还取决于数据、随机性和执行环境。

这些记录需要在宿主机回收后仍然可用。只有“运行成功”这个状态不够:还需要 job.json、runtime-precheck.log、stdout/stderr,以及精确到 Git SHA 的依赖来源,才能还原当时运行了什么。

瓶颈:薛定谔的依赖环境

外部依赖往往是复现困难的来源。租用服务器上实际加载的 ms-swift,可能是刚打包进去的版本,也可能是宿主镜像里已有的版本。仅看提交时的配置,无法判断运行时用了哪一个。

这也是 Sculley 等人 (2015) 在《Hidden Technical Debt in Machine Learning Systems》 中讨论的“未声明的依赖与流水线丛林”债务。为减少这类不确定性,我约定了目录结构和依赖检查方式。

架构重组:固定目录拓扑与分层日志

bundle 的目录结构固定在代码中。在 orbit/core/execution/bundle.py 的 JobBundle.ensure_structure 中,各目录的职责如下:

bundle_root/
 |-- job.json      (JobSpec: 强类型的输入输出契约)
 |-- inputs/       (挂载数据集、解析后的配置 yaml)
 |-- scripts/      (entrypoint.sh, nvml 探针脚本)
 |-- runtime/      (执行面心跳:runtime.log, status.json)
 `-- artifacts/    (负载产物:训练 log, checkpoint, manifest.json)

输入配置与运行记录分开存放。排查时可以按日志层级定位:如果 runtime.log 异常,是宿主级别的启动失败;如果 artifacts/runtime-precheck.log 报错,说明环境暂存时依赖拉取出了问题;只有到了 artifacts/training.log,才是真正的模型崩溃。目录本身就提供了查找日志的顺序。

原理推演:精准的来路记录 (Provenance)

入口脚本会先检查依赖。负载运行前,它必须输出实际导入的包路径:

import swift
import pathlib
print(f'swift runtime import ok: version={getattr(swift, "__version__", "unknown")} '
      f'path={pathlib.Path(swift.__file__).resolve()}')

接入 affinetes 这类外部环境时,我另加了一层很薄的集成代码。在 orbit/integrations/affinetes_swe 中,我要求上游代码必须按完整的 40 字符 Git commit hash 钉死:

[ORBIT: Thin Wrapper]                    [Upstream Environment (Blackbox)]
+---------------------------+            +---------------------------------+
| Enforce 40-char SHA1      |            | Actor.evaluate()                |
| Fast-fail on dirty tree   | ===Call==> | Unmodified semantics            |
| Persist minimal manifest  |            | OpenEnv restore/step            |
+---------------------------+            +---------------------------------+

如果代码树处于 dirty 状态,运行时直接拒绝启动。在隔离层面,子进程完全把上游视为黑盒,不去劫持任何语义。这也是 Bertrand Meyer 在《Design by Contract》 里定义的严格接口契约。不仅如此,就像 Pineau 等人提出的 ML Reproducibility Checklist 所倡导的,我记录的是运行时实际观测到的状态,而不是期望状态。观测与期望之间的偏差,是排查不可复现问题的线索。

硬核落地:放弃修改的权力

把外部依赖当作黑盒,就得接受上游的行为和限制,不能随手修改内部逻辑。这样可以继续跟进上游的安全更新,也能将每次运行的 metric 追溯到具体的 git commit。集成层只负责调用和记录,保持上游语义不变。

任务插件边界说明了谁负责构建 bundle,以及谁解释其中的结果。

llminfrastructureobservabilityorbitreproducibility

评论