☰
Rocky Linux 9.x 部署 vLLM 推理服务:从裸系统到 OpenAI 兼容接口
2026/10/1 19:33:20 网站建设 项目流程

很多人一说在 Rocky Linux 9.x 上跑大模型推理,第一反应就是麻烦:装 NVIDIA 驱动、配 CUDA、搞 Docker GPU 透传、再装 vLLM 0.16.0,任何一步报错都能折腾半天。我在公有云英伟达 GPU 实例上完整踩过一遍之后发现,只要把顺序和依赖理清楚,从裸系统到跑起一个兼容 OpenAI 接口的推理服务,15 分钟是真实可行的——前提是别踩那些我后来整理出来的坑。这篇就把我从云主机选型、网络配置、驱动安装,到 vLLM 部署、模型加载、接口验证的完整过程写下来,适合手里有一台 NVIDIA GPU 云主机、想快速把 Qwen 或 DeepSeek 这类模型通过 vLLM 提供出去的开发者参考,新手也能照着操作。

1. 为什么我选 Rocky Linux 9.x + vLLM 这套组合

先回答一个很多人问的问题:云厂商 GPU 实例默认给的大多是 Ubuntu,为什么我坚持用 Rocky Linux 9.x?

我的理由很实际。Ubuntu 的优势是包新,但问题也出在"新"上:NVIDIA 驱动模块是靠 DKMS 挂到内核上的,Ubuntu 的 apt 一升级内核,驱动模块经常失效,nvidia-smi第二天就可能报"couldn't communicate with the NVIDIA driver"。对于要长期跑推理服务的生产环境,这种不确定性很烦人。Rocky Linux 9.x 基于 RHEL 9 的稳定分支,更新节奏保守,配合 DKMS 编译的 NVIDIA 驱动,跑几个月都不出问题。另外 RHEL 系自带 NetworkManager 和 firewalld,这套网络和防火墙管理方式,做运维的人都很熟,不需要额外学习成本。

再聊 vLLM 0.16.0。它是目前把大模型推理性能和易用性平衡得比较好的方案之一。核心优势有三个:

  • PagedAttention:把 KV Cache 按页管理,显存利用率比传统方案高很多,同样一张卡能塞更大的模型、更长的上下文。
  • Continuous Batching:请求不用排队等上一批跑完,而是动态插入当前 batch,并发一高吞吐也不会直线掉。
  • 原生 OpenAI 兼容接口:启动后直接提供/v1/chat/completions、/v1/embeddings等端点,你之前写的 OpenAI SDK 代码几乎不用改就能对接。

0.16.0 这个版本号不需要太纠结。vLLM 迭代很快,旧教程里的核心启动参数基本延续,本节的操作流程在 0.15.x 到 0.16.x 上都通用。我之所以锁定 0.16.0,是因为部署时 Docker 镜像和 PyPI 包直接用对应版本标签,方便复现和后期升级。

云主机的选择直接影响后续部署的顺利程度。我按实际使用场景给一个参考:

显存档位推荐型号适合场景
24GBNVIDIA L20 / A107B~14B 模型,单卡推理,开发测试
48GBNVIDIA L40S / L2014B~32B 模型,中等并发
80GBNVIDIA A100 / H10070B 级模型,高并发生产,张量并行

一个容易忽略的点:购买实例时优先选预装 NVIDIA 驱动和 Docker的镜像。公有云平台通常提供这类"GPU 基础镜像",省掉最耗时的驱动编译环节。标题说的 15 分钟,前提就是从这里开始的——如果从零装驱动,光编译就要 5~10 分钟,就不叫 15 分钟了。选不到预装镜像也没关系,下面我会把手动安装的步骤也完整写出来,多花 20 分钟而已。

2. 基础环境一步不落的搭建过程

2.1 网络配置:先解决 IP 漂移和防火墙

