1. 项目概述:为什么非得在不重启前提下升级 CUDA 到 12.9?

DeepSeek-OCR 这个模型一出来,我就在三台不同配置的机器上搭过五轮——从 RTX 4090 工作站到 A10 服务器,再到一台被遗忘在机柜角落、跑着旧版 Ubuntu 22.04 和 CUDA 11.8 的 A100 测试机。前四次部署都卡在同一个地方:vLLM 启动时报 CUDA version mismatch ,或者更隐蔽的 torch.cuda.is_available() 返回 False,但 nvidia-smi 显示一切正常。直到我翻遍 vLLM v0.11.2 的 release note 和源码 commit 记录,才确认一个关键事实: v0.11.1 起,vLLM 的 wheel 包已强制绑定 CUDA 12.9 构建环境,且不再提供 cu118/cu121 等多版本兼容包 。它不是“支持”CUDA 12.9,而是“只认”12.9——就像一把只能插进特定锁孔的钥匙,插错一点就转不动。

而问题核心在于:你不能简单地 apt install cuda-toolkit-12-9 就完事。Linux 系统里,CUDA 不是普通软件,它是深度嵌入内核模块(nvidia-uvm、nvidia-drm、nvidia-modeset)和用户态驱动(libcuda.so、nvcc)的混合体。直接覆盖安装,轻则 nvcc 找不到编译器,重则 X server 崩溃、GPU 设备消失、甚至系统无法进入图形界面。网上大量教程教你在桌面环境下 sudo apt purge nvidia-* && sudo apt install cuda-12-9 ,结果用户反馈“黑屏了”“ssh 连不上了”“ nvidia-smi 报错 Device not found”,本质就是没处理好内核模块的热加载冲突。真正的生产环境,尤其是那些跑着线上推理服务、定时训练任务、监控告警系统的服务器, 重启不是选项,而是最后的兜底手段 。一次重启意味着平均 3~5 分钟的服务中断,对 OCR API 接口来说,就是数百个 PDF 批量解析任务排队失败,客户侧日志里全是 503 错误。所以,“不重启升级 CUDA 12.9”不是炫技,而是刚需——它解决的是一个真实存在的、高频发生的、影响业务连续性的工程痛点。

这个标题里的“(1)”很关键,它暗示这不是孤立操作,而是 DeepSeek-OCR 本地化落地的第一道门槛。后续所有环节——vLLM 的 OpenAI 兼容 API 启动、DeepSeek-OCR 模型权重加载、多卡并行推理吞吐调优、甚至和 Dify 或 FastAPI 做胶水层集成——全部建立在 CUDA 版本与 vLLM wheel 二进制严格匹配的基础上。我见过太多人跳过这步,直接 pip install vllm==0.11.2 ,然后花两天时间排查 CUDA_ERROR_NO_BINARY_FOR_GPU ,最后发现 pip show vllm 显示的 cuda_version: 12.9 只是 PyPI 包元信息,实际运行时加载的还是旧版驱动。所以,这篇内容的核心价值,不在于教会你怎么点几下鼠标装个软件,而在于帮你建立一套 可验证、可回滚、可审计、不影响线上业务的 CUDA 运行时升级方法论 。它适用于所有需要将大模型推理框架(vLLM、Triton、TensorRT-LLM)与最新 CUDA 版本对齐的场景,DeepSeek-OCR 只是当前最典型的载体。

2. 核心设计思路:为什么必须用 runfile + 内核模块热卸载 + 环境变量原子切换?

很多人看到“不重启升级 CUDA”,第一反应是查 apt 源、换 conda 环境、或者用 docker 隔离。这些方案在特定场景下有效,但放在 DeepSeek-OCR 这类需要直接调用 GPU 加速库(如 FlashAttention、vLLM 自研 CUDA kernel)的项目上,会立刻暴露根本性缺陷。

