上一篇总览里,我们先把路线拆开了:
本地、Docker、K8S 解决的是“跑在哪儿”,Ollama、vLLM、SGLang 解决的是“用什么跑”。
但真正一开始动手时,很多人最先撞上的并不是框架差异,而是环境问题。
比如:
lspci能看到 GPU,为什么nvidia-smi还是报错- 驱动装好了,为什么 Python 里还是用不了 CUDA
- 本地能跑,为什么一进 Docker 容器就看不到显卡
- 容器里能看到 GPU,为什么到了 K8S 里又调度不出来
这些问题看起来分散,实际上都指向同一件事:
大模型推理环境不是“装一个框架”就结束了,而是一条从硬件识别、驱动加载、CUDA 运行时、Python 依赖到容器透传逐层打通的链路。
如果这条链路没有先理顺,后面的 vLLM、SGLang、Ollama,甚至 K8S 上的 GPU 调度,都会变成不断返工的源头。
所以这篇文章不急着讲某个框架怎么装,而是先把最底层、也最容易反复踩坑的一层讲清楚:
一台 GPU 主机想真正具备大模型推理能力,到底要准备哪些东西,它们之间是什么关系,安装顺序又该怎么安排。
摘要
先给结论。
一套可用于大模型推理的基础环境,至少要依次打通这五层:
- 宿主机能够识别 GPU 硬件
NVIDIA Driver正常加载,nvidia-smi可用CUDA Toolkit或对应运行时准备完成PyTorch或推理框架能够正确调用 CUDA- 如果要走容器化,还要完成
NVIDIA Container Toolkit与容器运行时配置
这五层里,最容易被混淆的是前三层:
Driver负责让操作系统识别并驱动 GPUCUDA Toolkit提供 CUDA 编译器、库和运行时PyTorch / vLLM / SGLang则建立在前两层之上,真正去调用 GPU 做推理
如果你只记一句话,可以先记住这一句:
先让宿主机识别 GPU,再让框架用上 CUDA,最后再让容器稳定透传 GPU。
系列导航
这是“大模型私有化部署实践”系列的第二篇。当前系列顺序如下:
- 本地、Docker、K8S:大模型私有化部署路线怎么选
- 大模型推理环境准备实战:GPU、驱动、CUDA、容器运行时
- 基于 Ubuntu 24.04 搭建 AI 推理用原生 K8S 集群
- 为 K8S 补齐入口与存储:MetalLB、Gateway API、NFS 动态供给
- 用 Ollama + Open WebUI 快速搭建本地 AI 体验环境
- vLLM 私有化部署实战:本地部署、Docker 部署、接口验证
- vLLM 上 K8S:服务部署、对外暴露、监控与验证
- SGLang 私有化部署实战:本地部署、Docker 部署、能力体验
- SGLang 上 K8S:接入 Open WebUI、服务发布与 GPU 运维
- 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 才开始变成“能干活的算力”。
不管是 PyTorch、vLLM 还是 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 已经安装,但
PATH和LD_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,最后只会把问题包在更外层。
更稳的方式通常是:
- 先把 PyTorch 跑通
- 再让 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 集群。
评论区