侧边栏壁纸
  • 累计撰写 238 篇文章
  • 累计创建 245 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

大模型推理环境准备实战:GPU、驱动、CUDA、容器运行时

zhanjie.me
2025-09-14 / 0 评论 / 0 点赞 / 1 阅读 / 0 字

上一篇总览里,我们先把路线拆开了:
本地、Docker、K8S 解决的是“跑在哪儿”,Ollama、vLLM、SGLang 解决的是“用什么跑”。

但真正一开始动手时,很多人最先撞上的并不是框架差异,而是环境问题。

比如:

  • lspci 能看到 GPU,为什么 nvidia-smi 还是报错
  • 驱动装好了,为什么 Python 里还是用不了 CUDA
  • 本地能跑,为什么一进 Docker 容器就看不到显卡
  • 容器里能看到 GPU,为什么到了 K8S 里又调度不出来

这些问题看起来分散,实际上都指向同一件事:

大模型推理环境不是“装一个框架”就结束了,而是一条从硬件识别、驱动加载、CUDA 运行时、Python 依赖到容器透传逐层打通的链路。

如果这条链路没有先理顺,后面的 vLLM、SGLang、Ollama,甚至 K8S 上的 GPU 调度,都会变成不断返工的源头。

所以这篇文章不急着讲某个框架怎么装,而是先把最底层、也最容易反复踩坑的一层讲清楚:
一台 GPU 主机想真正具备大模型推理能力,到底要准备哪些东西,它们之间是什么关系,安装顺序又该怎么安排。

摘要

先给结论。

一套可用于大模型推理的基础环境,至少要依次打通这五层:

  1. 宿主机能够识别 GPU 硬件
  2. NVIDIA Driver 正常加载,nvidia-smi 可用
  3. CUDA Toolkit 或对应运行时准备完成
  4. PyTorch 或推理框架能够正确调用 CUDA
  5. 如果要走容器化,还要完成 NVIDIA Container Toolkit 与容器运行时配置

这五层里,最容易被混淆的是前三层:

  • Driver 负责让操作系统识别并驱动 GPU
  • CUDA Toolkit 提供 CUDA 编译器、库和运行时
  • PyTorch / vLLM / SGLang 则建立在前两层之上,真正去调用 GPU 做推理

如果你只记一句话,可以先记住这一句:

先让宿主机识别 GPU,再让框架用上 CUDA,最后再让容器稳定透传 GPU。

系列导航

这是“大模型私有化部署实践”系列的第二篇。当前系列顺序如下:

  1. 本地、Docker、K8S:大模型私有化部署路线怎么选
  2. 大模型推理环境准备实战:GPU、驱动、CUDA、容器运行时
  3. 基于 Ubuntu 24.04 搭建 AI 推理用原生 K8S 集群
  4. 为 K8S 补齐入口与存储:MetalLB、Gateway API、NFS 动态供给
  5. 用 Ollama + Open WebUI 快速搭建本地 AI 体验环境
  6. vLLM 私有化部署实战:本地部署、Docker 部署、接口验证
  7. vLLM 上 K8S:服务部署、对外暴露、监控与验证
  8. SGLang 私有化部署实战:本地部署、Docker 部署、能力体验
  9. SGLang 上 K8S:接入 Open WebUI、服务发布与 GPU 运维
  10. vLLM 和 SGLang 到底怎么选

如果说上一篇解决的是“路线怎么选”,那这一篇解决的就是:

不管你后面选 vLLM、SGLang、Ollama 还是 K8S,最底层那套 GPU 推理环境到底该怎么准备。

1. 先建立整体认知:一套推理环境到底由什么组成

很多人第一次搭大模型环境时,最容易把所有东西混成一团:

  • 驱动是不是就是 CUDA
  • CUDA 是不是就是 PyTorch
  • Docker 装完是不是容器里就能直接看到 GPU
  • K8S 里的 Device Plugin 是不是能替代驱动安装

这些理解如果不先理顺,后面非常容易出现“装了很多东西,但不知道哪一层在出问题”的状态。

更清晰的方式,是把整个环境拆成下面这几层:

1.1 硬件层:你得先真的有一块能用的 GPU

这听起来像废话,但在云主机、虚拟机和裸机场景里,它并不总是理所当然。

第一步应该先确认的是:

  • 机器里是否真的挂载了 NVIDIA GPU
  • 操作系统层面能否枚举到 PCI 设备
  • 当前节点到底是 CPU 机器,还是已经具备 GPU 能力