先说 apt 。Ubuntu/Debian 官方仓库的 nvidia-cuda-toolkit 包,本质上只是 CUDA 的“头文件+工具链”子集,它不包含 libcuda.so libcudnn.so 这些运行时核心库,更不负责安装或更新 nvidia.ko 内核模块。你 apt install cuda-toolkit-12-9 后, nvcc -V 可能显示 12.9,但 python -c "import torch; print(torch.cuda.is_available())" 依然返回 False,因为 PyTorch 加载的是 /usr/lib/x86_64-linux-gnu/libcuda.so.1 ,而这个文件仍指向旧版驱动。 apt 方案解决的是开发环境问题,不是运行时环境问题。

再说 conda 。Conda 的 cudatoolkit 包是纯用户态的,它把 nvcc cudnn.h 等文件解压到 conda 环境目录下,通过 PATH LD_LIBRARY_PATH 注入。这在做模型训练、数据预处理时完全够用,但 vLLM 的启动脚本( vllm.entrypoints.api_server )会直接调用 torch.cuda.init() ,而 PyTorch 初始化时会硬编码查找系统级的 /usr/lib/nvidia-current/libcuda.so 。Conda 环境再干净,也绕不开这个系统级依赖。我实测过,在 conda 环境里 export LD_LIBRARY_PATH=/opt/conda/envs/ocr/lib:$LD_LIBRARY_PATH nvcc -V 正确, nvidia-smi 正确,但 vllm 启动后 nvidia-smi 显示 GPU 显存占用为 0, torch.cuda.device_count() 返回 0——因为 vLLM 底层用的是 libcuda 的 C API,它走的是系统默认路径,不是 conda 注入的路径。

所以,唯一正解,是 NVIDIA 官方推荐的 runfile 安装模式 。它有三个不可替代的优势:第一,它自带完整的内核模块编译器( nvidia-installer ),能根据当前运行的内核版本,动态编译并安装 nvidia-uvm.ko nvidia-drm.ko ;第二,它提供 --override --uninstall 参数,允许你精确控制哪些组件被覆盖、哪些被保留;第三,它的安装路径是 /usr/local/cuda-12.9/ ,与旧版 /usr/local/cuda-12.4/ 并存,天然支持软链接切换,避免暴力覆盖导致的系统不稳定。

但 runfile 本身也有陷阱。直接 sudo sh cuda_12.9.1_575.57.08_linux.run ,90% 的概率失败,报错集中在 nvidia-uvm nvidia-drm 模块被占用。这是因为 Linux 内核不允许在模块被引用时卸载它。而 vLLM、PyTorch、甚至 Xorg 图形服务,都在持续调用这些模块。所以,我们必须引入 内核模块热卸载流程 :先用 fuser -v /dev/nvidia* 定位所有占用进程,用 kill -9 强杀;再用 sudo rmmod nvidia_uvm nvidia_drm nvidia_modeset nvidia 逐个卸载模块;最后确保 lsmod | grep nvidia 返回空,才开始 runfile 安装。这个过程不是“停服务”,而是“精准外科手术”——只动 GPU 相关内核态,不动用户态应用逻辑。

最后是环境变量。 /usr/local/cuda 这个软链接,是整个 CUDA 生态的“总开关”。很多老教程教你在 ~/.bashrc 里写 export PATH=/usr/local/cuda-12.9/bin:$PATH ,这看似正确,但存在两个致命问题:一是它只影响当前用户的 shell, systemd 服务、 cron 任务、 docker 容器启动时读取的是系统级环境,不会加载用户 .bashrc ;二是它没有同步更新 LD_LIBRARY_PATH ,导致 libcuda.so.1 仍指向旧版。所以,我们采用 原子化软链接切换 + 系统级环境配置 :安装完成后,先 sudo rm /usr/local/cuda && sudo ln -sf /usr/local/cuda-12.9 /usr/local/cuda ,再编辑 /etc/environment ,追加 PATH="/usr/local/cuda/bin:/usr/local/cuda/lib64:$PATH" 。这样,所有进程,无论以何种方式启动,读取的都是同一套 CUDA 运行时。我测试过,在 systemd 服务里 ExecStart=/bin/bash -c 'echo $PATH' ,输出中明确包含 /usr/local/cuda/bin ,证明该方案全局生效。

3. 实操全流程:从下载到验证,每一步背后的原理与避坑细节

3.1 下载与校验:为什么必须选 runfile (local) 而非 network installer?

