ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Docker GPU透传与deepo镜像:深度学习环境配置实战指南

Docker GPU透传与deepo镜像:深度学习环境配置实战指南

Docker 这东西,搞深度学习的人迟早得碰。不是因为它时髦,而是因为配环境这件事,实在能把人逼疯。我前面在 ExpRe 系列里陆陆续续记了不少实验复盘,这次把 Docker 这块单独拎出来写一篇,而且直接聊 GPU 透传和 deepo 镜像。原因很简单:深度学习日常里,最痛的就是环境迁移。你在某台机器上配好的 CUDA、cuDNN、PyTorch,换个机器就废了,重装两小时起步,运气不好装完还有各种玄学冲突。Docker 恰恰能把这一整套都打包成镜像,换机器直接拉下来跑,和宿主机环境完全隔离,干净利落。

这篇笔记适用的人群很明确:已经装了 Docker 但不知道怎么让它用上 GPU 的同学,以及想省掉手动装 CUDA 和 PyTorch 时间、直接拿现成深度学习镜像开干的人。我会把从宿主机准备、驱动检查,到容器启动、GPU 验证的完整链路都过一遍,最后附上几个我自己踩过的坑。

1. 项目整体设计与思路拆解

1.1 为什么深度学习项目需要 Docker

先说个真实场景。我在某台服务器上调试一个图像分割模型,依赖的是 CUDA 11.2 和 PyTorch 1.8,跑了大半个月,一切正常。后来要把项目迁移到另一台更高配置的机器上继续调,结果那边预装的是 CUDA 12.2,PyTorch 跑起来直接报错,很多算子库不兼容。当时重装驱动、重装 CUDA、重新配虚拟环境,折腾了整整一个下午。

这种问题本质上是环境耦合导致的。传统做法里,Python 库用虚拟环境隔离,但 CUDA、cuDNN、驱动这些底层依赖仍然和宿主机的系统环境绑在一起。一旦换了机器,版本对不上,整个项目的运行环境就可能崩掉。Docker 的思路则完全不同:把包括 CUDA、cuDNN、Python 解释器、所有依赖库在内的整套环境全部打包进镜像,运行的时候通过容器隔离,跟宿主机层面的系统库不产生任何直接交互。这意味着只要镜像里装的是什么版本,你在任何一台机器上跑起来就是什么版本。

从这个角度看,Docker 本质上是把“环境”变成了一种可以复制、分发、存档的产物。这就给深度学习项目带来了几个非常实际的好处。

第一是可复现性。镜像里锁定了所有关键依赖的版本,别人拉取后跑出来的结果可以与你保持一致。第二是隔离性,项目 A 需要用 CUDA 11,项目 B 需要用 CUDA 12,两个容器可以在同一台机器上共存而互不影响。第三是部署效率,训练好的模型连同推理环境一起打包成镜像,部署到生产服务器时不用再重新配一遍环境。

1.2 方案选型:GPU 透传与容器化组合

不过 Docker 的隔离性也带来了一个麻烦:默认情况下,容器拿不到宿主机的 GPU。深度学习训练如果不让容器访问 GPU,那纯用 CPU 跑,基本就失去了意义。所以要在这套方案里解决两个问题:让容器能用上 GPU,以及让容器里有现成的深度学习环境。

GPU 透传方案的选型,我直接选了 N VIDIA 官方提供的容器工具包,也就是 nvidia-container-toolkit。这套工具的原理是:当 Docker 启动容器时,通过一个运行时钩子,把宿主机上的 GPU 设备、NVIDIA 驱动相关的库文件注入到容器内部,让容器里的 CUDA 程序认为 GPU 是直接可用的。整个过程中,宿主机和容器共享同一个驱动,但容器内部可以装载自己版本的 CUDA Toolkit,彼此版本互不冲突。

选 deepo 镜像则是为了节省配环境的时间。它是目前社区里比较知名的深度学习全栈镜像,内置了 PyTorch、TensorFlow、MXNet、Caffe 等一堆主流框架,拉下来就能跑。单独看任何一个框架,手动装也就十分钟以内的事,但如果你经常切换框架,或者需要在不同机器上做快速验证,deepo 的集成度和便利性就体现出来了。

这套组合下来的整体结构是:宿主机提供 GPU 驱动和容器运行时支持,Docker 负责进程与文件系统隔离,deepo 镜像提供开箱即用的深度学习软件栈。