这一层最典型的检查方式就是:

lspci | grep -i nvidia

一台已经正确挂载 GPU 的机器,常见输出会类似这样:

00:03.0 3D controller: NVIDIA Corporation GA102GL [A10] (rev a1)

如果你看到的是类似上面的结果,说明操作系统至少已经枚举到了 NVIDIA 设备。
如果没有任何输出,通常要先回头检查:

  • 云主机是否真的购买了 GPU 规格
  • 虚拟机是否透传了 GPU
  • 宿主机 BIOS 或硬件层是否识别正常

如果连这一层都看不到,那后面的驱动、CUDA、PyTorch、容器运行时都无从谈起。

1.2 驱动层:让操作系统真正认识并驱动 GPU

NVIDIA Driver 是整个链路里最基础的一层。

它的作用很简单,但非常关键:

  • 让操作系统识别 GPU
  • 提供基础计算与设备访问能力
  • nvidia-smi 等工具能够工作

如果没有驱动,系统即使知道机器上“有这么一块 PCI 设备”,也不代表它真的能被拿来做 CUDA 计算。

1.3 CUDA 层:给上层框架提供 GPU 计算环境

CUDA Toolkit 是 NVIDIA 提供的通用并行计算平台与工具链。

从实战视角理解,它至少包含两类价值:

  • 对开发者来说,它提供编译器、库、调试工具和运行时
  • 对推理环境来说,它提供很多上层框架调用 GPU 时依赖的运行基础

很多人会问:
做推理是不是一定要手动装完整 CUDA Toolkit?

答案不是绝对的。

如果你是纯容器化方案,很多运行时依赖可能已经在镜像里;
但如果你在宿主机本地跑 vLLM 或 SGLang,本地 CUDA 环境通常就非常重要。

1.4 框架层:PyTorch 和推理框架真正开始使用 GPU

到了这一层,GPU 才开始变成“能干活的算力”。

不管是 PyTorchvLLM 还是 SGLang,它们都不是直接凭空使用显卡,而是建立在前面那几层之上:

  • 硬件得先存在
  • 驱动得先能工作
  • CUDA 相关环境得先可用

这也是为什么很多文档都会强调:

vLLM 是上层推理引擎,PyTorch 更像底层基础,二者并不是互斥关系。

1.5 容器层:把宿主机 GPU 能力透传给 Docker 或 K8S

当你进入 Docker 或 K8S 后,还会多出一层额外要求:

  • 宿主机能用 GPU,不等于容器自动就能用
  • 宿主机 nvidia-smi 正常,不等于 Pod 里也能看到 GPU

这时候就需要 NVIDIA Container Toolkit 一类能力,把宿主机的 GPU 访问能力正确交给容器运行时。

2. 最容易混淆的关系:Driver、CUDA、PyTorch 到底是什么关系

如果整篇文章只讲一个重点,我最想讲清楚的就是这一节。

因为很多环境问题,最后都不是命令写错,而是概念顺序从一开始就错了。

2.1 Driver 不是 CUDA,CUDA 也不是 PyTorch

可以把它们理解成这样一条链路:

  • NVIDIA Driver:让操作系统能识别和驱动显卡
  • CUDA Toolkit / Runtime:提供 GPU 计算相关的库和运行环境
  • PyTorch / vLLM / SGLang:在上面真正执行训练或推理逻辑

如果换个更形象一点的比喻:

  • Driver 像是“通电并打通硬件接口”
  • CUDA 像是“提供 GPU 计算所需的工具链和运行环境”
  • PyTorch 和推理框架像是“真正开始干活的应用层”

这三者不是互相替代,而是层层依赖。

2.2 为什么很多人只装了驱动,框架还是跑不起来

因为“驱动正常”只代表 GPU 在系统层面可以被识别。

它并不自动意味着:

  • Python 环境已经具备 CUDA 依赖
  • PyTorch 已经装了兼容版本
  • 推理框架一定和当前 CUDA 版本匹配

你完全可能出现这种情况:

  • nvidia-smi 正常
  • nvcc --version 正常
  • 但 Python 里 torch.cuda.is_available() 依然是 False

这说明问题已经不在驱动层,而是在 Python 依赖层或 CUDA 兼容层。

