这次我们来看一个相当硬核的 DIY 项目:用 32 块 Nvidia CMP170HX 矿卡拼出一台“家用超级计算机”,目标是把显存堆到 2TB 级别,然后用来跑 vLLM 推理服务。这个思路的本质很简单,就是把原本面向专用计算市场的矿卡,改造成一张张纯计算卡,通过多卡并行凑出超大显存,用低成本覆盖大模型推理和高并发场景。
这项目最值得关注的点有三个。第一是显存规模,2TB 显存在常规单机方案里几乎不可能做到,单张 80GB 的 H100 要 25 张才能持平;第二是成本,CMP170HX 在二手市场的价格和同显存专业卡完全不在一个量级;第三是 vLLM 能否在这种“杂牌多卡”上稳定工作,这才是整个方案能不能落地的关键。下面我会按“硬件选型 -> 驱动/环境 -> 部署 vLLM -> 功能测试 -> API 调用 -> 性能观察 -> 排错”的顺序,把这套方案拆开讲清楚。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | DIY 多卡推理服务器,面向 vLLM 大模型部署 |
| 核心硬件 | 32 × Nvidia CMP170HX(矿卡/计算卡改造) |
| 总显存目标 | 2TB(以 32 卡方案计,实际单卡容量需按购买版本确认) |
| 主要用途 | 本地大模型推理、长文本生成、高并发 API 服务、批量任务 |
| 启动方式 | Ubuntu + Python 虚拟环境 + vllm serve 命令行启动 |
| 是否支持 API | 支持,vLLM 默认提供 OpenAI 兼容接口 |
| 是否支持批量任务 | 支持,可通过并发请求或批处理脚本实现 |
| 推荐系统 | Ubuntu 22.04/24.04,不建议 Windows 直接跑 vLLM |
| 主要限制 | 矿卡无视频输出、需要机架/转接/电源改造、散热考验大 |
| 适合读者 | 有一定硬件改造经验、想低成本堆大显存、熟悉命令行部署的开发者 |
从这张表能看出,这不是开箱即用的一键包项目,而是硬件与软件都要打磨的长期方案。如果你的目标是“买回来就能跑”,那直接考虑 4090 或者租云 GPU 更合适;如果你追求的是“极限单位显存成本”,那这套方案值得认真研究。
2. 适用场景与使用边界
2.1 适合做什么
这套 2TB 显存多卡方案,最大的优势是“显存容量”。大模型推理中最贵的资源就是 KV Cache,上下文越长、并发数越高,KV Cache 占用越大。2TB 显存可以支撑:
- 超长上下文测试:比如 128K 甚至更长上下文的开源模型,不需要频繁做量化压缩。
- 高并发 API 服务:在显存充足的情况下,vLLM 可以同时处理更多请求,吞吐量明显优于 24GB 单卡。
- 多模型常驻:不用来回切换模型,几个 70B 级别模型可以同时常驻显存。
- 批量离线推理:对一批文本、代码或文档做批处理,显存够大就不容易 OOM。
2.2 不适合做什么
- 大模型训练和全参微调:没有 NVLink,PCIe 通信带宽有限,训练效率会很低,微调也容易受跨卡通信瓶颈影响。
- 图像/视频渲染类任务:CMP170HX 本来就是无视频输出的计算卡,装进机箱也只是纯算力卡,不适合做图形渲染。
- 需要官方质保和稳定业务支持的生产环境:矿卡来源、改造成本和故障率都需要自己承担。
- 对功耗和噪音敏感的家庭环境:32 张卡的功耗和散热不是普通电脑桌能承受的。
2.3 使用边界与合规提醒
CMP170HX 属于特殊市场定位的显卡,通常以二手或拆机件流通。DIY 过程需要在合规测试环境中进行,购买硬件前要确认来源、型号、显存容量和接口规格。项目本身只能用于开发测试、开源模型部署和个人学习,不能用于挖矿,也不能用未授权数据或模型提供对外服务。涉及模型部署时,请优先使用具有明确开源许可证的模型;使用自有数据做推理时,要做好隐私保护和访问控制。这篇文章讨论的全部内容,都以合法采购、合法使用和测试环境验证为前提。
3. 环境准备与前置条件
3.1 操作系统
vLLM 对 Linux 的支持最成熟,这套多卡方案建议使用 Ubuntu 22.04 或 Ubuntu 24.04。Windows 下虽然可以通过 WSL 做部分验证,但 32 张卡的驱动、CUDA 和 PCIe 设备识别在 Windows 下会非常折腾,不建议走这条路。
用下面命令确认系统版本:
lsb_release -a uname -m3.2 GPU 驱动与 CUDA
CMP170HX 虽然来源特殊,但识别方式还是 NVIDIA 驱动那一套。先确认系统能看到多少张卡:
nvidia-smi -L nvidia-smi --query-gpu=index,name,memory.total --format=csv如果nvidia-smi没有输出,说明驱动没装好,或者卡的 PCIe 供电/转接线有问题。建议按 NVIDIA 官方驱动流程安装,完成后用nvidia-smi检查每一张卡是否被正确识别。
CUDA 版本与 PyTorch、vLLM 的匹配关系需要保持一致。更稳妥的做法是先装好 NVIDIA 驱动,再通过 pip 安装匹配的 PyTorch 和 vLLM。
3.3 Python 与虚拟环境
vLLM 依赖较多,强烈建议用虚拟环境隔离,不要直接装在系统 Python 里。
sudo apt update sudo apt install python3 python3-pip python3-venv -y # 创建虚拟环境 python3 -m venv vllm_env source vllm_env/bin/activate # 可以看到当前 Python 路径已在虚拟环境内 which python3.4 硬件前置检查
在跑 vLLM 之前,先确认这几点:
- 主板和 PCIe 通道够不够:32 张卡需要多路 PCIe 转接,通常要用矿机主板或带多条 PCIe x16/x1 插槽的服务器主板。
- 电源功率是否足够:单张 CMP170HX 的功耗不低,32 张卡的整机功耗会非常大,需要按单卡功耗总和留足余量。
- 散热风道:矿卡多为被动散热或涡轮散热,装入机箱后要保证风道能覆盖所有卡。
- 转接线和供电线:PCIe 转接线、电源模组线、机架固定件都要提前准备好。
如果这些前置条件没准备好,后面 vLLM 启动时会出现“识别不到 GPU”“显存不足”“驱动报错”等问题,并且很难判断是软件还是硬件导致的。
4. 部署 vLLM 与启动服务
4.1 安装 vLLM
激活虚拟环境后,直接安装 vLLM。
pip install --upgrade pip pip install vllm安装完成后,检查版本:
python -c "import vllm; print(vllm.__version__)"如果安装的是最新版本,它通常会依赖特定版本的 PyTorch 和 CUDA。可以先让 pip 自动解析依赖,遇到版本冲突再单独调整。
4.2 确认多卡可用性
启动 vLLM 前,先用 PyTorch 检查 GPU 是否可以正常访问:
python -c "import torch; print(torch.cuda.device_count()); print(torch.cuda.get_device_name(0))"如果打印出的设备数和 32 差距很大,说明部分卡没有被正确识别。排查方向一般是 PCIe 转接线是否插稳、供电是否充足、驱动是否识别完整。
4.3 启动 vLLM 推理服务
vLLM 提供vllm serve命令,可以一键启动一个 OpenAI 兼容的推理服务。下面以 Qwen2.5-7B-Instruct 为例启动:
vllm serve Qwen/Qwen2.5-7B-Instruct \ --tensor-parallel-size 8 \ --gpu-memory-utilization 0.9 \ --max-model-len 32768 \ --port 8000 \ --host 0.0.0.0参数说明:
--tensor-parallel-size:指定使用多少张卡并行切分模型。7B 模型其实用不了 32 卡,可以先从 1 卡起步,再逐步扩大到 4、8 卡。--gpu-memory-utilization:每个 GPU 最多使用的显存比例。0.9 表示预留 10% 显存给 CUDA context,避免 OOM。--max-model-len:最大上下文长度。显存越大,这个值可以调得越大,但也要看模型本身支持的窗口长度。--host 0.0.0.0:允许局域网内其他机器访问。如果只想本机测试,可以用127.0.0.1。
如果模型文件还没有下载,vLLM 启动时会在~/.cache/huggingface下自动下载模型。下载量可能在十几 GB 到几十 GB 不等,首次启动会比较慢。
国内网络环境下,如果模型下载困难,可以改用 ModelScope 下载模型后,再用本地路径启动:
vllm serve /data/models/Qwen2.5-7B-Instruct \ --tensor-parallel-size 8 \ --gpu-memory-utilization 0.9 \ --max-model-len 32768 \ --port 8000具体路径以你下载到的模型位置为准。
4.4 验证服务是否启动成功
启动日志中如果出现类似下面的信息,说明服务已经就绪:
INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000这时打开另一个终端,用 curl 访问:
curl http://127.0.0.1:8000/v1/models如果返回模型列表,说明推理服务已经正常启动,可以进行下一步测试。
5. 功能测试与效果验证
5.1 基础文本生成测试
先用最简单的请求验证模型能不能正常推理。vLLM 支持 OpenAI 兼容的/v1/chat/completions接口:
curl http://127.0.0.1:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "Qwen/Qwen2.5-7B-Instruct", "messages": [ {"role": "user", "content": "用一句话解释什么是 KV Cache"} ], "max_tokens": 256, "temperature": 0.7 }'判断标准:
- 返回 JSON 中有
choices[0].message.content。 - 生成内容没有乱码。
- 没有出现 timeout 或 connection refused。
- 如果能正常返回,说明模型加载、前向推理、采样流程都跑通了。
5.2 长文本与超长上下文测试
2TB 显存方案的重点之一,就是长上下文。可以用一段较长的输入文本测试模型的上下文处理能力:
curl http://127.0.0.1:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "Qwen/Qwen2.5-7B-Instruct", "messages": [ {"role": "user", "content": "请你阅读下面的长文本,然后总结核心观点。"} ], "max_tokens": 1024, "temperature": 0.2 }'测试时要重点观察两个地方:
- 服务端是否报
context length exceeded错误。如果报错,说明--max-model-len设置得比输入长度小。 nvidia-smi里显存占用是否在输入变长后明显上升。KV Cache 会随输入长度增加而增长。
5.3 多并发请求测试
vLLM 的核心优势是 PagedAttention 和 Continuous Batching,并发请求多时吞吐量仍然能保持稳定。可以用一个简单的 Python 脚本做并发测试:
import requests import concurrent.futures import time url = "http://127.0.0.1:8000/v1/chat/completions" payload = { "model": "Qwen/Qwen2.5-7B-Instruct", "messages": [ {"role": "user", "content": "讲一个关于程序员的笑话。"} ], "max_tokens": 128, "temperature": 0.8 } def call_one(i): try: resp = requests.post(url, json=payload, timeout=120) return i, resp.status_code, len(resp.text) except Exception as e: return i, -1, str(e) start = time.time() with concurrent.futures.ThreadPoolExecutor(max_workers=32) as executor: results = list(executor.map(call_one, range(64))) suc = [r for r in results if r[1] == 200] fail = [r for r in results if r[1] != 200] print(f"成功: {len(suc)}, 失败: {len(fail)}") print(f"总耗时: {time.time() - start:.2f}s")判断标准:
- 成功数量等于请求总数。
- 没有超时和连接错误。
- 总耗时时长在可接受范围内。
- 如果在高并发下大量请求超时,优先检查
--max-model-len是否偏大、显存是否被占满、模型服务日志是否有 OOM 报错。
5.4 批量任务测试
除了并发请求,还可以做离线批量任务。准备好一批输入文件,例如batch_input/目录下放多个.txt文件,然后用脚本逐个读取并调用 vLLM 接口:
import os import glob import requests import json input_dir = "./batch_input" output_dir = "./batch_output" os.makedirs(output_dir, exist_ok=True) url = "http://127.0.0.1:8000/v1/chat/completions" files = glob.glob(os.path.join(input_dir, "*.txt")) for idx, fp in enumerate(files): with open(fp, "r", encoding="utf-8") as f: text = f.read()[:2000] payload = { "model": "Qwen/Qwen2.5-7B-Instruct", "messages": [ {"role": "user", "content": f"请对以下内容做摘要:\n{text}"} ], "max_tokens": 512, "temperature": 0.3 } try: resp = requests.post(url, json=payload, timeout=180) data = resp.json() summary = data["choices"][0]["message"]["content"] out_path = os.path.join(output_dir, f"result_{idx}.json") with open(out_path, "w", encoding="utf-8") as f: json.dump({"input": fp, "output": summary}, f, ensure_ascii=False, indent=2) print(f"[OK] {fp} -> {out_path}") except Exception as e: print(f"[FAIL] {fp}: {e}")批量任务真正跑起来后,要关注的是任务队列和失败重试机制。生产环境中建议给每个任务编号,记录输入文件、请求时间、返回状态、输出路径,失败后自动重试指定次数。
6. 接口 API 与批量任务设计
6.1 OpenAI 兼容接口说明
vLLM 启动后默认提供以下接口:
GET /v1/models:查看当前加载的模型。POST /v1/chat/completions:对话补全接口。POST /v1/completions:文本补全接口。POST /v1/embeddings:部分模型支持 Embedding 接口,具体看模型和 vLLM 版本。
这套接口标准的好处是,原来接 OpenAI SDK 的代码,只需要改一下 base_url 就能切换到本地 vLLM 服务。
6.2 Python 调用示例
from openai import OpenAI client = OpenAI( base_url="http://127.0.0.1:8000/v1", api_key="EMPTY" ) resp = client.chat.completions.create( model="Qwen/Qwen2.5-7B-Instruct", messages=[ {"role": "user", "content": "介绍一下 vLLM 的特点"} ], max_tokens=512, temperature=0.6 ) print(resp.choices[0].message.content)如果安装过openai库,这个脚本可以直接运行。
6.3 批量任务架构建议
对于批量离线任务,不建议把几万个请求直接循环发送,容易给服务造成压力。更稳妥的方式是:
- 把待处理文本拆成若干小任务文件。
- 用少量 worker 并发请求 API。
- 每个任务记录状态:
pending、running、success、failed。 - 对失败任务做有限次重试。
- 所有输出写入独立目录,避免多线程写同一个文件导致覆盖。
batch_input/ task_001.txt task_002.txt batch_output/ result_001.json result_002.json logs/ task.log这样即使中途程序崩溃,也可以根据日志和结果目录恢复进度。
7. 资源占用与性能观察方法
7.1 显存占用怎么看
vLLM 启动后,可以持续用nvidia-smi观察每张卡的显存占用:
watch -n 1 nvidia-smi重点看两个值:
Memory-Usage:每张卡已经使用的显存。Volatile GPU-Util或Utilization:GPU 计算核心使用率。
如果多张卡显存分配不均,或者某些卡显存占用明显偏低,需要检查--tensor-parallel-size是否设置正确,以及 PCIe 通信是否成了瓶颈。
7.2 GPU 利用率与卡间通信
vLLM 在做 Tensor Parallel 推理时,每生成一个 token,跨卡通信都会拉高 PCIe 总线占用。CMP170HX 系列没有 NVLink,多卡通信完全通过 PCIe,这也是这套方案的主要性能瓶颈。观察方法:
nvidia-smi dmon -s pucvmetpcie相关字段可以看 PCIe 读写速率。如果多卡扩展后吞吐量没有线性提升,甚至下降,大概率是卡间通信带宽不够。
7.3 如何降低显存占用
如果显存不足或想塞下更多模型,可以考虑:
- 降低
--gpu-memory-utilization,从 0.9 降到 0.7。 - 减小
--max-model-len,例如从 32768 降到 16384。 - 使用量化模型,比如 AWQ、GPTQ 量化版模型。
- 使用
--enforce-eager关闭 CUDA Graph,但会降低推理性能,需要根据实际表现取舍。 - 对超大模型,用小 batch 并发,避免瞬时 KV Cache 暴涨。
7.4 如何避免端口冲突和进程残留
vLLM 默认监听 8000 端口。如果之前启动过服务,再次启动前先确认端口没有被占用:
ss -lntp | grep 8000如果端口被占用,可以换端口启动:
vllm serve Qwen/Qwen2.5-7B-Instruct --port 8001已经启动但想要停止的 vLLM 服务,找到 PID 后正常结束:
ps aux | grep vllm kill <PID>不要频繁用kill -9,可能导致缓存文件和进程残留。
8. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| nvidia-smi 看不到部分显卡 | 供电不足、PCIe 转接线松动、驱动未完整加载 | 检查 PCIe 物理连接,查看系统日志 | 重新插拔转接线,检查电源功率和模组线 |
| vLLM 启动报 CUDA out of memory | 显存被其他进程占用,或模型加载所需显存超过单卡容量 | nvidia-smi 检查空闲显存 | 关闭其他进程,调整 gpu-memory-utilization,使用量化模型 |
| 模型下载速度慢或失败 | 网络问题或模型仓库连接超时 | 检查网络,确认模型名称拼写 | 使用国内模型源下载后改为本地路径启动 |
| 请求返回 context length exceeded | 输入长度超过 max-model-len | 检查输入文本长度 | 调大 --max-model-len,或截断输入文本 |
| 并发请求超时 | 显存不足、模型过大、并发数过高 | 查看服务日志和显存状态 | 降低并发数,减小 max-model-len,增加 batch 调度参数 |
| 多卡扩展后性能不升反降 | 卡间通信带宽不足 | 用性能监控查看 PCIe 读写 | 减少 tensor-parallel-size,优先用 4 卡或 8 卡并行 |
| Python 依赖安装冲突 | vLLM 和 PyTorch 版本不匹配 | 查看 pip 报错信息 | 单独创建虚拟环境,按 vLLM 官方要求安装对应版本 |
| 调用 OpenAI SDK 报 404 | base_url 配置错误 | 检查接口路径 | 确保 base_url 以/v1结尾 |
这些问题是多卡 vLLM 部署中最常见的几类。遇到问题时,先看日志,再查硬件,不要直接重装系统。
9. 最佳实践与使用建议
9.1 先单卡验证,再逐步扩展
第一次部署不要直接上 32 卡。先用--tensor-parallel-size 1跑通 7B 模型,确认驱动、依赖、接口都没问题后,再逐步扩展到 4 卡、8 卡。这样定位问题会容易很多。
9.2 保留一套最小可运行配置
把验证过的关键命令和配置保存下来,最好是写成脚本。例如:
# start_vllm.sh #!/bin/bash source /opt/vllm_env/bin/activate vllm serve Qwen/Qwen2.5-7B-Instruct \ --tensor-parallel-size 8 \ --gpu-memory-utilization 0.9 \ --max-model-len 32768 \ --port 8000以后重启服务,直接执行脚本,避免记错参数。
9.3 输入、模型、输出分目录管理
模型文件、输入素材、输出结果不要混在一起。推荐结构:
/home/user/vllm-lab/ models/ inputs/ outputs/ logs/ scripts/模型文件用models/单独存放,批量任务输入放inputs/,生成结果放outputs/,这样不会边跑边找文件。
9.4 批量任务要加日志和失败重试
批量任务一旦跑起来,大概率会碰到网络抖动、模型超时、显存暂时不足等问题。日志和重试机制不是可选项,而是必选项。每个任务记录状态,失败重试最多 3 次,重试间隔可以指数退避。
9.5 接口服务要限制访问范围
虽然 vLLM 本身没有复杂的鉴权体系,但暴露在局域网或公网时要非常小心。建议:
- 只在可信内网访问,不要直接绑定
0.0.0.0暴露公网。 - 如果需要对外开放,放在 Nginx 等反向代理后面,并增加访问控制。
- 不要用默认的
api_key=EMPTY对外提供服务。
9.6 合规与授权提醒
涉及模型权重下载、数据集使用、生成内容对外发布时,必须确认:
- 模型许可证是否允许商用或二次分发。
- 输入数据是否包含个人隐私或敏感信息。
- 生成内容是否可能涉及版权、肖像权、名誉权。
- 如果是二手或来源不明硬件,要确认硬件来源合法。
这套方案从硬件到模型全部在测试环境中验证,不要用于任何违规用途。
10. 总结与下一步
CMP170HX 多卡改造方案的核心价值,是用低成本换大显存,给 vLLM 大模型推理提供了非常广阔的试验场。2TB 显存意味着你可以同时常驻多个大模型,也可以用超大上下文做文档分析和批量推理。这个方向最适合对硬件改造和命令行部署都有经验的开发者。
最开始验证时,建议先跑通 7B 模型单卡,然后扩展到 8 卡测试tensor-parallel-size的效果,最后再上 32 卡满配并观察卡间通信和显存分配,整个过程按“软件先行、硬件扩展”的顺序推进。最容易踩的坑集中在三个地方:供电和转接导致部分卡掉线、PCIe 通信带宽撑不起大规模并行、以及模型下载源不稳定。这三个问题只要在规划阶段提前准备好,后面的部署会顺畅很多。
下一步可以继续折腾的方向包括:测试 AWQ/GPTQ 量化模型在这个显存规模下的并发能力、在同一套机器上部署多个不同尺寸的模型、把批量任务改成完整的任务队列并接入异步处理框架,或者把接口服务接到自己的业务系统里做成一个家庭级 AI 推理网关。这套方案的实验属性很强,但能跑通的东西确实不少,建议收藏备用。