2. 环境准备与工具选型解析

2.1 宿主机前置条件检查清单

动手操作之前,先把宿主机状态确认清楚,省得后面出了问题不知道是驱动问题、Docker 问题还是镜像问题。我按以下顺序逐项检查:

# 查看显卡及驱动信息 nvidia-smi # 查看操作系统版本 cat /etc/os-release # 查看内核版本 uname -r # 查看 Docker 版本 docker --version # 查看 Docker 运行状态 systemctl status docker

nvidia-smi 能正常输出,代表 NVIDIA 驱动已经装好,且驱动版本支持当前显卡。这里值得注意的一点是:驱动版本决定 CUDA 版本的兼容上限。如果镜像里要求 CUDA 版本高于驱动支持的上限,容器里会报错。实际运行中,驱动是向下兼容 CUDA 的,所以尽量保持驱动版本不要太旧,这是最省心的做法。

Docker 如果还没安装,执行系统包管理器的安装命令即可。这里我建议直接把 Docker 源配成官方源或国内可用的镜像源,而不是用系统自带的老旧版本。安装完成后,设置 Docker 服务开机自启并立即启动。

2.2 为什么选择 deepo 而不是手动搭建镜像

很多同学会有疑问:我自己的项目就两三个框架需求,直接基于 PyTorch 官方镜像,在 Dockerfile 里 RUN 几行 pip install 不就行了?为什么还要用 deepo?

答案是场景不同。如果你有一个长期维护、依赖明确的项目,确实建议基于官方镜像手动定制,这样镜像体积可控、依赖精准、安全边界也清晰。但如果你面对的是快速验证、学习实验、或者需要在不同框架间来回横跳,deepo 的优势就很明显:一次拉取,所有主流框架都有了,不用来回切换镜像,也不用等 pip 慢慢装。

deepo 默认镜像体积确实不小,通常会超过 10GB。但镜像体积换来的是开箱即用,这在 GPU 机器上尤其划算。因为 GPU 机器本身算力宝贵,花在环境配置上的时间越少越好。

deepo 镜像的取名上也有讲究,它提供多种 tag,从纯 CPU 版本到包含全部 GPU 支持框架的完整版。为了省事,我直接拉了全量 GPU 版本。如果你磁盘空间特别紧张,可以考虑针对性更强的 tag。

2.3 GPU 运行时工具的安装与配置

deepo 镜像本身只是提供了软件栈,容器能否调用 GPU,还取决于宿主机上是否安装了 nvidia-container-toolkit。这套工具链在 Ubuntu 系系统上安装相对简单,我把具体的安装步骤放在后面的实操章节,这里先解释一下它做了什么。

Docker 默认的运行时叫 runc,只负责启动和隔离容器进程。nvidia-container-toolkit 在 Docker 和容器之间插入了一层钩子,当容器启动时,它检测到用户通过 --gpus 参数请求了 GPU,就会动态地把宿主机的 NVIDIA 驱动库和设备节点映射到容器中。这个机制的关键在于,宿主机只需维护一份驱动,而容器里可以装任意版本的 CUDA 用户态组件,只要那个版本不高于驱动支持的上限就能工作。

一句话概括就是:宿主机驱动负责底层调度,容器内 CUDA 负责上层计算库,两边通过 nvidia-container-toolkit 对接。理解了这个模型,后面排查问题时会省很多力气。

3. 实操过程与核心环节实现

3.1 宿主机 Docker 环境搭建

我这边的宿主机是一台 Ubuntu 22.04 的服务器,带一张 NVIDIA 显卡。以下操作都基于这套环境,如果系统是其他发行版,命令上稍作调整即可。

先更新系统索引,然后安装 Docker 依赖的软件包:

sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release

之后添加 Docker 官方 GPG 密钥并配置软件源,这一步的目的是确保安装的是 Docker 官方维护的版本,而不是某个旧仓库里的过时版本。

sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg

配置源文件时,需要注意系统架构和发行版代号要与宿主机匹配。我的机器是 x86_64,因此下面这行命令里的架构是 amd64。

echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

然后安装 Docker 本体,顺手把 containerd 等配套组件一起装上:

sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

安装完成后,验证 Docker 服务是否正常运行:

sudo systemctl start docker sudo systemctl enable docker sudo docker run hello-world

hello-world 能跑起来,说明整个流程基本通了。这里有个细节:默认情况下执行 docker 命令需要 sudo 权限,如果不想每次输 sudo,可以把当前用户加入 docker 用户组:

