写这篇东西的起因,是近期帮几个项目组把vllm部署从开发机迁到生产Ubuntu服务器,发现很多人卡在最开始的“ubuntu + conda + vllm”组合上:有的conda环境装完发现Python版本不对,有的一启动就报CUDA错误,还有的干脆在虚拟机上折腾半天GPU都识别不到。其实这套流程本身不复杂,但中间有大量“文档不会写但你一定会遇到”的细节,值得掰开揉碎讲一遍。
整篇内容围绕一件事展开:如何在Ubuntu系统上通过conda创建独立环境,把vllm装好并成功跑起大模型推理服务。我会把从零开始的环境准备、conda安装、镜像源配置、vllm安装、模型下载、启动参数、量化与缓存优化,再到生产环境常用的docker-compose方式,以及常见的排查链路全部过一遍。适合刚接触大模型部署的算法工程师,也适合要在服务器上把服务稳定跑起来的运维或后端同学。
1. 为什么不用系统Python直接装:先想清楚环境隔离这件事
很多人在Ubuntu上装vllm有个下意识反应:打开终端,直接pip install vllm。在刚装好的干净系统上,这也许能跑通,但用不了一个月就会后悔。vllm对Python版本、PyTorch版本、CUDA版本的耦合度非常高,而系统的Python往往由apt管理,动它可能会干扰系统工具链。更稳妥的做法是先用conda把环境隔离出来,再在隔离环境里装vllm。
1.1 vllm的依赖到底有多“挑剔”
vllm之所以对依赖敏感,是因为它依赖一个完整的推理运行时栈。你可以把vllm理解成一辆组装好的赛车:发动机是CUDA,变速箱是PyTorch,而vllm本身是底盘和电控系统。换任何一个部件版本,都可能影响整辆车的表现。
具体来看,vllm在编译时会链接特定版本的PyTorch、CUDA runtime以及flash-attention等算子库。如果你用系统Python,这个环境里可能已经装了某个版本的numpy、torch或者其他的科学计算包,pip在解决依赖时经常会“为了兼容现有包而选择旧版vllm”,或者反过来为了装新版vllm把环境搞乱。conda的价值就在这儿:它可以让你为vllm单独划一个干净的房间,房间里的Python、pip、lib都是可控的。
另外值得一提的是,vllm官方发布的预编译wheel包,通常会声明requires-python >=3.8,但要装较新版本(比如0.7.x及以后),建议直接用Python 3.10到3.12之间的版本。如果你拿Python 3.7去试,大概率会在安装阶段就收到“找不到匹配版本”的提示。
1.2 从驱动到CUDA到PyTorch的版本匹配链条
除了Python隔离,安装vllm前还要理清一条版本匹配链:
- GPU驱动:由NVIDIA驱动提供,负责与硬件通信,使用
nvidia-smi可以看到。 - CUDA:这里要注意区分驱动自带的CUDA Driver和真正用于编译运行的CUDA Toolkit。vllm运行时主要依赖Driver对应的CUDA版本兼容性,而pytorch的cuda版本则是通过pip安装到Python环境里的。
- PyTorch和vllm:PyTorch在编译时会选定一种CUDA版本变体,比如
cu121、cu124,vllm的wheel同样会针对这些变体发布。
链条关系大致是:GPU→NVIDIA驱动(决定允许的CUDA版本范围)→PyTorch的CUDA变体(在Python环境内)→vllm依赖的torch接口。实际使用中,驱动版本决定上限,只要驱动够新,常见的CUDA 12.1和12.4都可以跑。
所以整个安装流程的第一步不是装conda,而是装好NVIDIA驱动后先运行nvidia-smi确认驱动版本和顶部显示的CUDA Version。很多文档把conda放第一步是有误导性的——先有驱动可见,后面所有问题才有意义。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Ubuntu侧准备:Miniconda安装与国内镜像加速
Ubuntu上装conda,官方推荐Miniconda而不是完整版Anaconda,因为Miniconda体积小、默认环境干净,适合在服务器上用。下载位置建议直接使用清华大学开源软件镜像站或中科大镜像,直接从repo.anaconda.com下载在国内速度不稳定。
2.1 安装Miniconda时容易被跳过的两步
安装Miniconda的命令并不复杂,注意别漏掉两个细节:
bash复制wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
执行bash脚本后,一路按回车和输入yes即可。默认安装位置是$HOME/miniconda3。这一步有两个容易被跳过的点:
第一,安装脚本的最后会询问是否运行conda init来初始化shell。如果你选了no,安装完后输入conda经常会提示“command not found”。如果之前没选,可以手动执行source ~/miniconda3/bin/activate再运行conda init bash。
第二,如果你是给root用户或者某个服务账号安装,要留意PATH环境变量的变化。conda init会把conda的初始化代码写入~/.bashrc,但某些非交互式shell或者systemd服务里不会加载它。届时通过systemd拉起服务时,会直接报conda: command not found,需要在service文件里显式指定conda可执行文件的完整路径。
安装完成后,建议先做一次conda --version验证。如果终端还是找不到conda,请关闭并重新打开终端,或者执行source ~/.bashrc。
2.2 配置conda源、pip源,以及用conda-forge跳过版权坑
国内服务器安装conda后,第一件事是换镜像源。conda源和pip源是两个不同的体系,相互独立,都需要配置。
配置conda源是在用户目录下的.condarc文件中写入镜像地址。推荐使用阿里云、清华或中科大的Anaconda源。我这里以清华源为示例:
yaml复制channels:
- defaults
show_channel_urls: true
default_channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
而pip源一般在~/.pip/pip.conf(如果没有就创建)里配置,比如用阿里云的镜像:
ini复制[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
trusted-host = mirrors.aliyun.com
有一个常见误区:从官方Miniconda安装后直接conda install pytorch时,如果license问题导致下载被卡,或者由于Anaconda商业条款限制已经被官方repo移除,此时应该改用conda-forge通道或者直接用pip安装PyTorch。热搜词里那句“conda使用conda-forge或miniforge避免版权”说的就是这个。Miniforge默认配置conda-forge作为唯一通道,完全规避了Anaconda的repo授权问题,如果你所在的企业对软件版权比较敏感,建议干脆使用Miniforge替代Miniconda。
配置完成后,执行conda clean -i清一下索引缓存,再执行conda info,看看输出里的channel URLs是否已经指向镜像。如果镜像配置错了,后面创建环境时会报HTTP错误或长时间卡在“Solving environment”。
3. 创建vllm专用conda环境,并把安装时间压缩到二十分钟内
conda环境创建的核心是conda create -n命令。对于vllm部署,我习惯直接指定Python 3.10或3.12,不要先建环境再单独改Python版本。一次性指定版本能省掉后面很多不必要的依赖重算。
bash复制conda create -n vllm-env python=3.10 -y
conda activate vllm-env
3.1 conda create指定Python版本的设计逻辑
你可能会问:为什么要专门建一个环境,而不是直接在base里装?原因主要有三个。一是base环境往往自带很多基础库,与vllm的依赖存在版本冲突风险;二是后续做版本升级时,如果vllm新版本要求特定Python版本,你只需要再建一个环境而不影响其他项目;三是如果哪天环境坏了,直接删除这个env重建即可,不用重装系统Python或Miniconda。
进入环境后,第一件事检查Python版本和pip版本:
bash复制python --version
pip --version
然后升级pip并安装vllm。vllm官方推荐用pip安装预编译wheel,不要自己从源码编译。源码编译vllm意味着要编译flash-attention以及一大堆C++/CUDA扩展,耗时可能从40分钟到数小时,还极易失败。
bash复制pip install --upgrade pip
pip install vllm
如果你需要特定版本,可以指定版本号,比如pip install vllm==0.6.3.post1。安装时会自动拉取匹配当前Python版本和CUDA版本的wheel,这个过程会同时安装torch、transformers、tokenizers等依赖。
3.2 第一次请求时不要忘记GPU驱动的可见性测试
装完vllm后,默认的验证方式只是python -c "import vllm; print(vllm.__version__)"。版本能打印出来只说明Python侧没问题,不等于能调用GPU。我建议紧接着做第二条验证:
bash复制python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.device_count())"
如果输出True且GPU数量大于0,说明PyTorch能看到GPU,vllm大概率也能正常用。如果输出False,不要急着重装vllm,先去排查nvidia-smi是否正常、驱动是否加载、当前用户是否有GPU设备权限。很多时候问题不在vllm,而是PyTorch编译时选的CUDA版本与驱动不匹配。
如果是在Docker容器里,还需要额外注意容器有没有挂载GPU设备,这个留在第5章细说。如果在VMware虚拟机里,要注意虚拟机设置里有没有开启“加速3D图形”或PCI直通GPU,否则NVIDIA驱动在虚拟机里会识别不到物理GPU。
4. 部署Qwen3模型:从模型下载到vllm启动命令逐项拆解
安装完成后,实际部署才能真正检验环境是否可靠。由于最近Qwen3系列模型使用频率比较高,尤其像Qwen3-8B以及MoE结构的Qwen3-30B-A3B这类,被问得最多。我用一个实际的qwen3模型部署案例,把vllm启动命令逐项拆开讲。
4.1 用modelscope离线拉取模型
在生产环境里,很多服务器没有直接访问HuggingFace的稳定链路,更常见的做法是从ModelScope下载模型,或者在一台能联网的机器上下好之后拷贝到目标机器。ModelScope和HuggingFace的模型目录结构基本一致,vllm可以通过设置环境变量或者直接传本地路径来读取。
先安装ModelScope客户端:
bash复制pip install modelscope
下载Qwen3-8B模型:
bash复制modelscope download --model Qwen/Qwen3-8B --local_dir /data/models/Qwen3-8B
用--local_dir指定下载到本地某个目录,是为了让vllm能直接用这个本地路径启动,省去模型搜索流程。下载完成后检查一下目录里有没有config.json、tokenizer.json、模型权重文件等必要文件。模型文件巨大,建议下载前先确认磁盘剩余空间足够,至少保留模型体积两倍的余量。
如果你的生产服务器本来就是离线环境,需要在一台能联网的机器上下载好,再通过scp或内网传输。注意离线传输时不要只拷权重文件,config.json、tokenizer_config.json、generation_config.json这些配置也必须一起拷,否则vllm启动时会报找不到配置。
4.2 启动参数到底调哪些,为什么是这些
vllm启动服务的最基本命令是:
bash复制vllm serve /data/models/Qwen3-8B \
--served-model-name qwen3-8b \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.9 \
--max-model-len 32768 \
--host 0.0.0.0 \
--port 8000
每个参数都值得解释一下:
--served-model-name:给模型起一个对外暴露的名字,客户端调用时填这个名字。如果不填,默认使用模型目录名。--tensor-parallel-size:张量并行数。单卡就是1,多卡可以设成卡数。这个参数决定模型如何切分到多张GPU上。Qwen3-8B在单张24GB显存的卡上基本可以跑,如果显存不足再考虑多卡。--gpu-memory-utilization:指定vllm最多使用多大比例的GPU显存。默认是0.9,即预留10%给其他进程。如果该卡只跑这一个模型,可以调到0.95,但不要调到1.0,否则显存分配和计算kernel之间可能产生意外冲突。--max-model-len:最大序列长度。需要结合模型支持的上下文长度和显存容量来权衡。设得越大,KV cache占用的显存越多,并发能力就下降。Qwen3-8B本身支持较长的上下文,但服务器显存不够时,建议把32K降到16K来换取更高的并发。--host 0.0.0.0:监听所有网络接口,这样才能让别的机器调这个服务。如果只在本地测试,保持默认的127.0.0.1更安全。
启动后出现Starting vLLM API server on http://0.0.0.0:8000这类日志,说明服务已经起来了。可以用curl验证OpenAI兼容接口是否正常:
bash复制curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "qwen3-8b", "messages": [{"role": "user", "content": "你好,请简单介绍一下你自己。"}]}'
4.3 配置缓存与量化,让显存利用率翻倍
如果是刚接触vllm,先把服务跑通是第一步。如果要在保证一定并发的前提下把模型塞进有限显存,就需要了解两个关键词:prefix caching和量化。
vllm在较新版本里提供了自动化前缀缓存(--enable-prefix-caching),开启后对于多轮对话或相似prompt的重复前缀,KV cache可以被复用,从而显著降低首个token延迟,也能提升整体吞吐。这个参数对生产环境几乎建议默认开启,尤其当业务场景里有大量system prompt固定前缀的请求时,效果非常明显。
bash复制vllm serve /data/models/Qwen3-8B \
--served-model-name qwen3-8b \
--enable-prefix-caching \
--gpu-memory-utilization 0.9
量化则是把模型权重的精度从FP16降到INT8、INT4或更低位,牺牲少量精度换显存。常用的量化格式有AWQ和GPTQ,对应的启动参数是--quantization awq或--quantization gptq。需要注意的是,量化版模型文件与原始FP16模型文件不是同一个,需要下载时明确指定带awq或gptq的版本。例如Qwen3系列在ModelScope上通常有Qwen3-8B-AWQ这样的模型目录。如果拿原始模型路径却加了--quantization awq,会直接报权重格式不匹配。
我在实际项目中见过不少例子,8B模型在24GB卡上开了prefix caching后,并发数从8提升到20以上。量化比较适合对精度要求不极端、但对吞吐要求很高的线上服务。
4.4 dp参数的用途和适用场景
热搜词里出现了--dp,这其实是较新版本vllm引入的数据并行参数。数据并行和张量并行思路不同:张量并行是把同一个模型切到多张卡,每张卡负责一部分;数据并行是每张卡放一个完整模型副本,请求按卡分发,从而提升总吞吐。对显存足够但单卡吞吐不够的场景,--dp可以有效利用多张卡。
比如有两张48GB显存的卡,每张都能独立装下一个Qwen3-30B-A3B的量化模型,那么可以用--dp 2把两份副本同时跑起来。要强调的是,--dp和--tensor-parallel-size不是同一个维度,不能随意混用。通常如果单卡能装下模型,优先考虑dp;如果单张卡放不下,只能张量并行。混合使用不是不行,但会显著增加显存和通信开销,对普通部署不太推荐。
5. 生产环境升级:docker-compose部署vllm与Bare安装的取舍
当vllm真正要跑在生产服务器上,很多人会面临一个问题:继续用conda裸环境,还是套一层Docker?两条路我都走过,我的结论是:开发调试阶段用conda裸环境最高效,正式对外提供API服务时,docker-compose方式更利于复现、升级和迁移,但要做对几件事,否则坑比好处多。
5.1 nvidia-container-toolkit是docker跑GPU的前提
在Ubuntu上让Docker容器使用GPU,必须先安装NVIDIA Container Toolkit,否则即使宿主机驱动正常,容器内也调用不了GPU。安装步骤并不复杂,需要添加NVIDIA官方apt仓库,然后安装nvidia-container-toolkit。
bash复制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-get update
sudo apt-get install -y nvidia-container-toolkit
然后重启Docker:
bash复制sudo systemctl restart docker
安装完成后,在宿主机执行docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi来验证。这条命令能输出GPU信息,说明容器已经可以访问GPU了。如果这一步失败,后面vllm容器肯定也起不来,不用急着去调vllm的Dockerfile。
5.2 docker-compose文件里真正容易写错的部分
vllm官方提供了现成的Docker镜像,通常镜像名是vllm/vllm-openai,也可以在GitHub Releases中找到对应的镜像地址。生产部署时,很多团队不会直接用裸docker run,而是在一个docker-compose文件里统一管理。我用一个实际范例来说明:
yaml复制services:
vllm:
image: vllm/vllm-openai:latest
container_name: vllm-qwen3
command:
- --model
- /models/Qwen3-8B
- --served-model-name
- qwen3-8b
- --gpu-memory-utilization
- "0.9"
- --host
- "0.0.0.0"
- --port
- "8000"
ports:
- "8000:8000"
volumes:
- /data/models:/models
environment:
- HF_HOME=/root/.cache/huggingface
- NCCL_DEBUG=INFO
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
restart: unless-stopped
ipc: host
有几个地方很容易写错:
第一是command的写法。yaml文件里如果直接写--gpu-memory-utilization 0.9这种带空格的字符串,会被当成一个整体参数传给容器,导致参数解析失败。正确写法是把参数拆成列表项,镜像的entrypoint会自动拼接。
第二是deploy.resources里的GPU配置。老版本docker-compose用runtime: nvidia加环境变量NVIDIA_VISIBLE_DEVICES=all的方式指定GPU,新版本推荐在deploy.resources.reservations.devices里声明capabilities: [gpu]。两种写法在docker-compose规范里兼容性不同,如果用的是较老的docker-compose版本,需要确认是否支持新版语法。
第三是ipc: host。vllm在多卡并行时会用到共享内存进行某些数据传输,如果容器默认的/dev/shm太小,可能导致数据加载慢甚至报错。设置成host模式是最简单的解法。
第四是模型目录的挂载。容器内的路径和宿主机不一致的情况下,启动命令里--model参数必须指向容器内的路径,别把宿主机路径直接传进去。
5.3 什么时候该回到conda裸环境,什么时候用容器
在开发机的conda环境里改代码、调参数跑通后,再进入Docker部署的流程,是比较自然的节奏。容器的主要优势是交付一致性:同一个镜像在测试机和生产机上表现完全一致,不用再担心某个服务器上系统库版本不一样。但它也有麻烦的地方,比如日志收集、容器内调试、模型文件更新不如裸环境直接。如果模型文件经常变化、需要频繁试验不同启动参数,我建议先在conda环境里验证好,再固化到镜像和docker-compose里。否则每次改参数都要重新构建或者重启容器,反而把试错时间放大了。
6. 实际踩坑排查链路:从“命令不存在”到“模型加载报错”
无论写多少安装教程,实际部署时总会遇到各种各样的问题。这里整理几条真实踩坑的完整排查链路,让大家在出问题时能按照链路一步步找原因,而不是病急乱投医。
6.1 conda命令找不到,多数时候不是没装好
热搜词里有句很有代表性的报错:'conda' 不是内部或外部命令,也不是可运行的程序或批处理文件。这个报错是Windows的cmd风格,说明你是在Windows命令提示符下执行的conda命令。部分用户在远程服务器上操作时,可能先用Windows的cmd或PowerShell尝试执行conda,才看到这句报错。解决思路是,要么在cmd里先执行conda activate(前提是Anaconda/Miniconda装了且被加入到PATH),要么直接切换到WSL或Ubuntu终端再操作。
如果是在Linux的bash里报conda: command not found,排查链路是:
- 用
ls ~/miniconda3/bin/conda确认conda是否真的装在了这个位置。 - 如果文件存在,在终端执行
export PATH="$HOME/miniconda3/bin:$PATH"测试能否调通。 - 调通后,执行
conda init bash并重启shell,确认~/.bashrc里是否出现了conda初始化代码。 - 如果使用的是zsh或其他shell,需要对应执行
conda init zsh。
如果是在systemd服务里调用conda环境内的python,情况就不一样了。systemd默认不加载用户的~/.bashrc,它只执行ExecStart里写的命令。正确写法是直接在ExecStart里使用conda环境里的python绝对路径,例如ExecStart=/home/ubuntu/miniconda3/envs/vllm-env/bin/python -m vllm.entrypoints.openai.api_server ...,而不是先写conda activate。
6.2 VMware/虚拟机里边GPU识别不到是怎么回事
虚拟机上跑vllm是大模型部署里一个特殊的常见场景。很多人为了学习方便,在VMware里装Ubuntu,然后想实测vllm,结果发现nvidia-smi直接报错。这很可能是宿主机和虚拟机的GPU透传没有设置好。
VMware Workstation对于NVIDIA GPU的支持,长期只提供“3D加速”级别的支持,可以跑图形渲染,但不一定能做CUDA计算。要在虚拟机里完整使用GPU做CUDA计算,需要开启PCI直通或使用vGPU功能,而Workstation对PCI直通的限制较多,往往需要企业级的vSphere/ESXi环境。
一条更省心的经验是:如果你只是想在个人电脑上学习vllm,用WSL2(Windows Subsystem for Linux 2)往往比VMware顺手得多。WSL2里通过Windows侧的GPU驱动直接支持CUDA,Ubuntu环境里装conda和vllm后GPU天然可见。需要注意的是,WSL2安装时需要先装Windows侧的NVIDIA驱动,并且要求驱动版本支持WSL。如果你坚持用VMware,也可以先在宿主机上确认nvidia-smi正常,再检查VMware的虚拟机设置里是否添加了PCI设备,把GPU物理设备直通进去。这一步操作门槛不低,对新手来说试错成本会很高。
6.3 预编译wheel下载慢或版本冲突:替换源与锁定版本
在国内网络环境下,直接用pip install vllm时最常遇到两件事。
一是下载大文件超时。vllm的wheel包体积经常超过100MB,PyTorch更是动辄几百MB到2GB。即使已经给pip配置了国内镜像,也要留意有些镜像同步vllm的wheel不够及时,这时候需要临时指定一个同步更快的镜像或用代理(内网中转)下载。更稳妥的做法是先下载wheel文件再本地安装:
bash复制pip download vllm -d /tmp/vllm_pkgs
pip install /tmp/vllm_pkgs/*.whl
二是版本冲突。由于vllm对torch版本有明确依赖范围,如果你手动先装了某个torch版本,再装vllm时可能会被提示需要升级或降级torch。此时不要强行--no-deps安装,否则运行时会出现找不到符号或版本不兼容的问题。正确的做法是让pip自动解析依赖,直接安装即可。如果担心torch重复下载,可以先把torch和vllm放在同一次安装里执行:
bash复制pip install torch vllm
pip在同一个依赖解析过程里会优先满足两者的共同约束,比分开安装减少冲突。
6.4 模型加载失败时的两个高频原因
模型加载阶段报错,通常集中在两个地方:一是本地模型路径下的文件不完整,二是模型与vllm版本不兼容。
文件不完整的典型表现是:下载过程中中断,config.json存在但权重文件缺失,vllm启动时会报safe_open读取失败或者找不到model-00001-of-0000X.safetensors。另一个表现是目录名里带了空字符或不可见字符,传参时被shell截断。排查时先ls -lh查看模型目录,确认权重文件数量和大小是否合理。
关于兼容性,vllm新版本发布后通常会优先适配最新架构,像Qwen3这类新模型可能需要较新版本的vllm才支持。如果你的vllm版本较老,启动时会出现unsupported architecture或者could not determine model architecture。解决方式是先升级vllm再启动。同时,离线下载模型的机器最好与线上部署的机器使用同一套ModelScope或HuggingFace的snapshot格式,否则有可能出现tokenizer目录结构不一致导致分词器加载失败。
6.5 缓存命中率低和并发性能差,先检查这几个参数
部署完成之后,如果发现吞吐不理想,不要急着怀疑vllm本身。vllm在生产场景中性能差,很多情况下是参数没调到位。
第一,要确认--enable-prefix-caching有没有开启。如果使用默认的调度策略,重复的prompt前缀在每次请求时都会重新计算KV cache,导致大量显存带宽被浪费。开启后可以在日志里看到类似Prefix cache hit rate的统计。如果命中率一直很低,说明业务请求的公共前缀较少,需要从系统设计侧去压缩prompt长度或固定system prompt格式。
第二,要注意--max-num-seqs和--max-model-len的搭配。max-num-seqs决定一次推理batch最多能塞进多少个序列。如果设得太小,GPU来不及并行处理多个请求;如果设得太大,超出的序列会排队等待,kv cache不够时甚至会触发swap,显著降低性能。常规经验是,max-model-len设置得越短,可并行的序列数就可以越大;如果模型长度设到32K,显存会被单个长序列的kv cache占掉很多,此时强行增加max-num-seqs反而没收益。
第三,考虑是否开启--enable-chunked-prefill。它的作用是让一个超长prompt的预填充阶段被切块,和其他请求的解码阶段混合调度,减少气泡。对于prompt长度差异很大的业务场景,这个参数能提升整体吞吐。如果请求大多是短文本,这个参数的收益则不明显。
从另一面说,如果vllm运行中频繁出现OOM或者CUDA out of memory,就需要把gpu-memory-utilization调低,并同步降低max-model-len或max-num-seqs。生产环境最忌讳的是把显存用满到100%,因为模型推理时的临时算子也需要少量显存,留5%到10%余量是明智的。
7. 把conda环境打包迁移到另一台机器的三个办法
运维场景里经常需要把开发好的环境整体搬到另一台机器上,比如从本地开发机迁到内网GPU服务器。这时重新去服务器上执行一遍conda create和pip install当然可行,但如果网络不给力或者依赖包版本记不清了,可以尝试环境打包迁移。这里介绍三种常见方案,按适用场景选择。
7.1 用conda env export迁移跨平台环境
一种最简单的方案是把环境配置导出成yaml文件,在目标机器上重建。执行conda env export > environment.yaml后,文件中记录了所有conda包和pip包的信息,包括版本号。在目标机器上执行:
bash复制conda env create -f environment.yaml
这个方案最适合Ubuntu到Ubuntu的迁移,因为底层平台一致。但要注意,导出的文件里可能包含当前机器的绝对路径,如果conda前缀路径不一致,可能出现文件找不到的情况。遇到这种问题,可以手动编辑导出的yaml,把prefix行删掉,让conda自动选择默认路径。另外,如果环境里有些包只在当前平台存在,完全换平台时(比如从Windows迁移到Linux)不能用这个方案,原因在于很多库的编译产物无法跨平台复用。
7.2 pip freeze方案适合Linux同构迁移
如果conda环境里的核心包都来自pip,直接用pip freeze生成依赖清单是最省事的:
bash复制pip freeze > requirements.txt
在目标机器上,先创建conda环境并指定Python版本,然后执行pip install -r requirements.txt。这样做的速度取决于目标机器访问PyPI镜像的速度,通常比重下vllm快不少。不过要小心的是,pip freeze会把所有通过pip安装的包都记录下来,包括一些传递依赖,如果本地环境本身已经很混乱,导出的清单可能在目标机器上解析失败。更干净的做法是在新环境里只手动安装核心依赖,其余交给pip自动解析。
7.3 conda-pack彻底离线迁移
目标机器完全离线时,前面两种方案都失效了,这时推荐使用conda-pack。在源机器上先安装conda-pack,然后打包:
bash复制conda install conda-pack
conda pack -n vllm-env -o vllm-env.tar.gz
把打包出来的tar.gz拷贝到目标机器,解压到conda环境的目录下,比如~/miniconda3/envs/vllm-env,然后激活。解压后需要执行一下环境内部的激活脚本,让库路径生效:
bash复制mkdir -p ~/miniconda3/envs/vllm-env
tar -xzf vllm-env.tar.gz -C ~/miniconda3/envs/vllm-env
source ~/miniconda3/envs/vllm-env/bin/activate
conda-pack会把整个vllm环境里的Python、pip、已安装包统统打包,解压后处于“半激活”状态,需要运行conda-unpack来修正路径。这个方案的好处是不依赖网络,缺点是包体积很大,vllm环境常常好几个GB,传输时需要耐心。另外,打包环境的机器和目标机器必须是同样的CPU架构和操作系统发行版,Ubuntu 20.04的包不能直接搬到Ubuntu 22.04甚至CentOS上用,因为glibc版本可能不兼容。这一点在迁移前一定先确认,否则解压后各种库加载报错会让人非常崩溃。
写在最后的一点建议
装vllm这件事,说难不算难,说简单也不算简单。回头再看整条链路,最花时间的往往不是安装本身,而是环境隔离思路不清、驱动与CUDA版本不对齐、模型文件不完整这三类问题。我在实际操作中养成了一个习惯:每换一台新机器,第一件事不是急着装conda和vllm,而是先跑一遍nvidia-smi确认驱动,再在干净环境里装一个最小版本验证GPU可用,然后才进行正式流程。这个习惯帮我省下过大量排查时间,建议你也试试。
另一个小技巧是,如果某条命令在网上查到好几种说法,以vllm官方文档和当前机器上的实际报错为准,不要盲目照搬旧教程。vllm迭代速度很快,很多启动参数在几个版本之间会有变化,网上流传的解决方案不一定适用于你手头的版本。遇到问题先看log,再查对应的版本changelog,这比反复试错高效得多。