云主机拿到手先别急着装东西,把网络和防火墙搞定,后面少很多麻烦。Rocky Linux 9 的网络配置和 CentOS 7 时代不一样了,/etc/sysconfig/network-scripts/ifcfg-eth0那套已经废弃,现在统一走 NetworkManager,配置文件在/etc/NetworkManager/system-connections/目录下。

云平台上普通实例的内网 IP 一般重启不会变,但如果你用的是按量付费的临时实例,或者基于自定义模板克隆出来的机器,IP 漂移是真实存在的。配合 Docker 容器固定端口监听时,IP 一变很容易出现"服务活着但连不上"的诡异问题。

如果需要手动配置静态 IP,我建议用nmcli,比手改配置文件直观很多:

# 先看当前网卡名和连接名,一般是 enp0s3 / ens5 / System eth0 之类 nmcli con show # 修改连接配置,IP 换成你实际的规划地址 nmcli con mod "System eth0" \ ipv4.addresses 10.0.10.2/24 \ ipv4.gateway 10.0.10.1 \ ipv4.dns 223.5.5.5,8.8.8.8 \ ipv4.method manual # 重启连接生效 nmcli con up "System eth0" # 验证 ip addr show

这里有个经验:改之前一定先确认云平台 VPC 网段和网关地址,填错了直接 SSH 断开,只能在控制台用 VNC 救回来。

防火墙方面,Rocky 9 默认 firewalld 是开启的,vLLM 默认监听 8000 端口,必须放行:

firewall-cmd --permanent --add-port=8000/tcp firewall-cmd --reload

还有一步经常被漏掉——云控制台的安全组规则。防火墙放行只解决系统内部,安全组没放行 8000 端口,外部仍然访问不了。买完实例后记得去控制台把入方向 8000/TCP 加上,最好顺手把来源 IP 限定成你自己办公网段,别裸奔到 0.0.0.0/0,毕竟这是个可以执行任意模型推理的接口。

2.2 NVIDIA 驱动:装错一次排查半小时

如果云厂商镜像里已经带 NVIDIA 驱动,直接跳过这一步,用nvidia-smi验证一下即可。需要手动装的话,Rocky 9 上我最推荐的是 NVIDIA 官方 RHEL 9 仓库方式,比下载.run脚本更干净,也方便后续用dnf更新:

# 先安装内核开发包,版本必须和当前内核完全一致 dnf install -y kernel-devel kernel-headers "kernel-devel-uname-r == $(uname -r)" # 添加 NVIDIA 官方仓库 dnf install -y https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo # 安装驱动,latest-dkms 会随内核自动重编模块 dnf module install -y nvidia-driver:latest-dkms # 验证 nvidia-smi

看到类似这样的输出就代表驱动正常:

+-----------------------------------------------------------------------------+ | NVIDIA-SMI 570.124.06 Driver Version: 570.124.06 CUDA Version: 12.8 | +-----------------------------------------------------------------------------+

几个关键点我必须强调:

  • kernel-devel-uname-r == $(uname -r)这种写法可以保证版本精确匹配。内核和开发包版本不一致,DKMS 编译必失败,装完nvidia-smi一定报错。
  • 如果之前手动跑过.run安装脚本,建议先nvidia-uninstall卸载干净,再走 dnf 路线,避免两份驱动打架。
  • UEFI Secure Boot 开启的机器,DKMS 模块签名会失败。公有云 GPU 模板通常默认关闭,如果你用的是自建虚拟化环境,进 BIOS 关掉,或者配置 MOK 签名,否则驱动永远加载不上。
  • 如果启动时报nouveau模块冲突,在/etc/modprobe.d/blacklist-nvidia.conf里写上blacklist nouveau,然后重建 initramfs。

安装完之后最好重启一次,确认重启后nvidia-smi依然正常。这一步能过滤掉大量"内核更新后驱动失效"的隐患。

2.3 Docker 和 NVIDIA Container Toolkit:打通 GPU 透传

