在 OpenScience 里,任务编排和沙箱执行分属两层:工作台决定“先跑什么、后跑什么、失败了怎么办”,容器决定“代码在哪个镜像里跑、能读到哪些目录、产物写到哪里”。多数“产物不见了”或“脚本在本地能跑、到平台就错”的问题,都出在没分清这两层的边界,或者数据目录没挂对。
判断:把步骤顺序、依赖和重试留在 OpenScience 的任务定义里,脚本只做计算和参数解析;执行环境用固定镜像,数据目录通过挂载进出沙箱。验证时先看宿主机输出目录是否在挂载映射的路径下,再核对文件类型。如果挂载路径写错或权限不足,产物可能留在容器可写层,随容器销毁而丢失。
分清哪些步骤由工作台编排、哪些由脚本自己决定
编排层负责流程控制,脚本负责计算逻辑。如果两边都写流程判断,重试和依赖就会失控。下面这张表可以先用来对齐职责。
| 职责 | 编排层(OpenScience) | 脚本(容器内执行) |
|---|---|---|
| 步骤顺序 | 定义 A → B → C 的执行顺序 | 只执行当前步骤的代码,不关心前后步骤 |
| 依赖关系 | 设置“步骤 B 依赖步骤 A 成功” | 不自己判断上游是否完成 |
| 重试与超时 | 配置重试次数、超时时间、失败策略 | 返回明确的退出码(0 成功,非 0 失败) |
| 参数与计算 | 传入参数、选择镜像、挂载目录 | 解析参数、完成计算、写入产物 |
一个常见的划分示例:工作台定义“下载数据 → 清洗 → 汇总”三步,清洗步骤依赖下载成功,重试 2 次。脚本内只写清洗逻辑,从挂载的输入目录读文件,写到输出目录,成功时退出码为 0。如果脚本自己判断“文件不存在就跳过”,工作台无法知道这一步是成功还是失败,重试也就没有意义。可以先从每个脚本只做一件事开始,用退出码和日志跟工作台交互。具体字段名需要按你用的 OpenScience 版本确认,但思路是一样的。
用镜像固定执行环境
容器镜像决定了代码在什么运行时和依赖下执行。同一任务在不同机器上行为不一致,通常是因为镜像用了 latest,或者依赖没有锁版本。建议在 OpenScience 的任务定义或执行环境设置里指定镜像,而不是让脚本在运行时自己装包。
需要固定的内容一般包括:操作系统基础镜像(如 debian:bookworm-slim)、运行时版本(Python 3.11 / Node 20 / Java 17)、系统库(如 libgomp、libcurl)、以及依赖清单(requirements.txt、package-lock.json、pom.xml)。镜像名用占位示例:registry.example.com/openscience/runtime-python:3.11-slim。更稳妥的方式是用 digest 固定,例如 registry.example.com/openscience/runtime-python@sha256:<摘要>。构建镜像的 Dockerfile 可以放在代码仓库里,示例骨架:
FROM python:3.11-slim
RUN apt-get update && apt-get install -y `--no-install-recommends` libgomp1 \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install `--no-cache-dir` -r requirements.txt
WORKDIR /workspace
这个骨架只固定了运行时和依赖,不包含业务代码。业务代码通常通过挂载或镜像内复制进入容器。镜像构建和推送可以在 CI 里完成,每次任务引用带版本号或 digest 的镜像,避免“今天能跑明天报错”。
把数据目录挂进沙箱
容器里的文件默认只存在于容器可写层,容器停止后大部分会消失。要让容器读到宿主机数据、并让产物落回宿主机,需要配置挂载。挂载字段通常出现在任务定义或沙箱配置里,不同 OpenScience 版本字段名可能不同,常见的是 mounts 或 volumes。下面是一个配置骨架,字段名需要按实际版本替换:
sandbox:
image: registry.example.com/openscience/runtime-python:3.11-slim
mounts:
- host_path: /data/input
container_path: /mnt/input
mode: ro
- host_path: /data/output
container_path: /mnt/output
mode: rw
建议输入目录设为 ro(只读),避免脚本意外修改原始数据;输出目录设为 rw(可写),让脚本写入产物。如果路径写错,通常的表现是容器内访问 /mnt/input 报 “No such file or directory”,或者脚本把产物写到了容器内的其他目录,容器销毁后产物就找不到了。可以先在宿主机确认 /data/input 和 /data/output 存在,并检查运行容器的用户对输出目录是否有写权限。如果容器内以非 root 用户运行,宿主机目录的属主和权限需要对应调整。
验证产物最终落在宿主机的哪个路径
确认产物没有随容器一起消失,最直接的方法是去宿主机的输出目录看。假设挂载时宿主机路径是 /data/output,可以在任务结束后执行:
find /data/output -type f -mmin -30 -ls
如果最近半小时内有文件写入,会列出完整路径和大小。需要核对的几个文件类型通常包括:日志文件(.log)、结果数据(.csv、.json、.parquet)、模型或中间产物(.pkl、.pt、.h5)。如果路径为空,可以按下面顺序排查:
- 进入容器内看挂载点,执行
ls -l /mnt/output,确认容器里是否真的能写到这个目录。 - 检查挂载配置的
mode是否误设为ro,只读时写入会失败。 - 查看宿主机目录权限,确认运行容器的用户能写入
/data/output。 - 看 OpenScience 的任务日志和容器标准输出,确认脚本是否执行到了写产物那一步。
- 确认脚本里写文件的路径跟挂载的
container_path一致,而不是写到了容器的当前工作目录或/tmp。
把输出目录固定在一个约定路径,并在脚本里只用这个路径写文件,可以省去很多“产物去哪了”的排查。