sudo usermod -aG docker $USER

改完之后重新登录终端才生效。这个操作会涉及到权限提升,建议在一台自己可控的机器上操作,生产服务器上要慎重评估。

3.2 安装 nvidia-container-toolkit

前面说过,Docker 默认拿不到宿主机 GPU,这一步解决的就是这个缺口。安装 nvidia-container-toolkit 前需要配置 NVIDIA 的软件源:

curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

然后把源地址写进 apt 源列表里,注意文件路径和架构参数要匹配宿主机环境。配置完执行源更新:

sudo apt update sudo apt install -y nvidia-container-toolkit

安装完成后,需要对 Docker 的 daemon 配置做一轮更新,让 Docker 识别 NVIDIA 提供的容器运行时:

sudo nvidia-ctk runtime configure --runtime=docker

这一步会自动修改 /etc/docker/daemon.json,在其中加入 nvidia 运行时的配置。修改完成后,必须重启 Docker 才能生效:

sudo systemctl restart docker

这里有个非常容易踩的坑:如果跳过 nvidia-ctk runtime configure 这一步直接使用 --gpus 参数,Docker 会报错找不到 nvidia 运行时,或者提示 Failed to initialize NVML。NVML 是 NVIDIA 的管理库,容器初始化 GPU 依赖这套机制。这属于典型的“该装的都装了,但就是没生效”的情况,多数原因是这个配置步骤被漏掉,或者配置后没有重启 Docker 服务。

3.3 GPU 可用性验证

环境装好之后,不着急拉 huge 的 deepo 镜像,先用一个小而快的镜像验证 GPU 通道通不通。NVIDIA 官方提供了一个 cuda 基础镜像,可以用来做这个测试:

docker run --rm --gpus all nvidia/cuda:12.2.0-base-ubuntu22.04 nvidia-smi

这个命令做的事情是:启动一个临时容器,把宿主机全部 GPU 透传进去,在容器内执行 nvidia-smi。如果一切正常,你会看到和宿主机上一模一样的显卡信息。如果报错,绝大多数情况都出在 nvidia-container-toolkit 的安装或配置上,可以回到上一节排查。

输出正常的话,GPU 通道就已经打通了。后续跑深度学习镜像时,直接复用同样的 --gpus all 参数即可。

3.4 拉取 deepo 镜像并启动容器

GPU 验证通过后,就可以进入正题了。拉取镜像的时候要注意 tag 的选择。deepo 镜像有多个版本,根据是否包含 GPU 支持、包含哪些框架有所区别。我选择的是全量 GPU 版本。

docker pull ufoym/deepo:latest

这个镜像比较大,拉取耗时取决于网络环境。如果速度不理想,可以考虑配置 Docker 的镜像加速器,这点我在后面常见问题里会展开说。

拉取完成后,启动容器时需要带上 GPU 参数,同时做目录挂载,把宿主机上的数据集目录共享到容器内部。我一般会在宿主机上专门建一个 workspace 目录用于存放代码和数据,统一挂载到容器里的 /workspace 下:

mkdir -p ~/workspace docker run -it --rm \ --gpus all \ --shm-size=8g \ -v ~/workspace:/workspace \ -w /workspace \ ufoym/deepo:latest \ /bin/bash

逐项解释一下这里用到的参数:

  • --gpus all:将所有 GPU 透传给容器。如果有多卡,也可以指定某几卡,比如 --gpus '"device=0,1"',只让容器看到 0 号和 1 号卡。
  • --shm-size=8g:设置容器的共享内存大小。PyTorch 的 DataLoader 在加载数据时会大量使用共享内存,默认的 64MB 根本不够用,跑数据加载稍大的训练任务就会报 “Bus error” 或者 SharedMemoryError。
  • -v 挂载目录:宿主机和容器之间的文件共享通道,这样训练产生的模型权重在宿主机上可以直接访问。
  • --rm:容器退出后自动删除,适合临时使用的场景。如果需要在容器退出后保留状态,比如手工安装了一些库,则去掉这个参数,改用 docker commit 固化成果。

容器启动后,你会看到一个以 root 身份进入的 shell。在这里面可以检查一下容器内的 Python 环境和 GPU 状态:

nvidia-smi python --version python -c "import torch; print(torch.__version__, torch.cuda.is_available())"