vLLM 的 Docker 镜像自带完整 CUDA 运行库,宿主机不需要装 CUDA Toolkit,只需要两样东西:Docker 和 NVIDIA Container Toolkit。前者提供容器运行环境,后者负责把 GPU 设备透传给容器。

安装 Docker:

dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin systemctl enable --now docker

安装 NVIDIA Container Toolkit:

dnf install -y dnf-utils yum-config-manager --add-repo https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo dnf install -y nvidia-container-toolkit # 关键一步:把 nvidia 运行时注册到 Docker nvidia-ctk runtime configure --runtime=docker # 重启 Docker 生效 systemctl restart docker

验证 GPU 透传是否打通,用这个最直接的命令:

docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubi9 nvidia-smi

容器里能看到和宿主机一致的 GPU 信息,说明透传成功。

这里要提醒一句:很多人装完 toolkit 忘了执行nvidia-ctk runtime configure --runtime=docker,导致容器里永远看不到 GPU。这一步的本质是修改/etc/docker/daemon.json,把nvidiaruntime 写进 Docker 配置。如果你看到容器报错could not select device driver "" with capabilities: [[gpu]],十有八九就是漏了这一步。

3. vLLM 0.16.0 的两种部署路径

3.1 路径一:官方 Docker 镜像,最快跑通

Docker 方式是我的首选,因为镜像里已经编译好所有 CUDA 依赖和 vLLM 本体,宿主机只需要一个干净驱动,整个部署过程就是拉镜像、起容器、加载模型三步。

拉取镜像:

docker pull vllm/vllm-openai:v0.16.0

启动容器,这里我以 24GB 显存跑DeepSeek-R1-Distill-Qwen-7B为例:

docker run --gpus all \ --ipc=host \ -p 8000:8000 \ -v /opt/models:/models \ --env "HUGGINGFACE_HUB_CACHE=/models/.cache" \ vllm/vllm-openai:v0.16.0 \ --model /models/DeepSeek-R1-Distill-Qwen-7B \ --served-model-name deepseek-r1-7b \ --max-model-len 32768 \ --gpu-memory-utilization 0.9

拆解一下几个关键点:

  • --ipc=host:多进程 IPC 共享,不设的话在某些数据集分布式加载场景会报 shared memory 不足。这是 vLLM 官方推荐加的,老教程经常漏。
  • -v /opt/models:/models:模型权重目录挂载。我习惯把大模型放独立数据盘,路径统一放/opt/models,避免重装系统或换实例时重新下载几十 GB 权重。
  • --served-model-name:对外暴露的模型名。起什么名都行,客户端请求时"model"字段必须和它一致。
  • --max-model-len 32768:最大上下文长度。这个值直接影响 KV Cache 显存占用,后面会单独讲。
  • --gpu-memory-utilization 0.9:允许 vLLM 使用单卡 90% 显存做模型权重和 KV Cache,留 10% 给驱动显示和紧急情况。

容器启动后日志会进入模型加载阶段,看到Starting vLLM server和Uvicorn running on http://0.0.0.0:8000就说明服务起来了。

3.2 路径二:pip 虚拟环境方式,便于二次开发

如果你不只是想跑服务,还要改 vLLM 源码、调试自定义模型,或者公司网络不允许拉 Docker 镜像,那走 pip 方式更合适。Rocky 9 默认 Python 是 3.9,vLLM 对 Python 版本有下限要求,我建议直接用 Python 3.11:

dnf install -y python3.11 python3.11-devel /usr/bin/python3.11 -m venv /opt/vllm-env source /opt/vllm-env/bin/activate # 升级 pip 并安装 vllm pip install --upgrade pip pip install vllm==0.16.0

安装完直接起服务:

python -m vllm.entrypoints.openai.api_server \ --model /models/DeepSeek-R1-Distill-Qwen-7B \ --served-model-name deepseek-r1-7b \ --max-model-len 32768 \ --gpu-memory-utilization 0.9 \ --port 8000

