用Docker部署大模型本是为了环境隔离和迁移方便,但不少人第一次跑起来就会发现一个尴尬的问题:容器启动了,模型也加载了,CUDA却始终识别不到GPU,显存占用为零,推理全靠CPU硬撑,速度慢得没法接受。更让人困惑的是,宿主机上执行nvidia-smi一切正常,驱动在、显卡在、显存在,偏偏进了容器就“失明”。这篇文章就来系统梳理这个问题的来龙去脉,帮你把排查思路理清楚。

先搞清楚原理:容器里的GPU是怎么来的
很多人一上来就盲目重装驱动,其实先理解原理能少走很多弯路。GPU并不是容器自带的能力,默认情况下Docker容器是一个完全隔离的用户态环境,它看不到宿主机上的任何设备文件。要 让容器使用GPU,本质上是把宿主机的显卡设备文件(比如/dev/nvidia0、/dev/nvidiactl、/dev/nvidia-uvm)以及NVIDIA驱动相关的用户态库挂载进容器里。
这件事如果靠手工挂载会非常繁琐,所以NVIDIA官方提供了nvidia-container-toolkit这个工具集。它在容器启动时介入,根据你传入的--gpus参数自动完成设备挂载和库注入。整个链路是:宿主机内核驱动(负责真正的GPU通信)→ 容器运行时(containerd或dockerd)→ nvidia-container-runtime(包装层)→ 容器内的CUDA用户态库。任何一环出问题,容器内GPU都会不可用。
这里有个容易混淆的概念要厘清:容器内的CUDA版本和宿主机的驱动版本是两回事。容器镜像里打包的是CUDA Toolkit(比如cuda 12.1),而驱动必须由宿主机提供,因为内核模块无法装进容器。只要宿主机驱动版本大于等于CUDA Toolkit要求的最低驱动版本,两者就能兼容。理解了这一点,后面排查版本问题时思路会清晰很多。
基础环境排查:从宿主机到Docker逐层检查
排查应该遵循从底层到上层、从宿主机到容器的顺序,避免一上来就钻进容器里瞎折腾。第一步先确认宿主机驱动正常:
# 检查驱动和显卡状态 nvidia-smi # 查看内核模块是否加载 lsmod | grep nvidia # 检查设备文件是否存在 ls -l /dev/nvidia*
如果nvidia-smi在宿主机上就报错,比如提示“NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver”,那问题出在宿主机层,常见原因是内核升级后驱动没重新编译,这时候用dkms status检查一下驱动模块编译状态,必要时重新安装驱动即可,容器层面怎么折腾都没用。
宿主机正常后,第二步检查nvidia-container-toolkit是否安装、Docker运行时是否注册。执行docker info,在输出里找Runtimes一栏,正常应该能看到nvidia字样。如果没有,说明工具包没装好或者Docker没识别到,需要重新安装配置:
# 安装nvidia-container-toolkit(以Ubuntu为例) curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg curl -sL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt-get update sudo apt-get install -y nvidia-container-toolkit # 配置Docker运行时并重启 sudo nvidia-ctk runtime configure --runtime=docker sudo systemctl restart docker
配置完成后,用docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi做一次冒烟测试。这条命令能跑通并输出显卡信息,说明整条链路是通的,再去排查应用层面的问题;如果这里就报错,问题锁定在基础设施层。
容器内深度排查:框架层面的常见坑
链路通了不代表万事大吉,大模型框架层面还有不少坑。最典型的是PyTorch里torch.cuda.is_available()返回False。这时候先在容器内直接跑nvidia-smi确认设备可见,再检查环境变量CUDA_VISIBLE_DEVICES是否被误设为空字符串或者-1,这个变量一旦配置不当会让框架主动忽略所有GPU。
第二个高频坑是CUDA版本与PyTorch构建版本不匹配。PyTorch官方发布的每个版本都有对应的CUDA构建(cu118、cu121等),如果你的镜像是cuda 11.8环境,却通过pip装了cu121版本的PyTorch,运行时找不到匹配的CUDA运行库,GPU自然不可用。检查方法:
import torch # 查看PyTorch编译时使用的CUDA版本 print(torch.version.cuda) # 查看当前CUDA是否可用 print(torch.cuda.is_available()) # 查看GPU数量 print(torch.cuda.device_count())
如果torch.version.cuda是12.1而容器内的nvcc --version显示11.8,就需要统一版本:要么换匹配的镜像,要么按容器内的CUDA版本重新安装对应构建的PyTorch。
第三个坑出现在Compose文件里。不少人的docker-compose.yml写的是老式的runtime: nvidia写法,但compose v2默认走的是 swarm 模式语法,会忽略这个字段。推荐统一使用deploy.resources.reservations.devices的声明式写法:
services:
llm:
image: my-llm-image
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
另外要注意镜像内的NVIDIA驱动库版本。有些自制镜像在基础镜像里手动安装了驱动,反而与宿主机注入的库冲突,导致容器内加载失败。原则上容器内永远不要安装内核驱动,只保留CUDA用户态库即可。
常见报错速查与预防建议
最后汇总几个高频报错及其对应解法。could not select device driver "nvidia" with capabilities: [[gpu]]意味着工具包未安装或Docker未重启,回到第二步重新配置;Failed to initialize NVML: Unknown Error在系统重启后偶发,通常与systemd的cgroup限制有关,可以尝试重启Docker守护进程或检查/etc/docker/daemon.json配置;容器内nvidia-smi报No devices were found则多半是--gpus参数没传或CUDA_VISIBLE_DEVICES设置有误。
从预防角度看,建议把GPU环境检查固化成镜像的一部分,在容器入口脚本里加入GPU自检逻辑,启动时打印设备数量和CUDA版本,问题早发现早处理。同时尽量使用官方CUDA基础镜像或经过验证的框架镜像,减少自行拼装环境带来的不确定性。对于Kubernetes场景,可以进一步了解NVIDIA Device Plugin和CDI(Container Device Interface)机制,CDI是较新的设备接入方式,通过声明设备描述文件让容器运行时挂载设备,比传统的runtime注入方式更标准化,也是社区正在推进的方向。
总结一下排查路径:宿主机驱动 → 工具包与运行时注册 → --gpus参数 → 容器内nvidia-smi → 框架CUDA版本匹配。按这个顺序走,绝大多数GPU不可用问题都能在十分钟内定位。Docker GPU环境配好一次之后其实非常稳定,难点集中在第一次配置和后续内核升级时的驱动重建,把这些环节脚本化,后续部署大模型就能省心很多。
Docker GPUnvidia-container-toolkit大模型部署修改时间:2026-09-15 14:52:51