torch.cuda.is_available() 返回 True 的话,整个链路就通了。

3.5 在 deepo 容器内跑一个 GPU 训练小例子

为了验证容器内 GPU 真的能算,而不只是 nvidia-smi 能看,我习惯跑一个极小的矩阵相乘示例。这里用 PyTorch 框架做一个简单的 GPU 张量运算:

python -c " import torch a = torch.randn(10000, 10000, device='cuda') b = torch.randn(10000, 10000, device='cuda') c = torch.mm(a, b) print('GPU 计算完成,结果矩阵维度:', c.shape) "

如果程序能顺利输出结果,说明 CUDA 库在容器内成功调用了 GPU 进行计算。这个测试虽然简单,但已经覆盖了 GPU 透传、CUDA 库加载、显存分配这几个关键环节。

3.6 运行容器后的环境管理技巧

用 deepo 镜像开箱即用是省事,但真实项目一定会往容器里装新的 Python 包。这里有两个选择:直接在容器里 pip install,然后在退出前用 docker commit 保存新状态;或者把依赖写在 requirements.txt 里,基于 deepo 镜像重新构建一个自定义镜像。

我个人的经验是:对于临时实验,直接在容器里装,用完即弃也没关系。对于长期维护的项目,采用 Dockerfile 的方式构建定制镜像,这样依赖变更可追溯,镜像可重建。deepo 在这里充当基础镜像的角色,你自己通过 Dockerfile 往上叠加需求。示例 Dockerfile 可以长这样:

FROM ufoym/deepo:latest WORKDIR /workspace COPY requirements.txt /tmp/requirements.txt RUN pip install --no-cache-dir -r /tmp/requirements.txt COPY . /workspace

构建自定义镜像:

docker build -t my_deep_learning_env:v1 .

这样既有 deepo 的完整框架栈,又有自己项目的依赖,两全其美。

4. 常见问题与排查技巧实录

4.1 容器内 nvidia-smi 报错 Failed to initialize NVML

这个问题我遇到不下三次,每次原因都略有不同,但排查路径基本一致。

第一个常见原因是宿主机没装好 nvidia-container-toolkit。装好了但没执行 configure,也会出现这个问题。处理方法是按 3.2 的流程重新走一遍,确认 nvidia-ctk runtime configure 执行过,并且 Docker 服务重启过。

第二个常见原因是 Docker 版本过旧,不支持 GPU 透传机制。这种情况下,要么升级 Docker,要么检查 daemon.json 里是否正确配置了 nvidia 运行时。

第三个原因是驱动版本和 NVML 库版本不匹配。一般发生在驱动升级或降级之后,容器里注入的 NVML 版本和当前驱动不配套。处理方法是重启 Docker 服务,或者彻底重启宿主机,让驱动重新初始化。

4.2 容器可以启动,但 torch.cuda.is_available() 为 False

遇到这个问题,说明容器能创建,但 PyTorch 运行时没有检测到 CUDA。原因通常有两类:一类是容器内 PyTorch 装的是 CPU 版本,另一类是 CUDA 库找不到对应的驱动 API。

deepo 全量版默认内置 GPU 版 PyTorch,所以第一类原因在 deepo 镜像里少见。但如果使用的是自定义镜像,就要确认安装的命令是 pip install torch 还是按照官网给出的 CUDA 对应版本安装。

第二类原因就是驱动版本太旧,不满足 PyTorch 版本要求的 CUDA 版本。PyTorch 1.13 之后的版本普遍要求 CUDA 11.7 或更高,如果你的驱动只支持到 CUDA 11.0,即使容器里装了最新版 PyTorch,也会因为驱动太老而无法初始化 CUDA。最简单的手段是把宿主机显卡驱动升级到较新版本,新驱动向后兼容旧 CUDA,不存在降级问题。

4.3 容器内数据加载报 Bus error (core dumped)

这个问题的元凶是共享内存不足。PyTorch 的 DataLoader 多进程加载数据时,会把数据放入共享内存队列,容器默认的 /dev/shm 只有 64MB,数据稍大就会撑爆。

解决办法就是在启动容器时显式加大共享内存:

docker run --shm-size=8g ...

这个参数值我一般直接给到 8g,或者根据数据集大小调整。如果训练任务确实特别吃内存,甚至可以考虑 --shm-size=16g。