用python -m方式比直接敲vllm serve更可控,可以在前面加nohup或用 systemd 管理。

两种方式怎么选?我的判断标准很简单:如果是纯部署交付,选 Docker,环境隔离好、回滚方便、团队成员拉起来就能跑;如果要做模型适配、性能调优或二次开发,选 pip 方式,改代码和调试都更直接。很多公司是把 pip 方式调好后再封装成自己的镜像,两条路径并不矛盾。

3.3 关键启动参数逐项拆解

vLLM 的启动参数很多,但真正影响部署成败的就这几个,我整理成表格方便对照:

参数作用我的建议
--model模型路径或 HuggingFace 模型名优先用本地路径,避免运行时下载
--served-model-name对外暴露的模型名自定义短名,方便客户端调用
--max-model-len最大上下文长度按业务需求设置,别一味求大
--gpu-memory-utilizationvLLM 可用显存比例默认 0.9,显存紧张可调 0.85
--tensor-parallel-size张量并行卡数单卡默认 1,多卡按实际卡数设置
--dtype模型权重精度默认 auto,显存不足可试 half
--quantization量化方式配合 AWQ/GPTQ 量化模型使用
--enforce-eager关闭 CUDA Graph显存紧张时开,吞吐略降

--max-model-len和显存的换算关系是新手最容易懵的地方。大模型推理时显存占用主要是两块:模型权重 + KV Cache。7B 模型 bf16 精度权重约 14GB,KV Cache 则随max-model-len和并发数线性增长。假设 24GB 显存、权重占 14GB,剩下的 10GB 里如果塞 32768 上下文,单并发还好,并发一高就会 OOM。所以部署前先想清楚业务场景:聊天应用给 8192~16384 就够,长文档分析再上 32768 甚至 65536。

4. 第一次推理验证:从模型下载到 API 响应

4.1 模型权重的两种获取方式

vLLM 启动时--model参数填模型名,它会自动去 HuggingFace 下载,但我强烈建议手动预下载到本地。原因很简单:自动下载一旦中断要重来,而且路径在容器和宿主机之间不好管理。

我习惯用huggingface-cli先拉权重:

pip install -U "huggingface_hub[cli]" mkdir -p /opt/models huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-7B \ --local-dir /opt/models/DeepSeek-R1-Distill-Qwen-7B

如果你的服务器访问 HuggingFace 速度不理想,可以设置镜像环境变量加速,这个在很多团队已经验证可用:

export HF_ENDPOINT=https://hf-mirror.com huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-7B \ --local-dir /opt/models/DeepSeek-R1-Distill-Qwen-7B

下载前确认三件事:磁盘空间、磁盘空间、还是磁盘空间。7B 模型 bf16 权重约 15GB,70B 模型要 140GB 以上,下载前df -h看一眼,别把系统盘塞爆。另一个细节:模型目录挂载进容器时,注意目录属主和 SELinux 标签,报Permission denied或failed to mount时在挂载参数后面加:Z让 Docker 自动调整标签,或者chown -R 1000:1000 /opt/models,容器内默认用户 UID 是 1000。

4.2 用 OpenAI 兼容接口做一次真实请求

服务起来后,先用 curl 做一次最简单的请求,验证链路是否真的通了:

curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-r1-7b", "messages": [{"role": "user", "content": "用一句话解释 vLLM 是什么"}], "max_tokens": 256, "temperature": 0.7 }'

正常会返回一段 JSON,里面有choices、usage字段,usage.completion_tokens显示实际生成了多少个 token。这一步如果通了,整个部署链路基本没问题。

如果你习惯用 Python,OpenAI SDK 直接就能接,因为 vLLM 的接口就是照 OpenAI 格式做的:

