容器镜像格式:sandbox 与 sif 的区别与使用

两种格式,两种哲学

很多人刚开始接触 Singularity 时都会问:“我到底该用 sandbox 还是 sif?” 这个问题没有标准答案,得看场景。我个人习惯把 sandbox 看作是“开发版”,sif 看作是“发布版”。

sandbox 本质上是一个目录,里面的文件系统是解压开的,你可以直接进去改东西、装包、删文件。sif 则是一个只读的压缩包,安全、可签名、可分发。你想想看,一个几百兆的 sif 文件,传到集群上时不容易坏,而且能保证内容没有被篡改。

一句话总结:写代码用 sandbox,跑生产用 sif。

sandbox —— 随意折腾的工作台

构建 sandbox 很简单,用 --sandbox 参数就行。我在项目中第一次用 sandbox 是调试 Visium 的 Python 环境,那时候需要反复装 scVI 和 SpaGCN 的不同版本。如果每次都打 sif,构建时间至少 15 分钟,我受不了。

singularity build --sandbox ./visium_dev docker://continuumio/miniconda3:latest

然后你可以直接 shell 进去,就像在自己电脑上一样:

singularity shell --writable ./visium_dev/

注意看那个 --writable,不加的话你只能读,不能写。进去之后 apt、pip、conda 随便用,所有改动都会留在那个目录里。

注意:sandbox 目录不能放在 NFS 等网络文件系统上?其实可以,但速度会慢很多。我曾经把一个 sandbox 放在集群的共享 NAS 上,结果每次 apt install 都要卡 5 分钟。后来乖乖挪到了本地 SSD。

当你的环境调试完成后,你可能会想:这个 sandbox 能不能直接拿去用?理论上可以,但别人拿到的就是一个目录,没法验证完整性。这时候就该转成 sif 了。

sif —— 稳固的可交付件

sif 是 Singularity Image Format 的缩写,说白了就是一个只读的 squashfs 文件系统加上一些元数据。它不会在运行时被意外改动,而且支持数字签名。我通常把最终验证好的 Visium 环境打包成 sif ,然后扔到对象存储上,团队里任何人拉下来就能跑。

singularity build visium_env.sif ./visium_dev/

这条命令会把 sandbox 目录压缩成一个 sif 文件。你也可以直接从 Docker Hub 拉:

singularity build visium_env.sif docker://continuumio/miniconda3:latest

嗯,这里要注意,build sif 的时候 不能 加 --writable,因为 sif 天生就是只读的。

核心差异对照

我整理了一个表格,你直接拿着用:

维度 sandbox sif
本质 目录(解压缩) 单文件(压缩只读)
读写能力 可读写(需 --writable) 只读(不可改)
构建速度 快(直接操作文件) 慢(需压缩)
可分发性 差(目录结构不便传输) 好(一个文件,可签名)
签名支持 不支持 支持(--sign)
典型用途 开发、调试、增量修改 生产部署、共享、归档

转换与互操作

sandbox 和 sif 之间可以互相转换。我最常见的流程是:先用 build --sandbox 做一个开发环境,改完后用 build 直接打 sif。反过来,如果你拿到一个 sif 想解包看看里面装了啥,也可以用 sandbox 模式:

singularity build --sandbox ./explore sif_file.sif

不过说实话,我很少这么做。大多数时候只需要 singularity shell sif_file.sif 进去看一眼就行。

我的小技巧:如果你频繁修改一个 Visium 环境,可以用 sandbox 做日常开发,然后定期(比如每周一)转一个 sif 版本,命名带上日期,比如 visium_env_2025_04_07.sif。这样既保留了演化历史,又有一个可运行的稳定版本。

SVG:sandbox 与 sif 的使用场景流程图

下面这张图能帮你快速理解两种格式在真实项目中的流转关系。我画了一个从拉取基础镜像到最终部署的典型 pipeline:

sandbox 与 sif 的典型使用流程 基础镜像(docker://) sandbox 开发环境(可读写) 反复修改+验证 sif 发布镜像(只读+签名) 集群 / HPC / 对象存储 singularity run visium_env.sif 说明 虚线:循环调试 实线:最终发布

避坑指南:我曾经踩过的坑

说实话,我之前吃过一个亏。当时在 sandbox 里装了 Visium 的全部依赖,然后直接把它拷贝到另一个服务器上,想用 singularity shell 进去跑。结果发现里面的 /tmp 目录权限不对,导致 SpaceRanger 的 pipeline 中间文件写不进去。问题出在 --writable 参数没有加,而且 sandbox 的目录权限被 scp 改变了。

从那以后我规定:sandbox 只用来调试,真正要搬环境必须打 sif。而且打 sif 的时候,最好指定一下 --section 只包含需要的部分,减少镜像体积:

singularity build --section runtime visium_env.sif ./visium_dev/

如果你在 sandbox 里跑过 apt update,这些缓存也会被打进 sif 里,没必要。可以 --section runtime 跳过 build 阶段的临时文件。

小提醒:sif 文件虽然只读,但你可以通过 --overlay 参数挂载一个可写层上去,实现“半持久化”的需求。比如 Visium 的临时结果可以写到 overlay 里,而镜像本身保持干净。

实战选择建议

最后总结一下我在 Visium 项目中的经验:

  • 刚开始摸索 Visium 参数、装包 —— 用 sandbox,随时调。
  • 环境稳定了,需要跑正式分析 —— 用 singularity build 打 sif,加签名,上传到组里的共享目录。
  • 别人要用你的镜像 —— 只给 sif,不要给 sandbox 目录(目录权限、路径依赖会出幺蛾子)。
  • 需要在集群上批量提交作业 —— sif + overlay 写临时文件,一举两得。