GPU 容器与 AI 推理部署:NVIDIA Container Toolkit 与 MIG 深度实践

系统讲解 GPU 容器化的完整技术栈:GPU 与容器的隔离模型、NVIDIA Container Toolkit 与 nvidia-container-runtime 架构、CUDA 基础镜像选型与版本匹配、GPU 资源调度与共享(MPS/MIG)、大模型推理容器化部署(vLLM)、多 GPU 与 MIG 切分、Kubernetes 中的 GPU 调度,以及 GPU 容器的监控排障实践。

前置阅读:建议先阅读 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内容体积(约)适用
-baseCUDA 运行库(libcudart、libcublas)约 300MB仅运行 CUDA 程序
-runtimebase + CUDA 工具运行所需约 500MB跑已编译程序(推荐)
-develruntime + 编译器头文件(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 隔离、混合负载
支持各代 GPUA100/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 推理才能稳定地规模化交付。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「docker」更多文章

  1. 镜像安全扫描与 SBOM:Trivy、Grype、Clair 与 CI 安全门禁
  2. containerd 插件机制与扩展:snapshotter、shim 与 CNI 的深度定制
  3. 边缘容器与轻量运行时:Wasm、gVisor 与 K3s 的资源受限实践