还有一种替代方案是在容器内设置 DataLoader 的 num_workers=0,绕开共享内存机制。但这会显著降低数据加载效率,尤其在数据预处理复杂的时候会影响 GPU 利用效率,所以加共享内存才是根治方案。

4.4 拉取 deepo 镜像速度慢或频繁超时

这个问题在国内网络环境下非常常见。Docker 默认拉取镜像走的官方源,而官方源很多时候连接不稳定。解决方案是配置镜像加速器,在 /etc/docker/daemon.json 中添加 registry-mirrors 配置,然后重启 Docker。

sudo vi /etc/docker/daemon.json
{ "registry-mirrors": ["https://docker.mirrors.example.com"] }

改完重启:

sudo systemctl daemon-reload sudo systemctl restart docker

需要说明的是,镜像加速器只是拉取镜像时的加速通道,不会影响镜像本身的内容和安全性。但要注意一点:加速器可能不缓存太大或太冷门的镜像,deepo 这种大镜像有时候也需要重试几次才能完整拉取。如果反复失败,建议检查网络稳定性,或者用代理环境下直连官方源。

4.5 容器内安装 Python 包后,重启丢失

这是 Docker 的预期行为,容器一旦删除,内部文件系统会随之销毁。解决思路有两个。

一是把安装步骤固化到 Dockerfile 里,重新构建镜像,这样依赖变更可持续。适合依赖相对稳定的项目。

二是使用 docker commit 把当前容器状态保存为镜像:

docker commit <container_id> my_deep_env:with_extra_pkg

这个方式适合临时保存状态,但不推荐作为长期管理手段,因为镜像构建过程不透明,难以复现。

4.6 常见问题速查表

问题现象可能原因解决方案
容器内 nvidia-smi 报 NVML 初始化失败nvidia-container-toolkit 未配置或 Docker 未重启重新执行 nvidia-ctk runtime configure 并重启 Docker
torch.cuda.is_available() 返回 FalsePyTorch 为 CPU 版,或驱动过旧确认容器内 PyTorch 为 GPU 版;升级宿主机驱动
数据加载时报 Bus error共享内存太小启动容器时加 --shm-size 参数
镜像拉取慢网络问题或镜像源不稳定配置镜像加速器后重试
容器内安装的包丢失容器被删除改用 Dockerfile 构建或 docker commit
多卡训练只看到一张卡--gpus 参数限制使用 --gpus all 或指定 device 列表
容器时间与宿主机不一致宿主时区未同步进容器启动时加 -v /etc/localtime:/etc/localtime:ro

5. 实操心得与扩展玩法

5.1 多 GPU 场景下的容器启动技巧

单卡实验搞定之后,多卡并行是深度学习绕不开的话题。用 docker run 启动多卡容器时,--gpus all 会把所有 GPU 都透传进去。但有几个细节值得留意。

第一是 GPU 编号对应关系。容器内看到的 GPU 顺序和宿主机不一定一致,尤其在使用 CUDA_VISIBLE_DEVICES 做显存隔离的机器上。启动容器后,先执行 nvidia-smi -L 确认容器内 GPU 列表,再按实际编号写并行策略。

第二是 NUMA 亲和性。在部分服务器上,某些 GPU 和特定 CPU 内核、内存之间的数据通路更快,容器编排时如果完全不考虑这点,训练性能可能打折扣。不过这个问题一般只在超大模型训练时才会成为瓶颈,日常实验不用过度担心。

第三是基于 deepo 容器跑分布式训练時,容器网络模式默认是 bridge,多容器之间的通信要走 Docker 虚拟网络,性能会受影响。这时可以考虑改用 --network host 模式,让容器直接使用宿主机网络栈,通信延迟更低。不过代价是容器不再拥有独立的网络命名空间,端口管理和安全隔离方面要自己把控。

5.2 数据与模型的管理方式

容器本身是临时的,但数据和模型权重必须持久化。我的习惯是在宿主机上建立几个固定目录,分别存放原始数据集、预处理后的数据、代码仓库和模型输出,启动容器时全部挂载进去。

~/docker_workspace/ ├── codes/ ├── data/ │ ├── raw/ │ └── processed/ └── models/ ├── checkpoints/ └── logs/

启动容器时统一挂载:

docker run -it --rm \ --gpus all \ --shm-size=8g \ -v ~/docker_workspace/codes:/workspace/codes \ -v ~/docker_workspace/data:/workspace/data \ -v ~/docker_workspace/models:/workspace/models \ ufoym/deepo:latest \ /bin/bash

