1.3 为什么偏偏是 Docker + CUDA:技术路线的取舍解剖 前两节讲清了"痛点"和"隔离在隔离什么"。现在到了做选择的时候:方案那么多,为什么本教程锁定 Docker + CUDA(准确说是 Docker + NVIDIA Container Toolkit) 这条路线? 这一节我不打算只说"因为流行",而是把备选方案摆在台面上,逐个看它们在真实大模型工程里的取舍。读完你应该能向团队技术负责人解释:为什么我们不选裸机、不选 VM、不选 Singularity、也不自己造轮子。
前两节讲清了"痛点"和"隔离在隔离什么"。现在到了做选择的时候:方案那么多,为什么本教程锁定 Docker + CUDA(准确说是 Docker + NVIDIA Container Toolkit) 这条路线?
这一节我不打算只说"因为流行",而是把备选方案摆在台面上,逐个看它们在真实大模型工程里的取舍。读完你应该能向团队技术负责人解释:为什么我们不选裸机、不选 VM、不选 Singularity、也不自己造轮子。
| 方案 | 代表 | 一句话定位 |
|---|---|---|
| 裸机 + venv/conda | 直接装 | 零封装,最灵活也最乱 |
| 虚拟机 + GPU 直通 | KVM/QEMU + VFIO | 最强隔离,最重最贵 |
| Docker + NVIDIA Container Toolkit | 本教程路线 | 轻量封装 + GPU 透传,生态最成熟 |
| Singularity/Apptainer | HPC 容器 | 无 root 容器,适合超算中心 |
| Kubernetes + Device Plugin | K8s 集群 | 大规模调度,重运维 |
这是 1.1 节的结论延伸,这里只补一句工程判断:裸机方案在"单人单卡探索期"完全够用,一旦进入"多人多卡长期迭代",它的隐性成本会超过容器化的一次性投入。本教程假设读者已经或即将进入后一种状态,所以裸机只作为反面基准,不展开。
虚拟机方案隔离最强——每个 VM 有独立内核、独立驱动,连内核崩溃都不互相影响。但它有两个硬伤在大模型场景几乎不可接受:
取舍结论:VM 适合"需要不同内核或强安全边界"的场景(比如跑不可信代码);大模型团队内部协作要的是"快速分发一致环境",VM 太重。
这是很多人会问的"专业级"问题,尤其在高校和超算中心,Singularity 是事实标准。我们必须诚实对比:
| 维度 | Docker | Singularity/Apptainer |
|---|---|---|
| root 权限要求 | 需要 daemon(通常 root) | 可无需 root 运行容器 |
| 超算中心友好度 | 一般(常禁 root) | 高(为 HPC 设计) |
| 生态与镜像丰富度 | 极高(Docker Hub 海量) | 较低,但可跑 Docker 镜像 |
| GPU 支持 | NVIDIA Container Toolkit 成熟 | 也支持 nvidia,但配置略异 |
| 学习资料与社区 | 极丰富 | 相对小众 |
我的建议:如果你在高校超算、没有 root、必须跑在调度系统(Slurm)上,Singularity 是更顺的选择,它也能直接 singularity build 一个带 CUDA 的容器。但本教程面向大多数工程团队自有的 Linux GPU 服务器,那里 Docker 生态、文档、现成基础镜像(如 nvidia/cuda、pytorch/pytorch)都最成熟,踩坑时网上答案最多。
注意:这不是说 Singularity 不好,而是"生态成熟度 + 学习曲线 + 现成镜像"三因素加权后,Docker 对普通团队性价比最高。若你的环境是纯超算,建议把本教程的"镜像设计思路"平移到 Singularity 定义文件(def file)里,概念是通用的。
早期有人这么用 GPU 容器:自己把 /dev/nvidia* 用 --device 挂进去,再把宿主机驱动目录用 -v 挂进容器。这能跑,但极其脆弱:
CUDA_VISIBLE_DEVICES 得手动算;.so,手动对容易漏。NVIDIA Container Toolkit 把这一切标准化了:你只需在 docker run 时写 --gpus all 或 --gpus '"device=0,1"',runtime 自动完成"挂载正确设备 + 注入正确驱动库 + 设置可见性环境变量"。这是"手动挂载"到"声明式调用"的质变。本教程后续所有 GPU 容器命令都基于它,正是因为它把不可靠的人工步骤变成了可靠的默认行为。
"CUDA"二字在本教程里有两层含义,必须分清:
libcudart、cuBLAS、cuDNN 等,封装在容器基础镜像里(如 nvidia/cuda:12.1.1-runtime);举例,从 NVIDIA 官方 CUDA GPU 列表可见:V100 的 CC 是 7.0,A100 是 9.0,H100 是 9.0,RTX 4090(Ada 架构)是 8.9,L40S 是 8.9。很多量化库(如 AWQ)要求 GPU 的 CC ≥ 7.5,否则直接报错"当前 GPU 算力不足"。这意味着:你选的基础镜像和编译选项,必须和目标卡的算力匹配。容器化的一大好处是,你可以为不同算力档的卡,准备不同标签的镜像,而宿主机只需一套驱动。
提示:具体某张卡的 CC 请以 NVIDIA 官方 "CUDA GPUs" 列表为准;本教程引用的 V100=7.0、A100/H100=9.0、4090=8.9 等数值为该列表公开值,但部署前请核对你手中硬件的真实型号,避免以模型代际(如"A100")想当然。部分消费级卡不支持 MIG 切分,规划多租户时务必先确认硬件能力。
为了把这一节的判断沉淀成可复用工具,给出一张决策树:
绝大多数中小型算法团队,会落到最右下角:Docker + NVIDIA Container Toolkit。这也是本教程的主线。
我替你做一个明确的主张开在这里:对于自有 Linux GPU 服务器上的大模型开发团队,Docker + NVIDIA Container Toolkit 是当前综合成本最低、生态最成熟、踩坑资料最多的隔离沙箱底座;Singularity 是超算/无 root 场景的等价替代;VM 与 K8s 分别在"强隔离"和"大规模调度"两个极端上才有必要。
记住这个主张,后面第二章动手时,你就不会纠结"我是不是选错了方案"——你选的是被大量团队验证过的主路。