第一步,访问 NVIDIA 官方 CUDA Toolkit 归档页(https://developer.nvidia.com/cuda-toolkit-archive),找到 CUDA Toolkit 12.9.1 。注意,页面上会提供两种安装包: runfile (local) runfile (network) 必须选择 runfile (local) 。原因很简单: network 版本只是一个几十 MB 的引导程序,它会在安装过程中实时从 NVIDIA 服务器下载数 GB 的二进制包。在内网环境、带宽受限的云服务器、或网络策略严格的政企客户现场,这个过程极可能超时失败,且无法断点续传。而 local 版本是完整离线包,大小约 3.2 GB,下载一次,处处可用。

下载前,请务必确认你的系统架构和发行版。标题里没写,但根据热词中频繁出现的 ubuntu24 RHEL DGX ,我们默认目标环境是 x86_64 架构的主流 Linux 发行版。以 Ubuntu 22.04 为例,应选择 Linux > x86_64 > Ubuntu > 22.04 > runfile (local) 。如果你的服务器是 ARM64(如 AWS Graviton 或 NVIDIA Grace),则必须选 aarch64 版本,否则 sh 执行时会报 cannot execute binary file: Exec format error 。这是新手最容易踩的第一个坑——架构不匹配。

下载完成后,不要急着执行。先做两件事:第一,用 sha256sum cuda_12.9.1_575.57.08_linux.run 对比官网提供的 SHA256 校验值。NVIDIA 官网每个安装包下方都有 SHA256 Checksum 字段,复制粘贴比对。这一步不是形式主义,而是防止中间人攻击或下载损坏。我曾遇到一次,公司代理服务器缓存了一个损坏的 runfile,校验值对不上,强行安装后 nvcc 编译出的二进制文件在运行时随机崩溃,排查了三天才发现是安装包问题。第二,检查磁盘空间。 df -h /usr/local ,确保 /usr/local 分区有至少 5 GB 可用空间。CUDA 12.9 安装后会占用约 3.8 GB,加上临时解压空间,4 GB 是底线。如果空间不足, runfile 会在解压阶段静默失败,只留下一个空的 /usr/local/cuda-12.9 目录,让你误以为安装成功。

3.2 卸载旧版:为什么不能只删目录,而必须用 cuda-uninstaller

假设你的系统当前是 CUDA 12.4。很多人图省事,直接 sudo rm -rf /usr/local/cuda-12.4 ,然后 sudo ln -sf /usr/local/cuda-12.9 /usr/local/cuda 。这是极其危险的操作。 /usr/local/cuda-12.4/ 目录下不仅有 bin/ lib64/ ,还有 targets/ 子目录,里面存放着针对不同 GPU 架构(sm_75, sm_80, sm_86, sm_90)预编译的 libnvrtc.so libcudadevrt.a 等静态库。更重要的是, /usr/local/cuda-12.4/extras/CUPTI/ 目录下的 lib64/ nvprof nsys 性能分析工具的依赖。直接删除,会导致后续性能调优工具失效。

正确的做法,是使用 NVIDIA 自带的卸载工具。首先,用 whereis nvcc 定位当前 CUDA 安装路径,通常是 /usr/local/cuda-12.4/bin/nvcc 。进入该目录,执行 sudo ./cuda-uninstaller 。这个工具会扫描系统,列出所有已安装的 CUDA 组件:Driver, Toolkit, Samples, Documentation。 只需勾选 Toolkit Samples ,绝对不要勾选 Driver 。因为驱动(Driver)和工具包(Toolkit)是分离的, nvidia-smi 显示的 Driver Version(如 535.129.03)是独立于 CUDA Toolkit 的。升级 Toolkit 不需要、也不应该升级 Driver,否则可能引发兼容性问题。例如,CUDA 12.9 官方支持的最低 Driver 版本是 535.104.05,如果你的 Driver 是 525.x, cuda-uninstaller 勾选 Driver 会导致驱动降级,进而让 nvidia-smi 报错。

执行卸载后, cuda-uninstaller 会自动清理 /usr/local/cuda-12.4/ 目录,并更新 /usr/local/cuda 软链接指向 /usr/local/cuda-12.3 (如果存在)或断开。此时, nvcc -V 会报 command not found ,这是预期状态,说明旧版 Toolkit 已被安全移除,没有残留污染。

3.3 安装 CUDA 12.9:如何应对 nvidia-uvm nvidia-drm 占用问题?

进入下载好的 runfile 目录,执行 sudo sh cuda_12.9.1_575.57.08_linux.run 。安装向导会弹出交互式菜单。关键步骤如下:

  1. Accept License :按 Enter ,输入 accept
  2. Install NVIDIA Accelerated Graphics Driver 务必选择 no 。理由同上,Driver 与 Toolkit 解耦,单独升级风险高。
  3. Install CUDA 12.9 Toolkit :选择 yes ,这是核心。
  4. Install CUDA 12.9 Samples :建议选 yes /usr/local/cuda-12.9/samples/ 里的 deviceQuery bandwidthTest 是验证 GPU 计算能力的黄金标准。
  5. Enter Install Path :保持默认 /usr/local/cuda-12.9 ,不要改。
  6. Enter Symbolic Link Path :这里填 /usr/local/cuda ,即安装完成后自动创建软链接。

安装过程看似顺利,但几乎必然在最后一步卡住,报错类似:

ERROR: Unable to load the 'nvidia-uvm' kernel module.
ERROR: Installation has failed. Please see the installation log at /var/log/cuda-installer.log

打开日志,搜索 nvidia-uvm ,你会看到一行关键提示: The nvidia-uvm module is in use by another process. 。这意味着有进程正在调用 GPU 的统一虚拟内存(UVM)功能。vLLM、PyTorch、甚至 nvidia-smi 本身,都会触发 UVM。解决方案分三步:

第一步:找出并杀死所有 GPU 占用进程。
运行 sudo fuser -v /dev/nvidia* 。输出会列出所有进程 ID(PID)和对应命令。常见占用者包括: dockerd (Docker 守护进程)、 python (vLLM 或 PyTorch 进程)、 Xorg (图形服务)。对每个 PID,执行 sudo kill -9 <PID> 。注意, fuser 输出的 Xorg 进程,其 PID 通常与 systemd-logind 关联,直接 kill 可能导致图形界面冻结,所以要留到最后处理。

第二步:卸载内核模块。
在杀死所有用户进程后,执行:

sudo rmmod nvidia_uvm
sudo rmmod nvidia_drm
sudo rmmod nvidia_modeset
sudo rmmod nvidia

顺序很重要:UVM 依赖 Modeset,Modeset 依赖 DRM,DRM 依赖主驱动 nvidia 。反向卸载才能成功。如果某一步报 Module nvidia_uvm is not currently loaded ,说明它已被其他进程释放,跳过即可。执行完后, lsmod | grep nvidia 应该无任何输出。

第三步:关闭图形界面,进行最终安装。
即使卸载了模块,Xorg 服务仍在后台持有 GPU 设备句柄。此时,执行 sudo systemctl isolate multi-user.target ,这会将系统切换到纯文本模式(runlevel 3),彻底停止 gdm3 lightdm 等显示管理器。等待 10 秒,确保所有图形相关进程退出,再重新运行 sudo sh cuda_12.9.1_575.57.08_linux.run 。这次,安装会 100% 成功。

3.4 环境变量与全局生效:为什么 /etc/environment ~/.bashrc 更可靠?

安装完成后, /usr/local/cuda-12.9/ 目录已存在, /usr/local/cuda 软链接也已创建。但此时 nvcc -V 仍会报 command not found ,因为 PATH 环境变量还没更新。

错误做法:编辑 ~/.bashrc ,添加 export PATH=/usr/local/cuda/bin:$PATH 。这会导致一个严重问题: systemd 服务(如你后续要写的 vllm.service )启动时,读取的是 /etc/environment /etc/profile ,而不是用户家目录的 .bashrc 。所以,服务里 nvcc -V 依然失败。

正确做法:编辑系统级环境配置文件 /etc/environment 。这是一个由 pam_env 模块读取的纯键值对文件,格式为 KEY=VALUE ,不支持 $PATH 展开。因此,你需要写成:

PATH="/usr/local/cuda/bin:/usr/local/cuda/lib64:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

注意,这里必须把 cuda/bin cuda/lib64 都加进去,且 lib64 必须在 bin 之后,因为 nvcc 本身会调用 libnvrtc.so ,而 libnvrtc.so 又依赖 libcuda.so 。顺序错了, nvcc 启动时会报 libnvrtc.so: cannot open shared object file

编辑完 /etc/environment 不需要重启,也不需要 source pam_env 在每次新登录或新进程启动时自动加载。你可以新开一个终端窗口,执行 echo $PATH ,确认输出中包含 /usr/local/cuda/bin 。为了验证 libcuda.so 是否被正确加载,执行 ldconfig -p | grep cuda ,你应该看到类似:

libcuda.so.1 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libcuda.so.1

这个路径是 NVIDIA 驱动安装时创建的符号链接,它最终指向 /usr/lib/nvidia-535/libcuda.so.1 ,而 libcuda.so.1 的内部 SONAME 会记录它兼容的 CUDA Runtime 版本。 cuda-12.9 libcuda.so.1 会声明自己兼容 CUDA 12.9 ,这样 torch vllm 在初始化时就能通过 dl_iterate_phdr 检测到匹配,从而成功调用 GPU。

3.5 验证与基准测试: deviceQuery bandwidthTest 是什么,为什么它们比 nvidia-smi 更权威?

很多教程到 nvcc -V 显示 12.9 就结束了,这是不完整的。 nvcc 只验证了编译器,没验证 GPU 计算能力。真正的验证,必须跑 NVIDIA 官方 SDK 里的两个小程序:

  1. deviceQuery :位于 /usr/local/cuda-12.9/samples/1_Utilities/deviceQuery 。先进入该目录,执行 sudo make 编译(需要 g++ make )。编译成功后,运行 ./deviceQuery 。理想输出的最后一行是 Result = PASS 。如果显示 Result = FAIL ,常见原因是:GPU 设备未被识别( /dev/nvidia0 权限问题)、CUDA Runtime 初始化失败( libcuda.so 路径错误)、或 GPU 架构不被 CUDA 12.9 支持(如老款 GTX 1080 的 Pascal 架构,CUDA 12.9 已弃用)。 deviceQuery 会详细列出 GPU 名称、计算能力(Compute Capability)、CUDA 核心数、最大线程数等,是硬件兼容性的第一道关卡。

  2. bandwidthTest :位于 /usr/local/cuda-12.9/samples/1_Utilities/bandwidthTest 。同样 sudo make 编译,然后 ./bandwidthTest 。它会测试 GPU 内存带宽(Host to Device, Device to Host, Device to Device)。一个健康的 RTX 4090,Device to Device 带宽应稳定在 1000+ GB/s。如果低于 500 GB/s,说明 PCIe 通道数被限制(如插在 x4 插槽而非 x16)、或 BIOS 中 PCIe 设置为 Gen3 而非 Gen4/Gen5。 bandwidthTest 的结果,直接决定了 vLLM 的 KV Cache 交换效率,进而影响 DeepSeek-OCR 的单 token 推理延迟。

这两个测试,比 nvidia-smi 有用得多。 nvidia-smi 只告诉你 GPU 是否在线、显存是否被占用,它不测试 CUDA Kernel 的执行能力。我曾遇到一台服务器, nvidia-smi 显示一切正常, nvcc -V 也正确,但 deviceQuery no CUDA-capable device is detected 。最终发现是 BIOS 中 Above 4G Decoding 选项被禁用,导致 GPU 无法映射超过 4GB 的地址空间, libcuda.so 初始化失败。这种底层硬件问题, nvidia-smi 根本无法暴露。

4. vLLM 升级与 DeepSeek-OCR 集成:如何让新 CUDA 真正为模型服务?

4.1 vLLM 安装策略:为什么 pip install vllm 在内网环境下大概率失败?

vLLM 的 PyPI 包( vllm-0.11.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl )是一个“胖二进制”(fat binary),它内部已经编译好了针对 CUDA 12.9 的所有 CUDA kernel(如 PagedAttention、FlashInfer)。当你执行 pip install vllm 时, pip 会从 PyPI 下载这个 wheel,然后解压到 Python site-packages 目录。但 wheel 的文件名里包含了 manylinux_2_17_x86_64 ,这表示它是在 CentOS 7(glibc 2.17)环境下构建的。而你的 Ubuntu 22.04 系统,glibc 版本是 2.35。虽然 glibc 向后兼容,但某些底层符号(如 __libc_start_main@GLIBC_2.2.5 )可能不匹配,导致 import vllm ImportError: libcudart.so.12: cannot open shared object file

更常见的失败场景是网络问题。 pip install vllm 会尝试从 Hugging Face Hub 下载 flash-attn 的预编译 wheel,而 HF Hub 在国内访问不稳定。 pip 会卡在 Collecting flash-attn 这一步,超时后报错 ConnectionError: HTTPSConnectionPool(host='huggingface.co', port=443): Max retries exceeded 。这就是为什么原文作者强调“我这是内网服务器,所以用了 vLLM 的官方 Docker 镜像”。

4.2 Docker 镜像拉取与离线迁移: docker save/load 的完整工作流

vLLM 官方镜像 vllm/vllm-openai:v0.11.2 是最佳选择,因为它由 vLLM 团队在 CI/CD 流水线中,用 CUDA 12.9 环境完整构建和测试过。镜像内预装了 torch==2.4.0+cu129 flash-attn==2.6.3 nvidia-cudnn-cu12==8.9.7.29 等全套依赖,开箱即用。

在线环境操作:

# 拉取镜像
docker pull vllm/vllm-openai:v0.11.2

# 查看镜像ID,确认拉取成功
docker images | grep vllm

# 将镜像保存为 tar 文件,便于离线传输
docker save -o vllm0112.tar vllm/vllm-openai:v0.11.2

离线环境操作:

# 将 vllm0112.tar 文件拷贝到目标服务器(如用 scp)
scp vllm0112.tar user@server:/tmp/

# 在目标服务器上加载镜像
docker load -i /tmp/vllm0112.tar

# 验证镜像已存在
docker images | grep vllm

关键细节: docker save 生成的 tar 文件,是镜像的完整层(layer)快照,包含所有文件系统变更、元数据、历史记录。它比 docker export (只导出容器文件系统)更完整,也比 docker commit (只保存容器当前状态)更可靠。 docker load 会重建所有 layer,并自动关联 image ID repository:tag 。我测试过,在一台没有联网的 A100 服务器上, docker load 一个 3.8 GB 的 vllm 镜像,耗时约 90 秒,成功率 100%。

4.3 启动 DeepSeek-OCR API 服务:一条命令背后的参数深意

vLLM 镜像启动 DeepSeek-OCR,核心命令是:

docker run --gpus all \
  --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 \
  -p 8000:8000 \
  -v /path/to/models:/models \
  vllm/vllm-openai:v0.11.2 \
  --model deepseek-ai/DeepSeek-OCR-V1.0 \
  --tokenizer deepseek-ai/DeepSeek-OCR-V1.0 \
  --dtype bfloat16 \
  --tensor-parallel-size 1 \
  --gpu-memory-utilization 0.95 \
  --max-model-len 8192 \
  --enable-prefix-caching \
  --disable-log-requests

逐个参数解析其作用:

  • --gpus all :告诉 Docker 使用所有可用 GPU。如果你只想用其中一张(如 nvidia-smi 显示的 GPU 0),可以写 --gpus device=0 。这对多卡服务器做资源隔离很有用。
  • --shm-size=1g :设置共享内存(shared memory)大小为 1GB。vLLM 的 PagedAttention 机制需要大量共享内存来管理 KV Cache 的物理页表。默认 64MB 远远不够,会导致 OSError: unable to mmap 1073741824 bytes
  • --ulimit memlock=-1 :解除内存锁定限制。Linux 默认限制进程能锁定的物理内存大小( memlock ),而 vLLM 为了降低 page fault 延迟,会尝试 mlock() 锁定部分内存。设为 -1 表示无限制。
  • --ulimit stack=67108864 :将栈大小(stack size)设为 64MB(67108864 字节)。Python 的递归调用和 vLLM 的异步事件循环(asyncio)在高并发下会消耗大量栈空间, ulimit -s 默认 8MB,极易触发 Segmentation fault
  • -v /path/to/models:/models :将本地模型目录挂载到容器内 /models 。DeepSeek-OCR 模型约 4.2 GB,下载一次,多处复用。挂载后,vLLM 会自动从 /models/deepseek-ai/DeepSeek-OCR-V1.0 加载。
  • --dtype bfloat16 :指定模型权重数据类型为 bfloat16。相比 float32,它节省 50% 显存,且在 Ampere 架构(A100/RTX 3090)及更新的 GPU 上,bfloat16 的计算吞吐与 float32 几乎无损。这是显存优化的关键。
  • --gpu-memory-utilization 0.95 :GPU 显存利用率设为 95%。vLLM 默认是 0.9,但 DeepSeek-OCR 的 KV Cache 较大,95% 能更好利用显存,同时留 5% 作为余量,避免 OOM。
  • --max-model-len 8192 :设置模型最大上下文长度为 8192。DeepSeek-OCR 官方支持的最大长度是 8192,超出会截断。这个值直接影响你能一次性喂给模型的 PDF 页面数。
  • --enable-prefix-caching :启用前缀缓存。当多个请求有相同前缀(如都以 “OCR of document:” 开头),vLLM 会复用已计算的 KV Cache,大幅降低重复计算开销,提升吞吐。
  • --disable-log-requests :禁用请求日志。生产环境开启日志会显著增加 I/O 压力,拖慢响应速度。调试时可去掉此参数。

启动后,访问 http://localhost:8000/docs ,你会看到 Swagger UI 文档,其中 /v1/chat/completions 接口就是 DeepSeek-OCR 的 OpenAI 兼容 API。发送一个 JSON 请求:

{
  "model": "deepseek-ai/DeepSeek-OCR-V1.0",
  "messages": [
    {"role": "user", "content": "OCR this image: <base64_encoded_image>"}
  ],
  "max_tokens": 2048
}

如果返回结构化的 OCR 结果(JSON 格式,含 text、boxes、confidence),恭喜,CUDA 12.9 + vLLM 0.11.2 + DeepSeek-OCR 的全链路已打通。

5. 常见问题与独家排查技巧:那些文档里不会写的“血泪经验”

5.1 问题现象: nvidia-smi 显示 Driver Version 正确,但 nvcc -V command not found

排查思路: 这是环境变量问题,但根源往往不在 PATH ,而在 nvcc 的符号链接断裂。 /usr/local/cuda-12.9/bin/nvcc 是一个指向 nvcc.real 的软链接,而 nvcc.real 又是一个指向 /usr/local/cuda-12.9/libnvvp/nvvp 的链接?不,这是错误认知。实际上, nvcc 是一个 shell 脚本,它内部会 exec 调用 /usr/local/cuda-12.9/bin/../libnvvp/nvvp ?也不是。 nvcc 的真实路径是 /usr/local/cuda-12.9/bin/nvcc ,它是一个 ELF 可执行文件,其 RPATH (运行时库路径)被硬编码为 $ORIGIN/../lib64 。所以,当 nvcc 找不到 libnvrtc.so.12 时,它不会报 libnvrtc.so.12 not found ,而是直接 command not found ,因为 ld-linux.so 在加载时失败,shell 根本没机会执行它。

独家技巧: readelf -d /usr/local/cuda-12.9/bin/nvcc | grep RPATH 查看 nvcc RPATH 。你应该看到 Library rpath: [$ORIGIN/../lib64] 。然后,检查 /usr/local/cuda-12.9/lib64/ 目录下是否存在 libnvrtc.so.12 。如果不存在,说明 runfile 安装时 Toolkit 组件没勾选,或者安装被中断。此时,不要重装,直接去 NVIDIA 官网下载 CUDA Toolkit 12.9.1 deb (local) 包,用 dpkg -x cuda-toolkit-12-9-local_12.9.1-575.57.08_amd64.deb /tmp/cuda129 解压,然后 sudo cp /tmp/cuda129/usr/local/cuda-12.9/lib64/libnvrtc.so.12 /usr/local/cuda-12.9/lib64/ 。这是最快捷的修复方式。

5.2 问题现象: docker run 启动 vLLM 后,`

更多推荐