第2章 核心对比:Singularity vs Docker

说实话,我刚接触容器那会儿,也以为Singularity就是Docker的一个"HPC特别版"。直到在集群上真刀真枪用起来,才明白这两兄弟从根儿上就不是一路人。这一章咱们掰开了揉碎了聊聊——它们到底差在哪儿,以及什么时候该用谁。

2.1 设计哲学——它们生而不同

我个人习惯用一句话概括:Docker为微服务而生,Singularity为科学计算而来。

Docker的目标是打包应用+依赖,实现"一次构建,到处运行"。它假设你拥有root权限,喜欢用daemon管理一切,容器之间最好完全隔离。

Singularity呢?它出生在高性能计算(HPC)环境里。那地方用户没有root,共享文件系统遍地都是,MPI作业跨节点跑,GPU直接透传——你想想看,Docker那套C/S架构在这根本玩不转。

一句话总结:Docker追求"隔离",Singularity追求"集成"。

2.2 架构差异——C/S 与进程级的较量

Docker是典型的C/S架构。你敲docker run,客户端把请求发给后台的dockerd守护进程,dockerd再调containerd、runc层层下发。这一套很成熟,但代价是——你依赖一个常驻的、拥有root权限的daemon。

Singularity没有daemon。你敲singularity exec,它直接挂载SIF镜像文件,把容器作为当前用户的一个普通进程启动。没有中间商赚差价。说白了,Singularity容器的本质就是一个隔离化的进程。

一个小实验:在Singularity容器里执行ps aux,你会看到容器进程的PID和宿主机是连通的——这在Docker里默认看不到。Singularity故意这么设计,因为HPC作业管理系统(比如SLURM)需要看到所有子进程。

2.3 安全模型——权限的取舍

我在HPC集群上折腾了大半年,最深的一个体会是:Docker的安全模型和HPC格格不入。

Docker默认需要root权限来启动daemon。虽然可以通过rootless模式缓解,但那套配置复杂得让人想摔键盘。更关键的是,很多HPC集群的安全策略直接禁止运行Docker daemon——因为它能提权、挂载宿主机目录、修改iptables。你想想看,集群管理员怎么可能放心?

Singularity不一样。它从设计上就不需要daemon,也不需要root。容器以当前用户身份运行,默认不提升任何权限。你在容器里是啥权限,出来还是啥权限。

避坑指南:我曾经在生产环境遇到过Docker容器的SUID提权问题——一个被攻破的容器通过SUID二进制获取了宿主机的root权限。Singularity默认禁用SUID,从机制上杜绝了这类问题。如果你在涉密或高安全等级的环境里跑容器,这一点尤其要重视。

2.4 使用场景——各有所长

没有银弹。两种工具各有主场:

场景 推荐工具 原因
微服务、Web应用、CI/CD Docker 生态成熟,编排工具多,镜像小
HPC、科学计算、AI训练 Singularity 无需root,MPI/GPU直通,与调度器集成
多用户共享集群 Singularity 无daemon,不留特权口子
桌面应用、开发环境 Docker 交互方便,端口映射顺手
复现老旧科研软件 Singularity 直接打包现有系统,SIF单文件易分发

2.5 命令对比——从 Docker 到 Singularity

如果你用过Docker,上手Singularity其实很快。命令结构高度相似,但细节上有不少坑。我个人习惯在迁移时先做一个"命令映射表",对照着练几遍就熟了。

操作 Docker Singularity
拉取镜像 docker pull ubuntu:22.04 singularity pull docker://ubuntu:22.04
运行容器 docker run -it ubuntu:22.04 bash singularity exec ubuntu_22.04.sif bash
交互式Shell docker run -it --rm ubuntu:22.04 singularity shell ubuntu_22.04.sif
挂载目录 -v /host:/container --bind /host:/container
构建镜像 docker build -t myapp . singularity build myapp.sif myapp.def
查看容器 docker ps singularity instance list

来看一个实际迁移的例子。假设你有一个Docker容器跑Python训练脚本:

# Docker 方式
docker run -it --rm --gpus all \
  -v /data:/data \
  -v /home/user/project:/workspace \
  nvcr.io/nvidia/pytorch:22.12-py3 \
  python /workspace/train.py

换成Singularity是这样的:

# Singularity 方式
singularity exec --nv \
  --bind /data:/data \
  --bind /home/user/project:/workspace \
  pytorch_22.12.sif \
  python /workspace/train.py

结构几乎一样?对的。但注意两个关键区别:

  • GPU标志:Docker用--gpus all,Singularity用--nv(NVIDIA)或--rocm(AMD)
  • 自动挂载:Singularity默认不挂载任何宿主机路径,你要手动用--bind指定。而Docker在某些配置下会自动挂载部分目录

避坑指南:我曾经帮团队从Docker迁移到Singularity时踩过一个坑——Singularity默认把$HOME挂载进容器,并且没有写保护。结果一个训练脚本把临时文件写到了用户的$HOME里,把集群用户目录撑爆了。后来我们在所有容器里强制加了一句:--no-home,同时用--bind单独指定工作目录。记住:永远不要在Singularity容器里依赖$HOME的行为,显式指定挂载点才是王道。

2.6 本章总结:一张表说清楚

下面这张SVG把全章的核心差异浓缩在一起。你可以把它当作选型时的"速查地图":

Singularity vs Docker 核心对比框架 Docker 容器方案 Singularity 容器方案 docker CLI dockerd (Daemon) containerd + runc 容器(隔离沙箱) 需root特权 · 有常驻daemon · C/S架构 singularity CLI mount (挂载SIF) run (直接启动进程) 容器即进程(无隔离墙) 无需root · 无daemon · 进程级架构 C/S vs 进程 需root vs 无root 有daemon vs 无daemon 选型建议:微服务/Web应用 → 选 Docker | HPC/科学计算/多用户集群 → 选 Singularity

最后用一张速查表收尾,方便你以后翻回来快速对比:

维度 Docker Singularity
架构模式 C/S(Client-Daemon) 进程级(直接运行)
是否需要root 是(默认,rootless模式复杂) 否
是否有常驻daemon 是(dockerd) 否
容器隔离粒度 强隔离(独立PID/net/mnt命名空间) 弱隔离(与宿主机共享更多命名空间)
GPU直通 --gpus all --nv / --rocm
MPI支持 需额外配置 原生集成(Hybrid MPI模型)
镜像格式 分层镜像(layer tar包) 单文件SIF(只读挂载)
核心适用场景 微服务、CI/CD、Web应用 HPC、科学计算、AI/ML训练

搞清楚这些差异,你就能在项目选型时少走弯路。我个人的建议是:别硬用Singularity跑Web服务,也别非拿Docker去HPC集群上折腾——工具选对,事半功倍。