2.3 为什么很多容器能跑,但宿主机未必要装一模一样的 CUDA

这也是另一个常见误区。

在很多容器化方案里,镜像内部已经带有 CUDA 相关用户态库。
但这并不意味着宿主机什么都不用准备。

宿主机通常至少仍然需要:

  • 正常的 NVIDIA 驱动
  • 正确的 GPU 设备节点
  • 配好的容器运行时 GPU 透传能力

也就是说:

宿主机提供的是“把 GPU 暴露出来的能力”,镜像内部提供的是“应用如何使用这些能力的运行时环境”。

3. 安装顺序为什么比安装本身更重要

很多环境问题,本质上不是“你没装”,而是“你装的顺序不对,导致你不知道该怎么验证”。

比较稳的顺序通常是下面这样:

第一步:确认硬件存在

先检查宿主机能不能看到 NVIDIA 设备:

lspci | grep -i nvidia

如果这一步都没有结果,就不要继续往下装。

第二步:安装驱动并验证 nvidia-smi

这一层的目标很明确:
不是把包安装成功,而是让下面这条命令真正有输出:

nvidia-smi

正常情况下,你应该能看到类似这样的结果:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14              Driver Version: 550.54.14      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------|
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
|  0   NVIDIA A10                      On | 00000000:00:03.0 Off  |                    0 |
|  0%   34C    P8               18W / 150W |      0MiB / 23028MiB  |      0%      Default |
+-----------------------------------------+------------------------+----------------------+

这一步要重点看三件事:

  • Driver Version 是否正常显示
  • CUDA Version 是否被识别
  • GPU 名称、显存容量是否与机器规格一致

如果命令不存在、报错,或者表格完全拉不起来,问题通常还停留在驱动层。
这时候先别往上装框架,优先把驱动问题查清楚。

只要 nvidia-smi 还不通,就不要急着装 PyTorch,更不要急着装 vLLM 或 SGLang。

第三步:准备 CUDA 环境并验证 nvcc

如果你走本地运行路线,通常还需要确认 CUDA 工具链是否完整。

例如:

nvcc --version

常见输出大致如下:

nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2024 NVIDIA Corporation
Built on Tue_Feb_27_16:19:38_PST_2024
Cuda compilation tools, release 12.4, V12.4.99

如果这里提示 command not found,常见原因有两个:

  • 你还没有安装本地 CUDA Toolkit
  • CUDA 已经安装,但 PATHLD_LIBRARY_PATH 还没有配置好

如果你走的是宿主机本地部署路线,比较常见的环境变量配置方式如下:

export LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH
export CUDA_HOME=/usr/local/cuda-12.4
export PATH=$PATH:/usr/local/cuda-12.4/bin
source ~/.bashrc

这一步的意义不只是看版本号,而是确认 CUDA 编译器、库路径和环境变量已经在正确位置。

第四步:安装 Python 依赖并验证 CUDA 是否可被框架调用

这一步可以用最小验证方式确认 Python 侧已经真正用上 GPU:

import torch
print(torch.cuda.is_available())
print(torch.cuda.device_count())
print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "no gpu")

一组正常输出通常像这样:

True
1
NVIDIA A10

如果输出是下面这样:

False
0
no gpu

那就说明系统层虽然可能已经装好了驱动,但 Python 侧还没有真正打通 CUDA。
这时候优先检查:

  • 当前虚拟环境是否正确
  • PyTorch 版本是否和 CUDA 兼容
  • 是否装成了 CPU 版 PyTorch

只要这一层没有打通,上面装再多推理框架也意义不大。

第五步:如果要容器化,再补容器运行时

只有当前四步已经稳定通过后,再进入 Docker 或 K8S,效率才会明显更高。

容器化阶段的重点不再是“显卡能不能被系统识别”,而是:

  • 容器能否看到 GPU
  • 容器运行时是否完成透传
  • 镜像内部运行环境是否兼容

4. 驱动安装前,为什么总有人提到 nouveau

很多 GPU 驱动安装文档里,都会提到一个词:nouveau

第一次接触时,这个词往往有点突兀。
但它之所以反复出现,是因为它确实是很多驱动安装失败的源头之一。

4.1 nouveau 是什么

简单理解,nouveau 是 Linux 社区的开源 NVIDIA 显卡驱动。

它在很多发行版中可能默认启用,用来提供基础显示能力。
但在需要安装官方 NVIDIA 驱动、并做 CUDA 计算的场景里,它经常会和官方驱动产生冲突。

