Docker 容器内 GUI 与 GPU 用不了速查:--gpus、nvidia-toolkit 与 X11 转发
更新时间:2026-10-24。FAQ 速查页,面向容器里跑
nvidia-smi报找不到设备、GUI 程序一启动就cannot open display的同学。容器用 GPU 与 GUI 的本质是「设备透传 × 显示协议转发」两条独立链路:GPU 靠 nvidia-container-toolkit 把宿主驱动与设备节点注进容器,GUI 在 Linux 上靠把宿主的 X11 Unix 套接字挂进容器再加 X 权限放行。两条链路的故障互不相关,但报错经常混在同一条启动命令里。病因收敛在六根轴上:nvidia-container-toolkit 未安装、--gpus参数缺失或写法不对、容器内缺驱动用户态库、X11 套接字没挂载、X 权限(xauth)拦截、WSL 与 Mac 平台天然限制。顺一遍基本必中。如果是容器能起但运行期崩溃,转 Docker 容器崩溃速查。
本文要回答的问题
could not select device driver "nvidia"是驱动没装还是 Docker 配置问题?- 容器里
nvidia-smi找不到,但宿主机上明明是好的,差在哪一步? Authorization required, but no authorization protocol specified怎么放行?- Mac 和 Windows 上的 Docker Desktop 能不能用 GPU、能不能显示 GUI?
- 同一个镜像为什么换台机器 GPU 就用不了了?
一、报错原文与典型现象
GPU 与 GUI 的报错分属两条链路,先看报错落在哪条(主机名与域以 host.example.com 占位,路径以 /proj/demo 占位):
# GPU 链路:Docker 不认识 nvidia 这个设备驱动
docker: Error response from daemon: could not select
device driver "nvidia" with capabilities: [[gpu]]# GPU 链路:容器起来了但看不到卡
nvidia-smi: command not found
# 或 NVIDIA-SMI has failed because it couldn't
# communicate with the NVIDIA driver# GUI 链路:X11 连接被拒
Authorization required, but no authorization protocol specified
Error: cannot open display: :0
# 或
No protocol specified
qt.qpa.plugin: could not load the Qt platform plugin "xcb"读现象先分链:带 nvidia、device driver 字样的是 GPU 链问题;带 display、xcb、protocol 字样的是 X11 转发问题。两条链独立排查,不要互相换药。
二、病因处置速查表
| 报错现象 / 场景 | 可能原因 | 快速验证 | 处置 |
|---|---|---|---|
| could not select device driver "nvidia" | nvidia-container-toolkit 未装或未配 runtime | 宿主机执行 nvidia-ctk --version | 安装 toolkit 并把 nvidia 注册进 docker runtime |
| 容器内 nvidia-smi 找不到 | 启动命令缺 --gpus all | docker inspect 看 HostConfig 设备 | 启动加 --gpus all;compose 写 deploy.resources |
| nvidia-smi 报无法与驱动通信 | 宿主驱动与容器 CUDA 库版本不配 | 对比宿主 nvidia-smi 与镜像 CUDA 版本 | 升级宿主驱动或换匹配的基础镜像 |
| No protocol specified / 权限拒绝 | X 权限拦截,容器身份无 cookie | 宿主 xhost 当前列表 | xhost 加本地放行,或挂载 .Xauthority |
| cannot open display | DISPLAY 未传入或 X11 套接字未挂载 | 容器内 echo 查看 DISPLAY | 挂 /tmp/.X11-unix 并 -e 传 DISPLAY |
| Mac/Windows 上 --gpus 无效、GUI 不显示 | Docker Desktop 虚拟机层不透传 GPU、无 X 服务 | 查 Docker Desktop 版本说明 | 训练类回原生 Linux;GUI 用 VNC/浏览器方案 |
三、排查决策图
下面这张图是标准排查路线,颜色按角色区分:红是报错入口与命中的根因,蓝是排查动作,绿是通过分支,橙是修复动作,紫是防复发沉淀。
读这张图抓分水岭:报错文本直接分链——含 nvidia、gpu、CUDA 走左支查 toolkit 与启动参数;含 display、xcb、protocol 走右支查套接字挂载与 X 权限。两条链的修复互不替代,GPU 修好不会让 GUI 自己亮起来,反过来 X 权限放行了也不会让显卡凭空出现,各修各的、验各自的复测项。
四、逐条排查展开
could not select device driver "nvidia":Docker 还不知道 nvidia 的存在。 宿主装了 NVIDIA 驱动不等于 Docker 能用 GPU——中间还隔着 nvidia-container-toolkit 这层「翻译」。它在容器启动时把驱动用户态库与设备节点(/dev/nvidia 开头的那批)注入容器。没装它,--gpus 参数无处可去,直接报 device driver 找不到。处置:按发行版装上 nvidia-container-toolkit,用它自带的配置命令把 nvidia runtime 注册进 Docker,重启 dockerd,再拿官方 CUDA 小样镜像验证。
容器起来了但 nvidia-smi 找不到:八成是 --gpus 没写。 镜像里带 CUDA 工具不代表容器启动时自动拿到卡——设备透传必须显式申请:docker run --gpus all ...;compose 里写在 deploy 的 resources reservations 下。验证用 docker inspect 看容器实际拿到的设备列表。还有一类隐蔽失败:宿主驱动版本低于镜像 CUDA 要求的最低版本,此时 nvidia-smi 能跑但报无法与驱动通信,只能升宿主驱动或换低版本 CUDA 基础镜像,容器侧无解。
X11 转发的标准三件套。 Linux 宿主上跑 GUI 容器的最小配置:挂载 X11 套接字目录 -v /tmp/.X11-unix:/tmp/.X11-unix、传显示号 -e DISPLAY=宿主显示号、放行 X 权限——宿主机执行 xhost +local: 允许本地连接,或把宿主 .Xauthority 挂进容器保持 cookie 一致。No protocol specified 与 Authorization required 都是第三步没做:X 服务器校验 cookie 失败。Qt 程序报 could not load xcb 多半是镜像缺 xcb 相关库,装齐依赖即可,与转发无关。
WSL 与 Mac 的平台天花板要先认。 Mac 的 Docker Desktop 跑在 Linux 虚拟机里,GPU 不透传到容器,--gpus 写了也无效——训练类负载只能回原生 Linux 或云 GPU 机。Windows 的 WSL2 后端近年已支持 GPU 透传,但要求宿主驱动、WSL 内核、Docker Desktop 三者版本搭配;GUI 方面 WSLg 可显示 Linux 图形界面,Mac 则没有原生 X 服务,GUI 容器一般改用容器内 VNC 或 noVNC 把界面转成浏览器访问。选型时先确认平台在支持矩阵里,别在 Mac 上硬调 --gpus。
多卡指定与资源隔离。 多卡机器上用 --gpus device=序号 或按设备 UUID 指定某张卡;CI 环境多任务抢卡时,不给 --gpus 的容器默认看不到任何卡,正好当隔离用。要注意容器内看到的卡号会被重新编号,与宿主 nvidia-smi 的编号未必一致,脚本里别硬编码卡号。
验证顺序也有讲究。 修 GPU 链时先在宿主跑通 nvidia-smi,再跑官方最小 CUDA 验证镜像,最后才轮到自家镜像——三层任一层失败就把问题锁在该层,不要一上来就在自家重型镜像里调。修显示链时先跑 xeyes 或等价的最小 X 客户端镜像验证转发通道,通道通了再启动真实 GUI 程序;通道不通时改自家程序参数是白费力气。排错报告里把「哪一层验证通过、哪一层失败」写清楚,接手的人不用从头再来。
几个容易踩的次级坑。 容器以非 root 运行时,X 权限放行范围要覆盖到具体用户而不是只放行 root;Wayland 宿主机上 X11 转发走的是 XWayland 兼容层,套接字路径与 DISPLAY 编号和纯 X 会话不同,先确认会话类型再抄命令;远程 SSH 到服务器跑 GUI 容器时,DISPLAY 指向的是 SSH 转发通道,本机屏幕看不到东西属于正常现象,要配 X11 forwarding 或干脆走 VNC。
镜像选型也能预先排雷。 需要 GPU 的镜像直接从官方 CUDA 基础镜像起步,驱动用户态库的版本搭配已由上游验证;需要 GUI 的镜像把 xauth、字体与 xcb 依赖装齐再交付,别让每个使用者在容器里现装。团队内维护一份「GPU 可用 / GUI 可用」双标注的镜像清单,比事后逐个镜像救火要省得多。
五、规范做法与预防
- GPU 启动命令模板化:把
--gpus all、设备指定写进团队共享的 run 脚本/compose 模板,不靠口口相传。 - 基础镜像与宿主驱动版本对齐:维护一张 CUDA 版本与最低驱动版本对照表,构建镜像时照表选 tag。
- X11 三件套进文档:套接字挂载、DISPLAY 传递、xhost 放行写成可复制的启动片段,README 置顶。
- 平台支持矩阵明示:Linux 原生 / WSL2 / Mac 三档分别标注 GPU 与 GUI 能力,新成员按矩阵选环境。
- CI GPU 任务显式申请资源:排队与调度以卡为单位,避免无
--gpus的任务误上 GPU 节点。 - 安全边界:
xhost +的放开是宿主机粒度,共享机器上改用 .Xauthority 挂载而不是全局放行;用完后及时收回放行规则,别让临时调试口子长成常驻后门。
这一篇在知识体系里的位置
本篇是 Docker 排错速查簇的设备与显示入口篇,定位「硬件透传」:GPU 能用但显存爆掉看 Docker 资源超限速查;容器起来后运行期崩溃看 Docker 容器崩溃速查;GUI 程序能起但中文字体方块、时区不对看 Docker 时区与编码速查。「报错分链 → GPU 链查 toolkit 与 --gpus、显示链查套接字与 X 权限」这条主线对一切设备透传类报错通用。
一句话总结
容器内 GPU 与 GUI 用不了先按报错分链:could not select device driver "nvidia" 是 nvidia-container-toolkit 没装或没注册 runtime,装 toolkit 重启 dockerd 可解;容器起来了但 nvidia-smi 找不到是启动命令漏了 --gpus all,compose 里要写在资源预留下;GUI 的 cannot open display 走 X11 三件套——挂 /tmp/.X11-unix、传 DISPLAY、xhost 或 .Xauthority 放行权限,缺第三步就报 No protocol specified。Mac 的 Docker Desktop 不透传 GPU、无原生 X 服务,训练与显示需求回原生 Linux 或改用 VNC 方案。启动脚本模板化加平台支持矩阵入库,这类报错基本绝迹。