构建内容
一个 GPU 实例,通过 HTTP API 提供开放权重模型,该 API 兼容 OpenAI 模式,因此你已有的所有客户端库只需更改一个 base URL 即可使用。在 nginx 后面,带有 TLS、API 密钥和一个重启后仍可用的 systemd 单元。
在 G-L40S 上编写:单卡四十八 GB VRAM,通过 PCIe 传递给实例,而不是在租户之间进行时间切片。这种区别在于可预测的延迟与在别人的训练任务后面等待之间的差异。这张卡在你取消之前都归你。
开始之前
- 使用 Debian 13 的 G-L40S。二十 GB 的 G-ADA 适用于较小的模型;第四步中的算术告诉你哪些模型适用。
- 此方案有两 TB NVMe,适合存放权重。因为缓存位于根文件系统而下载四十 GB 两次,是一种浪费一小时的方式。
- 指向实例的主机名,如下面的
llm.example.com。
1. 驱动
sed -i "s/ main$/ main contrib non-free-firmware non-free/" /etc/apt/sources.list.d/debian.sources
apt update
apt install -y nvidia-driver firmware-misc-nonfree build-essential
reboot返回之后:
nvidia-smi你应该看到列出的卡、打印的驱动版本,以及接近零的内存使用率。此时缺少卡几乎总是 initramfs 过时的原因;update-initramfs -u 并再次重启,然后再开票。
nvidia-smi -q -d PERFORMANCE | grep -A3 "Clocks Event Reasons"
nvidia-smi -pm 1持久模式保持驱动在进程之间加载,这消除了每次重启时的几秒初始化时间。
2. 运行时
apt install -y python3-venv python3-pip
install -d -o root -g root /srv/models /opt/vllm
python3 -m venv /opt/vllm/venv
/opt/vllm/venv/bin/pip install --upgrade pip
/opt/vllm/venv/bin/pip install vllm安装会拉取大量 CUDA 链接的 wheel 集,需要一段时间。同时,决定一个模型。
3. 选择适合的模型
权重是底线,而不是预算。十六位精度的模型每个十亿参数大约需要两 GB VRAM,加上每个并发请求的键值缓存,以及大约一 GB 的运行时开销。
| 卡 | 16 位权重 | 实际模型大小 | 缓存剩余 |
|---|---|---|---|
| L40S,48 GB | 每个十亿 2 GB | 最多约 200 亿 | 6 到 8 GB |
| L40S,48 GB,8 位 | 每个十亿 1 GB | 最多约 340 亿 | 10 GB |
| RTX 4000 Ada,20 GB | 每个十亿 2 GB | 最多约 70 亿 | 5 GB |
| 两块 × L40S,96 GB | 每个十亿 2 GB | 最多约 400 亿 | 12 GB |
缓存列决定了一次有多少人可以使用端点。把卡塞满权重,你就构建了一个非常快的单用户玩具。
4. 提供服务
将缓存指向 NVMe 并启动服务器。替换你选择的任何开放权重模型的仓库标识符;运行时在首次启动时获取它。
cat > /etc/systemd/system/vllm.service <<EOF
[Unit]
Description=vLLM inference server
After=network-online.target
[Service]
Environment=HF_HOME=/srv/models
Environment=VLLM_API_KEY=<a long random string>
ExecStart=/opt/vllm/venv/bin/vllm serve <org>/<model> \\
--host 127.0.0.1 --port 8000 \\
--served-model-name local \\
--max-model-len 16384 \\
--gpu-memory-utilization 0.92 \\
--max-num-seqs 32
Restart=on-failure
RestartSec=10
TimeoutStartSec=1800
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable --now vllm
journalctl -fu vllm绑定到回环是故意的。运行时中的 API 密钥检查是一个单一共享密钥,没有速率限制,因此 nginx 负责暴露,运行时永远不接触公共接口。
宽裕的启动超时是因为首次启动会下载数十 GB 并编译内核。后续启动不到一分钟。
5. 代理和 TLS
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name llm.example.com;
ssl_certificate /etc/letsencrypt/live/llm.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/llm.example.com/privkey.pem;
location /v1/ {
proxy_pass http://127.0.0.1:8000;
proxy_buffering off;
proxy_read_timeout 600s;
proxy_set_header Host $host;
}
}proxy_buffering off 是使流式响应流动的配置。保持开启,令牌会以整齐的批次到达,每当 nginx 感觉缓冲区已满时,这看起来就像模型很慢,但其实不然。
验证
从卡开始:
nvidia-smi --query-gpu=memory.used,memory.total,utilization.gpu --format=csv使用的内存应约为总内存的百分之九十,因为那是 --gpu-memory-utilization 所要求的。然后端点:
curl -s https://llm.example.com/v1/models -H "Authorization: Bearer <the key>" | head -20一个模型,名为 local。现在进行真实的补全,并计时:
time curl -s https://llm.example.com/v1/chat/completions \
-H "Authorization: Bearer <the key>" \
-H "Content-Type: application/json" \
-d '{"model":"local","messages":[{"role":"user","content":"List three prime numbers."}],"max_tokens":64}'最后,在负载下測量,而不是一次一个请求,那才是唯一值得引用的数字:
/opt/vllm/venv/bin/vllm bench serve \
--backend openai-chat \
--base-url https://llm.example.com \
--endpoint /v1/chat/completions \
--model local --num-prompts 200 --request-rate 8从输出中读取三个数字:聚合输出令牌/秒、中位首个令牌时间,以及端到端第 99 百分位。在单个 L40S 上,模型大小在 100 亿到 200 亿之间,聚合输出吞吐量为每秒数百个令牌,首个令牌时间远低于一秒是预期的形状。首个令牌时间随并发增加而升高,意味着你的键值缓存太小,所以降低 --max-model-len 或 --max-num-seqs 并重新运行。
之后
使用 nvidia-smi dmon 在第一周监测温度和时钟限流,因为热限流的卡在持续负载下会产生间歇性缓慢,这会被归咎于网络。在账单方面,记住 GPU 实例的费用相同,无论卡是忙还是空闲,所以批量处理工作而不是让它保活。定价在 GPU 页面。