4.2 为什么它会影响大模型推理环境

因为大模型推理依赖的是官方 NVIDIA 驱动提供的完整计算能力。
如果系统还在加载 nouveau,就可能导致:

  • 官方驱动安装异常
  • 驱动模块无法正常加载
  • 重启后显卡状态不一致
  • nvidia-smi 无法正常工作

所以很多文档都会先处理 nouveau,再继续安装官方驱动。这不是仪式感步骤,而是典型的前置排障动作。

更实用的做法,是先检查它到底有没有在当前系统中加载:

lsmod | grep nouveau

如果有输出,说明它很可能正在占用显卡相关能力。
在很多 Ubuntu 场景下,常见处理方式会是:

cat >/etc/modprobe.d/blacklist-nouveau.conf <<'EOF'
blacklist nouveau
options nouveau modeset=0
EOF

update-initramfs -u
reboot

重启后再执行:

lsmod | grep nouveau

如果没有输出,通常才说明这层冲突已经被清掉了。

5. 本地运行时,为什么 PyTorch 版本不能随便装

很多人把重点都放在 vLLM 或 SGLang 的安装命令上,却忽略了它们背后真正依赖的运行基础。

5.1 PyTorch 不是可有可无的配角

在很多本地部署路径里,PyTorch 并不是“顺手装一下”的依赖,而是整个推理框架能否正常使用 CUDA 的关键基础层。

如果类比来看:

  • 驱动是硬件层前提
  • CUDA 是 GPU 计算环境
  • PyTorch 则是很多上层推理框架真正踩住的地基

所以很多看起来像“vLLM 安装失败”的问题,最后其实是:

  • PyTorch 和 CUDA 不匹配
  • Python 环境混乱
  • 依赖装到了错误的虚拟环境

5.2 为什么建议先验证 PyTorch,再装推理框架

因为 PyTorch 的验证更简单,也更容易定位问题。

如果连 torch.cuda.is_available() 都不正常,你继续往上装 vLLM 或 SGLang,最后只会把问题包在更外层。

更稳的方式通常是:

  1. 先把 PyTorch 跑通
  2. 再让 vLLM 或 SGLang 建立在这套可验证环境之上

这会比“直接一路装到框架启动”更可控。

6. 容器化前,为什么一定要理解 NVIDIA Container Toolkit

本地环境跑通之后,很多团队的下一步就是 Docker 化。
而这一步里最关键的,不是镜像本身,而是 GPU 透传。

6.1 为什么 Docker 不是装完就能看见 GPU

Docker 默认并不知道该怎么把 NVIDIA GPU 交给容器。

也就是说,即使宿主机上:

  • nvidia-smi 正常
  • 驱动加载正常
  • CUDA 环境正常

也依然可能出现下面这种情况:

  • 宿主机能看到 GPU
  • 容器里却完全看不到 GPU

6.2 NVIDIA Container Toolkit 到底解决什么问题

NVIDIA Container Toolkit 的核心作用,是把宿主机的 GPU 能力以正确方式接入容器运行时。

它通常要解决的是:

  • 容器如何访问 NVIDIA 设备
  • 容器如何挂载所需驱动相关能力
  • Docker 或 containerd 如何知道某个工作负载需要 GPU

所以它本质上是“宿主机 GPU 能力”和“容器运行时”之间的桥。

如果你走的是 Docker 路线,一个比较常见的安装与验证流程会长这样:

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

curl -s -L 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 update
sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

安装完成后,不要直接上 vLLM 或 SGLang,先用最小镜像做验证:

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

如果配置正常,容器里会看到和宿主机类似的表格输出,例如:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14              Driver Version: 550.54.14      CUDA Version: 12.4     |
|  0   NVIDIA A10                      On | 00000000:00:03.0 Off  |                    0 |
+-----------------------------------------------------------------------------------------+

只要这条命令不通,就先别继续怀疑模型镜像,优先回头检查容器运行时配置。

6.3 在 Docker 和 K8S 里,它的角色为什么都重要

很多人以为:

  • Docker 需要它
  • K8S 里有 Device Plugin,就不需要它了

实际上并不是这样。

在 K8S 场景里,NVIDIA Device Plugin 主要负责的是资源发现和调度暴露;
但宿主机容器运行时本身,仍然要先具备正确的 NVIDIA GPU 容器支持能力。