这种方式的好处是:容器随便删、镜像随便换,数据和模型权重永远留在宿主机上,不会因为容器的生命周期而丢失。训练中断后重启容器,模型可以从挂载目录里的 checkpoint 恢复,完全不受影响。

5.3 镜像瘦身与自定义基础镜像

deepo 全量版方便是方便,但十几 GB 的镜像体积在某些场景下确实肉疼。如果你的项目就只用 PyTorch,完全没必要带着 TensorFlow 和 Caffe 走。

一个常见做法是:基于 deepo 镜像启动容器,查看里面已装的框架列表,然后写一个精简 Dockerfile,只保留需要的框架。因为 deepo 的镜像是基于官方 Ubuntu 加 CUDA 的层叠结构,精简起来比较直接,自己重写 FROM 的时候选择更小的基础镜像即可。

另一种做法是在宿主机上维护一个自定义镜像仓库,把自己基于 deepo 裁剪后的镜像推送到私有仓库里。新机器需要环境时,直接从私有仓库拉取,比从 Docker Hub 拉全量 deepo 快得多。

5.4 结合训练任务调优容器参数

给深度学习容器分配 CPU 和内存资源,我一般遵循一个基本原则:GPU 是核心资源,CPU 和内存要保证数据管线不拖后腿。

启动容器的推荐配置:

docker run --gpus all \ --shm-size=8g \ --cpus=8 \ --memory=32g \ ...

--cpus 和 --memory 限制了容器能使用的最大 CPU 与内存资源。这里要注意的是,限制值过小时,训练会因资源不足而卡慢;限制值过大时,多个容器会挤占宿主机资源。合理的做法是观察宿主机空闲资源,按训练规模动态调整。

如果同一台机器上同时跑多个容器,我建议给每个容器设置明确的资源上下限,避免某个容器异常耗尽全部资源拖垮其他任务。

5.5 开发调试阶段的容器使用建议

日常调试时,我不太建议一直开着 docker run -it 进去改代码再跑。更顺手的做法是:在宿主机上写代码,挂载目录实时同步,容器内负责执行。修改完代码立即在容器里重新运行脚本,不用频繁进入退出。

调试阶段我用 docker start 和 docker exec 组合比较多。启动容器时不加 --rm,让它常驻后台,然后用 exec 进入容器操作。

# 启动常驻容器 docker run -dit --name dev_env \ --gpus all \ --shm-size=8g \ -v ~/docker_workspace:/workspace \ ufoym/deepo:latest \ /bin/bash # 进入容器 docker exec -it dev_env /bin/bash # 在容器内多次运行代码 python train.py

这样容器的生命周期与调试会话解耦,改代码、跑实验、看结果都在同一个环境里反复进行,效率拉满。调试完成后,如果这个环境还想要保留,docker commit 固化一下;不想要了,docker rm 直接删掉,宿主机干净如初。

6. 最后的几点经验总结

个人实操下来,Docker 加 GPU 透传这套方案,最大的收益不是省掉了安装软件的时间,而是把所有机器都变成了同一台机器。镜像拉下来,环境就是那个环境,不存在“在我机器上能跑,在你机器上就跑不了”的尴尬。deepo 镜像适合作为入门和轻量实验的起点,长期项目建议基于它定制自己的镜像,这样既不浪费 deepo 的集成便利,又能保证依赖的精简可控。

有一个容易被忽略的细节,就是容器的生命周期和宿主机资源的关联。容器在跑训练任务时,如果外部环境发生中断,比如宿主机重启,容器不会自动恢复。生产环境需要考虑容器自启动策略,比如使用 docker run --restart unless-stopped 启动训练容器。但实验阶段我反而不建议这么干,因为训练中断后,你大概率需要先查日志再决定是否续跑,而不是让容器自动回到原有状态。

最后再补充一个体会:这套流程中,最容易出问题的既不是 Docker 本身,也不是 deepo 镜像,而是宿主机驱动和容器镜像之间的版本匹配。我的建议是给宿主机装上尽量新的驱动,它天然向下兼容老版本的 CUDA 运行时,能规避掉一大半兼容性问题。另外一个习惯是,每次新环境搭好后先跑一遍 torch.cuda.is_available() 和一个小矩阵运算,确认 GPU 链路是通的再开始真正的工作,这个习惯帮我省下了很多无谓的排查时间。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进