前置阅读:建议先阅读 Docker 详解:容器化技术的核心概念与实战入门 与 Docker 资源限制:cgroup 与容器资源隔离。GPU 容器依赖这两篇讲到的设备注入、运行时与资源隔离概念。
关键概念:GPU 是宿主机上的 PCIe 设备,默认不会进入容器;要让容器使用 GPU,需同时注入设备文件(/dev/nvidia*)、用户态驱动库(libcuda)与 OCI 运行时钩子,这套能力由 NVIDIA Container Toolkit 提供。理解"设备 + 库 + 运行时"三层,再往下看 CUDA 镜像、资源调度与 MIG 就不难了。
1. GPU 容器基础
1.1 为什么 GPU 不能直接 docker run
普通容器共享宿主机内核与设备树,但 GPU 并不是命名空间隔离的对象——Docker 默认只隔离进程、文件系统、网络,PCIe 设备对容器完全不可见:
容器默认视角:
- 只有 CPU、内存可见,/dev/nvidia* 不存在
- 调用 CUDA 报 "CUDA driver version is insufficient" 或
"libcuda.so.1: cannot open shared object file"
GPU 容器需要三层都补齐:
① 设备文件 /dev/nvidia0、/dev/nvidiactl、/dev/nvidia-uvm
② 用户态库 libcuda.so.1、libnvidia-ml.so.1、libcudart
③ 运行时挂钩 nvidia-container-runtime hook 注入
1.2 GPU 容器的三层依赖
| 层 | 内容 | 谁提供 |
|---|---|---|
| 内核驱动 | nvidia 内核模块、UVMM | 宿主机安装的 NVIDIA 驱动 |
| 用户态库 | libcuda、libnvidia-ml、CUDA runtime | 宿主机驱动 + 镜像内 CUDA 包 |
| 运行时钩子 | 把设备与库注入容器 | NVIDIA Container Toolkit |
nvidia-smi # 宿主机侧先确认驱动存在(容器外)
一句话:GPU 容器 = 宿主机驱动(内核)+ 镜像内 CUDA 库(用户态)+ 运行时钩子(注入),三层缺一不可。
2. NVIDIA Container Toolkit
2.1 架构:libnvidia-container 与运行时包装
NVIDIA Container Toolkit(原 nvidia-docker)由四部分组成:
nvidia-container-cli 底层工具:探测设备与库,生成注入配置
libnvidia-container 核心库:解析二进制、计算依赖的 .so
nvidia-container-runtime OCI runtime wrapper:在 runc 前执行 hook
nvidia-container-toolkit Docker/containerd 侧配置工具(nvidia-ctk)
工作链路:docker run --gpus all → docker 调 nvidia-container-runtime → 其调 nvidia-container-cli 探测设备与库 → 生成 mount 清单 → 再交给 runc 创建容器。
2.2 安装与配置
# Ubuntu 安装 NVIDIA Container Toolkit
sudo apt-get install -y nvidia-container-toolkit
# 配置 Docker 运行时(写入 daemon.json)并重启
sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker
docker info | grep -i runtime # 应出现 Runtimes: nvidia runc
2.3 Docker 侧启用
# --gpus 参数(推荐)
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
# 指定设备与能力
docker run --rm --gpus '"device=0,1"' --gpus '"capabilities=compute,utility"' \
nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
# 旧式运行时方式
docker run --rm --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=0 nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
一句话:Container Toolkit 的本质是"探测并注入",
--gpus是 Docker 对它的声明式入口。
3. CUDA 基础镜像选型
3.1 三类官方镜像
| 镜像 Tag | 内容 | 体积(约) | 适用 |
|---|---|---|---|
-base | CUDA 运行库(libcudart、libcublas) | 约 300MB | 仅运行 CUDA 程序 |
-runtime | base + CUDA 工具运行所需 | 约 500MB | 跑已编译程序(推荐) |
-devel | runtime + 编译器头文件(nvcc) | 约 2GB+ | 编译 CUDA 代码 |
# 编译用 devel,运行时切 runtime,产物拷贝以减小镜像
FROM nvidia/cuda:12.4.1-devel-ubuntu22.04 AS builder
RUN nvcc -O3 -o /out/app /src/kernel.cu
FROM nvidia/cuda:12.4.1-runtime-ubuntu22.04
COPY --from=builder /out/app /usr/local/bin/app
ENTRYPOINT ["app"]
3.2 版本匹配:CUDA 与驱动
CUDA 12.x 要求宿主机驱动 >= 525.60.13(Linux)
镜像内 libcuda 来自驱动:运行时镜像的 CUDA runtime 版本必须 <= 驱动支持的最高版本
nvidia-smi | rg "CUDA Version" # 宿主机驱动支持的最高 CUDA 版本
3.3 深度框架镜像
# PyTorch 官方 NGC 镜像(含 CUDA + cuDNN + TensorRT)
docker pull nvcr.io/nvidia/pytorch:24.04-py3
docker run --gpus all --shm-size=16g --ipc=host \
-v $(pwd)/workspace:/workspace nvcr.io/nvidia/pytorch:24.04-py3 python train.py
一句话:devel 用来编译、runtime 用来运行、NGC 镜像全家桶最省心;所有场景都必须满足"镜像 CUDA 版本 ≤ 驱动支持的版本"。
4. GPU 资源调度
4.1 Docker 侧资源声明
# 多卡分配
docker run --gpus '"device=0,1"' myapp
# 只暴露计算能力(compute 计算、utility 管理)
docker run --gpus '"capabilities=compute,graphics,utility"' myapp
# 环境变量方式(与 --gpus 等价)
docker run -e NVIDIA_VISIBLE_DEVICES=0,1 -e NVIDIA_DRIVER_CAPABILITIES=compute,utility myapp
4.2 显存与算力限制的真相
cgroup 可限制 CPU 与内存,但显存不通过 cgroup 限制:
- --memory 限制的是主存,不是 GPU 显存
- 显存由 CUDA 驱动按需分配,超限报 OOM 而非被 cgroup 杀掉
- 严格限制显存:用 MIG(硬件级)或 vGPU(虚拟化级)
算力限制:MPS 的 Active Thread Percentage、vGPU 时间片共享
4.3 共享 GPU:MPS
# 容器内启动 MPS 守护(多进程并发共享一个 GPU)
export CUDA_MPS_PIPE_DIRECTORY=/tmp/mps && nvidia-cuda-mps-control -d
# 两个推理进程共享 GPU 0,由 MPS 统一调度算力
docker run --gpus '"device=0"' -e CUDA_MPS_PIPE_DIRECTORY=/tmp/mps inference-a &
docker run --gpus '"device=0"' -e CUDA_MPS_PIPE_DIRECTORY=/tmp/mps inference-b &
一句话:Docker 的
--gpus只解决"给不给设备",显存/算力"给多少"要靠 MPS、MIG 或 vGPU。
5. 大模型推理部署
5.1 推理服务容器化
大模型推理(LLM)容器化的核心是显存规划与并发控制。以 vLLM 为例:
FROM nvidia/cuda:12.4.1-runtime-ubuntu22.04
RUN pip install vllm
EXPOSE 8000
ENTRYPOINT ["python", "-m", "vllm.entrypoints.openai.api_server"]
# 单卡部署 7B 模型(量化 + 显存上限)
docker run --gpus all --shm-size=8g --ipc=host -v /models:/models -p 8000:8000 \
vllm:latest --model /models/qwen2-7b-instruct \
--gpu-memory-utilization 0.9 --max-model-len 8192 --quantization awq
5.2 KV Cache 与显存规划
LLM 显存 = 权重 + KV Cache + 激活
权重:参数数量 × 精度字节(Qwen2-7B 半精度 ≈ 14GB)
KV Cache 由 --gpu-memory-utilization 控制预分配比例
超显存 → CUDA OOM → 进程退出(容器 restart)
| 参数 | 含义 | 建议 |
|---|---|---|
--gpu-memory-utilization | 显存利用率上限 | 0.85-0.95 |
--max-model-len | 最大序列长度 | 过长浪费 KV 显存 |
--max-num-seqs | 并发批大小 | 越高吞吐越大、单请求延迟略增 |
5.3 健康检查与自动扩缩
# readiness 探测 + 水平扩缩(按吞吐而非 CPU)
curl -sf http://localhost:8000/health || exit 1
docker compose up --scale api=4
一句话:LLM 容器部署是"显存即命运"——先按权重+KV 预算显存,再调并发参数,最后用吞吐指标扩缩容。
6. 多 GPU 与 MIG
6.1 多卡调度与张量并行
# 一张容器用多卡(张量并行切到 4 卡)
docker run --gpus '"device=0,1,2,3"' -e CUDA_VISIBLE_DEVICES=0,1,2,3 \
vllm:latest --tensor-parallel-size 4 --model /models/llama3-70b
nvidia-smi topo -m # NVLink/PCIe 拓扑,选同 NUMA 域卡
6.2 MIG:硬件级切分
MIG(Multi-Instance GPU)把一块 A100/H100 切成多个独立实例,每个实例有专属显存与算力,隔离性比软件共享强:
# 宿主机把 A100 80GB 切成 7 个 1g.10gb 实例
nvidia-smi mig -cgi 1g.10gb,...,1g.10gb; nvidia-smi mig -cci
# 容器绑定到指定 MIG 实例(按 UUID)
docker run --gpus '"device=MIG-GPU-.../1"' myapp
| 维度 | MPS(软件共享) | MIG(硬件切分) |
|---|---|---|
| 隔离 | 弱(共享算力,可互相影响) | 强(专属显存/算力/SM) |
| 显存上限 | 无(受整卡 OOM 约束) | 每个实例独立显存 |
| 适用 | 同模型多副本、开发 | 多租户、SLA 隔离、混合负载 |
| 支持 | 各代 GPU | A100/H100/H200 等 |
6.3 容器内确认分配
docker run --rm --gpus '"device=0"' nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi -L # 只看到 1 个设备
一句话:多卡用张量并行放大模型,MIG 用硬件切分隔离租户——两者解决的是"放不下"与"分不开"两类问题。
7. Kubernetes 中的 GPU 调度
7.1 NVIDIA Device Plugin
K8s 自身不认识 GPU,需要 Device Plugin 把 GPU 以 nvidia.com/gpu 资源形式暴露给调度器:
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/main/nvidia-device-plugin-daemonset.yaml
kubectl describe node | rg "nvidia.com/gpu" # 节点应出现该扩展资源
# Pod 申请 GPU
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
containers:
- name: train
image: nvcr.io/nvidia/pytorch:24.04-py3
resources: { limits: { nvidia.com/gpu: 1 } }
7.2 调度器扩展与共享
原生 K8s 调度器只做"数量分配",不感知显存:
申请 1 卡但显存不够 → 运行时 OOM,调度器无法预知
共享 GPU 扩展:gpu-share(按显存比例)、HAMi(显存+算力)、MIG 子实例
多卡训练需同 NUMA 域 + NVLink 亲和,用 Topology Manager 约束
7.3 生产清单
□ 所有节点同一驱动版本;device plugin 与驱动同步升级
□ GPU 节点打污点;GPU Pod 设置 requests == limits 避免超卖
□ 用 DCGM 采集 GPU 指标做 HPA(而非只靠 CPU)
一句话:K8s 里 GPU 是"扩展资源"——设备插件负责上报、调度器负责分配、DCGM 负责观测,三者都配齐才叫可用的 GPU 集群。
8. GPU 容器运维与排障
8.1 诊断命令
# 容器内外对照
nvidia-smi # 宿主机全局
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi # 容器内
nvidia-smi -L # 卡类型与 MIG
dmesg | rg "NVRM|Xid" # 内核错误事件
8.2 常见问题速查
| 症状 | 根因 | 对策 |
|---|---|---|
CUDA driver version is insufficient | 镜像 CUDA 高于驱动支持 | 换低版本 CUDA 镜像或升级驱动 |
NVIDIA-SMI has failed... | 驱动未加载/容器未注入设备 | 确认宿主机 nvidia-smi、检查 –gpus |
CUDA error: out of memory | 显存超限 | 降 batch/并发、量化、或换大卡 |
| 容器内 nvidia-smi 不可见 | 缺 runtime hook | 重跑 nvidia-ctk runtime configure |
| Xid 79/43 | 驱动与 GPU 通信异常 | 查 dmesg、升级驱动、检查供电/温度 |
8.3 指标监控:DCGM
# DCGM-Exporter 暴露 Prometheus 指标(GPU_UTIL/FB_USED/TEMP)
docker run -d --gpus all -p 9400:9400 nvcr.io/nvidia/dcgm-exporter:3.3.7-3.1.9-ubuntu22.04
curl -s localhost:9400/metrics | rg "DCGM_FI_DEV_FB_USED"
# Prometheus 告警:显存占用超 98%
groups:
- name: gpu.rules
rules:
- alert: GPUOOM
expr: dcgm_fi_dev_fb_used / dcgm_fi_dev_fb_total > 0.98
一句话:排障先从"宿主机 nvidia-smi 通不通"切分问题域,再用 Xid 与 DCGM 定位驱动层 vs 应用层故障。
9. 总结
| 主题 | 关键结论 | 一句话记忆 |
|---|---|---|
| GPU 容器模型 | 设备 + 用户态库 + 运行时钩子三层 | 缺一层都起不来 |
| 运行时 | NVIDIA Container Toolkit 探测并注入 | --gpus 是入口 |
| CUDA 镜像 | base/runtime/devel 按编译与运行区分 | 编译 devel、运行 runtime |
| 版本匹配 | 镜像 CUDA ≤ 驱动支持版本 | 只降不升 |
| 资源调度 | cgroup 管不了显存 | 显存靠 MIG/vGPU |
| 推理部署 | 按权重+KV 预算显存再调并发 | 显存即命运 |
| 多卡与 MIG | 张量并行放大型号、MIG 隔离租户 | 大模型并行、租户切分 |
| K8s 调度 | Device Plugin 上报 + 调度器分配 | GPU 是扩展资源 |
| 排障 | 宿主机 nvidia-smi 先切分问题域 | 先用 Xid 定位 |
GPU 容器化不是"跑个带 CUDA 的镜像"那么简单,而是一条"驱动层 → 运行时层 → 镜像层 → 调度层 → 观测层"的完整链路。落地要点:先用宿主机 nvidia-smi 确认驱动与版本,安装并配置 NVIDIA Container Toolkit,按编译/运行选择 CUDA 镜像并严守版本匹配规则;生产环境把显存与算力按 MPS/MIG 明确切分,K8s 里靠 Device Plugin 与 DCGM 完成调度与观测。当 GPU 在容器平台中成为和 CPU 一样"按需申请"的资源,AI 推理才能稳定地规模化交付。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。