所以两者不是替代关系,而是分工不同:

  • NVIDIA Container Toolkit 解决“容器怎么用 GPU”
  • NVIDIA Device Plugin 解决“K8S 怎么发现并分配 GPU”

7. 一套更稳的验证链路:不要一上来就跑大模型

环境准备阶段,最浪费时间的事情之一,就是一上来就拿一个大模型硬跑。

更稳的做法,通常是从小到大逐层验证。

7.1 先验证宿主机

先确认:

  • lspci 能看到 NVIDIA 设备
  • nvidia-smi 正常
  • nvcc --version 可用

这一步解决的是宿主机层面的问题。

7.2 再验证 Python 与 CUDA

确认 Python 环境里:

  • 能导入 torch
  • torch.cuda.is_available()True
  • 能识别到 GPU 数量

这一步解决的是框架基础依赖问题。

7.3 再验证容器

如果你要走 Docker 或 K8S,就先不要急着跑完整推理服务。
先用最小化 CUDA 测试镜像验证:

  • 容器内能否执行 nvidia-smi
  • 容器是否识别到 GPU

一个最小可复用的 Docker 验证命令如下:

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

如果后面要上 K8S,也建议先在宿主机层把这条命令跑通。
因为这一步一旦失败,通常说明问题还在 Docker 或 NVIDIA Container Toolkit,而不是 K8S 编排层。

这一步非常有价值,因为它能把“宿主机问题”和“容器运行时问题”清楚分开。

7.4 最后再验证推理框架

只有前面三步都通过后,再去验证:

  • vLLM 是否能启动
  • SGLang 是否能启动
  • 模型是否能正确加载

这样一来,问题就不会从一开始就变成一个巨大的黑盒。

8. 最常见的几类坑,基本都出在这几层

如果把课件里的经验和日常部署里的问题放在一起看,环境阶段最常见的坑基本集中在下面几类。

8.1 宿主机能看到显卡,但驱动没真正打通

典型表现:

  • lspci 有结果
  • nvidia-smi 报错或找不到命令

这说明问题还停留在驱动层。

8.2 驱动正常,但 Python 侧还是用不了 CUDA

典型表现:

  • nvidia-smi 正常
  • torch.cuda.is_available()False

这通常说明问题已经从系统层进入到 Python 依赖层。

8.3 宿主机正常,容器却看不到 GPU

典型表现:

  • 宿主机 nvidia-smi 正常
  • 容器内 nvidia-smi 不正常

这通常要优先回头检查:

  • NVIDIA Container Toolkit
  • Docker 或 containerd 配置
  • GPU 运行时参数是否生效

8.4 环境没问题,但 GPU 被旧进程占满

这类情况在反复试验 vLLM、SGLang 时很常见。

典型表现:

  • 环境都正常
  • 但新服务启动失败或显存不足

这时就要先回到最基础的运维动作:

  • nvidia-smi 里有哪些进程占用 GPU
  • 清理残留 Python 进程或旧推理服务
  • 必要时清理 PyTorch CUDA 缓存

很多“看起来像安装失败”的问题,最后其实只是资源没有被释放干净。

9. 结语:环境准备不是前戏,而是决定后面效率的主战场

大模型私有化部署最容易让人低估的,就是环境准备这一步。

因为它表面上看不像框架选型那么“高级”,也不像模型效果那么“有成就感”,但它恰恰决定了后面所有工作的效率上限:

  • 你是不是能快速定位问题
  • 你是不是能把环境复现给别人
  • 你后面做 Docker 和 K8S 时是不是会不断返工

从实战角度看,一套健康的大模型推理环境,不是靠“把所有包都装一遍”得到的,而是靠一条清晰的验证链路逐层打通出来的:

  • 先确认 GPU 在
  • 再确认驱动通
  • 再确认 CUDA 可用
  • 再确认 Python 能调 GPU
  • 最后才是容器透传和推理框架启动

如果这条顺序是清楚的,后面的 vLLM、SGLang、Ollama 甚至 K8S GPU 调度,都会顺很多。
如果这条顺序一开始就是乱的,那么后面每一层都可能把问题继续放大。

下一篇文章,我们就从已经准备好的这套宿主机环境出发,继续往前走一步:
如何基于 Ubuntu 24.04 搭建一个真正能承载 AI 推理服务的原生 K8S 集群。

0

评论区