from openai import OpenAI client = OpenAI( base_url="http://localhost:8000/v1", api_key="EMPTY" # vLLM 默认不校验 key,随便填 ) resp = client.chat.completions.create( model="deepseek-r1-7b", messages=[{"role": "user", "content": "写一段 100 字的自我介绍"}], max_tokens=256 ) print(resp.choices[0].message.content)

这里有个常被忽略的点:请求里model字段必须等于启动参数里的--served-model-name。很多人直接填模型原始名,比如deepseek-ai/DeepSeek-R1-Distill-Qwen-7B,结果报model not found,其实不是模型没加载,而是名字对不上。

4.3 性能预检:显存、吞吐、延迟怎么看

服务起来别急着上线,先用几分钟做性能预检。预检分三块:

一看显存。另开一个终端跑watch -n 1 nvidia-smi,观察Memory-Usage和Volatile GPU-Util。稳定的推理服务显存使用曲线应该接近一条平线,因为 vLLM 启动时就把模型权重和 KV Cache 池分配好了。如果显存波动剧烈或频繁接近上限,说明并发控制有问题。

二看日志吞吐。vLLM 启动后日志里会持续输出类似这样的统计数据:

Avg prompt throughput: 18.2 tokens/s Avg generation throughput: 45.6 tokens/s Running: 3 reqs, Waiting: 2 reqs

这里的信息量很大:Avg generation throughput是每卡每秒生成 token 数,Running和Waiting的比值能反映并发排队情况。如果Waiting一直很高,说明请求已经超出单卡处理能力,要么加卡做张量并行,要么限流。

三看单请求延迟。用上面那个 curl 命令,配合time统计首 token 时间和总耗时。首 token 时间超过 3 秒、总耗时超过 20 秒,业务侧体验就会明显变差,这时候要检查是不是模型权重没有量化、max-model-len设太大导致 KV Cache 吃紧,或者 GPU 利用率根本没跑上去。

5. 高发坑位与排查链路

5.1 nvidia-smi 连接失败:驱动模块层面的排查

这是露脸频率最高的问题,报错一般是:

NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the NVIDIA driver is installed and running.

注意:出现这个报错不代表显卡坏了,绝大多数时候是驱动模块没有加载进内核。我的排查顺序是固定的,按步骤走能少走很多弯路:

  1. 看内核里有没有一个叫nvidia的模块:

    lsmod | grep nvidia

    如果空,说明模块没加载。

  2. 看模块文件是否存在:

    ls /usr/lib/modules/$(uname -r)/kernel/drivers/video/nvidia.ko.xz

    文件不存在,说明当前内核根本没有编译对应驱动模块。

  3. 查 DKMS 状态:

    dkms status

    如果显示nvidia/570.124.06: installed就正常;显示Unable to install或没有记录,就是 DKMS 编译失败了。

  4. 检查编译失败的日志:

    cat /var/lib/dkms/nvidia/*/build/make.log | tail -50

    最常见的失败原因就是kernel-devel版本和uname -r不一致。解法非常简单——重新安装匹配版本的内核开发包,然后强制重编模块:

    dnf install -y "kernel-devel-uname-r == $(uname -r)" dkms autoinstall
  5. 重启前确认 secure boot 已关闭,然后reboot。重启后如果nvidia-smi还是报同样错误,继续查dmesg | grep -i nvidia,看有没有具体的权限或签名报错。

这个链路帮我救回过不止一台机器。很多时候大家一慌就重装驱动,其实问题只是内核更新后 DKMS 模块没重建,重装驱动反而把环境搞得更乱。

5.2 容器内看不到 GPU:runtime 配置的锅

宿主机nvidia-smi正常,但容器一启动就报:

0 GPU is visible, but the requested number of GPUs is 1. docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].

排查链路如下:

  1. 先确认 toolkit 是否安装成功:

    nvidia-container-cli info

    如果这条命令本身报错,按它提示的缺失库处理,一般是libnvidia-container没装完整。

  2. 看 Docker runtime 配置是否生效:

    cat /etc/docker/daemon.json

    正常应该有一行"runtimes": {"nvidia": {"path": "nvidia-container-runtime", ...}}。没有就是漏了nvidia-ctk runtime configure --runtime=docker,补上再重启 Docker:

    nvidia-ctk runtime configure --runtime=docker systemctl restart docker
  3. 检查nvidia-uvm模块:

    lsmod | grep nvidia_uvm

    CUDA 依赖nvidia_uvm,它没加载时容器能起来但无法做 GPU 计算。手动加载:

    modprobe nvidia_uvm

另外一个小坑:如果你用的是 podman 或 containerd 而不是 Docker,--gpus参数的行为不一样,toolkit 的配置也要对应调整。文章里我默认 Docker,因为它在云上最通用。

5.3 端口通不了:防火墙、安全组和 SELinux 三座山

本机 curllocalhost:8000正常,但外网访问超时,这个问题通常不是 vLLM 的问题,而是网络路径上有三重拦截:

  • firewalld:前面已经放行了 8000 端口。检查当前状态用firewall-cmd --list-all,看到ports: 8000/tcp才行。
  • 云安全组:控制台里入方向规则必须允许 8000/TCP。常见情况是云厂商模板默认只放行 22 和 3389,其他端口一律拒绝。
  • SELinux:容器映射端口时 SELinux 一般不放行。最简单且安全的做法是检查并仅针对容器服务放行。如果你测试环境图快,可以临时setenforce 0,但生产环境我建议用正确的方式:ausearch -m avc -ts recent查看 SELinux 拦截记录,再对应写 allow 规则。

排查顺序建议从外到内:先 telnet 云主机的公网 IP 8000 端口,不通再查安全组;安全组通了我们再进主机测firewall-cmd。一层层缩小范围,比一次性在所有地方瞎改高效得多。

5.4 显存不够:不是模型太大,是参数没算明白

部署时最常见的实际错误是CUDA out of memory。很多时候模型明明装得下,是参数设置把显存算爆了。

一个 7B 模型的显存估算公式很简单:

  • 模型权重:7B × 2 字节(bf16)= 约 14GB
  • KV Cache:粗略估算为 2 × 层数 × 头维度 × 最大长度 × batch,实际运行起来 24GB 显卡上给 32768 上下文的 KV Cache 要预留 6~10GB
  • 中间激活值、CUDA context 等杂项:约 1~2GB

所以 24GB 卡跑 7B 模型,max-model-len设 32768、并发 8 时已经很紧张。遇到 OOM 时,按优先级调整:

  1. 把--max-model-len降到 16384 或 8192,效果立竿见影。
  2. 把--gpu-memory-utilization从 0.9 降到 0.85,给系统留更多余量,虽然 KV Cache 池会变小,但更稳定。
  3. 启用--enforce-eager,关闭 CUDA Graph 以节省显存,代价是吞吐略有下降。
  4. 换用 AWQ/GPTQ 量化权重,7B 模型从 bf16 的 14GB 降到 4bit 的约 4GB,那 24GB 卡跑 32B 量化模型都游刃有余。

还有一个我踩过的坑:--tensor-parallel-size设成 2,结果机器上只有一张卡,vLLM 启动直接报错。多卡并行前一定先确认nvidia-smi里看到几张卡,再设置并行度。


最后分享一个小体会:vLLM 部署这件事,真正花时间的从来不是 vLLM 本身,而是它底下的驱动、容器和网络环境。我在这台 Rocky Linux 9.x 机器上把所有流程记成了一套标准操作清单之后,后续给新实例部署基本就是复制粘贴、换模型路径的事。这套链路里坑的位置几乎是固定的——DKMS 没编上、runtime 没注册、安全组没放行——你把这几个点提前验证一遍,15 分钟真的够用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询