PVE GPU 透传实战:4 卡 RTX 3090 与 vLLM 推理
多 GPU 服务器上做 PCI Passthrough,和单卡消费级桌面是完全不同的工程。IOMMU 分组不可控、无板载显卡的管理面缺失、PCIe 拓扑对 P2P 通信的影响、长时间负载下的热稳定性,这些问题在网上大量的“透传教程”里几乎不会被提到,但也恰恰是生产环境中最容易翻车的地方。
本文以一台 4 卡 RTX 3090 的无头机为主,记录 PVE 下 GPU 透传的配置流程、踩坑细节和实测数据(CUDA 带宽、PyTorch 训练、vLLM 推理)。另有一台 8 卡 V100 的数据中心级平台,配置差异会一并说明;V100 的性能数据尚未采集,文中不编造数字,相关部分只保留方法。
硬件平台与架构决策
平台规格
| 项目 | 平台 A:4卡 RTX 3090 | 平台 B:8卡 V100 |
|---|---|---|
| 宿主机 IP | 192.168.1.2 | 192.168.1.3 |
| GPU | NVIDIA RTX 3090 x4 | NVIDIA V100 (SXM2/PCIe) x8 |
| CPU | AMD Ryzen Threadripper 3960X (24C/48T) | Dual Intel Xeon Gold 6138 @ 2.00GHz (40C/80T) |
| 内存 | 128 GB (32GB x4) DDR4 | 256 GB ECC DDR4 |
| 主板/机架 | ASRock TRX40 Creator | 浪潮 Inspur NF5468M5 |
| 板载显卡 | 无 | 带有独立 AST2500 BMC |
| 管理方式 | 纯 IP / SSH | IPMI 带外管理 |
网络拓扑与 IP 规划
为了让 GPU 推理层、LLM 网关、OpenClaw Agent 以及监控栈能够安全高效地协作,我们首先对物理机和虚拟机进行网段规划。所有节点通过 PVE 的虚拟网桥(如 vmbr0)在二层互通:
| 节点角色 | IP 地址 | 宿主机位置 | 核心组件 |
|---|---|---|---|
| PVE 宿主机 1 | 192.168.1.2 |
物理机 A | PVE 系统、PCIe 直通管理 |
| PVE 宿主机 2 | 192.168.1.3 |
物理机 B | PVE 系统、NVLink / PCIe 直通管理 |
| 3090 推理 VM | 192.168.1.20 |
PVE 1 (平台 A) | vLLM 实例(挂载 4x 3090) |
| V100 推理 VM | 192.168.1.21 |
PVE 2 (平台 B) | vLLM 实例(挂载 8x V100) |
| LLM 路由网关 | 192.168.1.12 |
PVE 1 或 2 | LiteLLM (统一 API 网关)、鉴权与限流 |
| OpenClaw VM | 192.168.1.11 |
PVE 1 或 2 | OpenClaw Gateway、Agent、Docker Sandbox |
| 自动化工作流 | 192.168.1.13 |
PVE 1 或 2 | n8n (通过 Agent 触发) |
| LLM Tracing | 192.168.1.14 |
PVE 1 或 2 | Langfuse(LLM 链路审计与观测) |
| 全局监控节点 | 192.168.1.30 |
PVE 1 或 2 | Prometheus、Grafana |
整个 Homelab 的多模型 AI Agent 架构与网络拓扑如下:
graph TB
subgraph Management["管理平面 (192.168.1.x)"]
PROM["全局监控节点<br/>192.168.1.30<br/>Prometheus/Grafana"]
LF["LLM Tracing 节点<br/>192.168.1.14<br/>Langfuse"]
PVE1["PVE 宿主机 1<br/>192.168.1.2<br/>(4x RTX 3090)"]
PVE2["PVE 宿主机 2<br/>192.168.1.3<br/>(8x V100)"]
end
subgraph Application["应用平面 (192.168.1.x)"]
OC["OpenClaw Agent<br/>192.168.1.11"]
N8N["n8n 工作流<br/>192.168.1.13"]
end
subgraph Gateway["网关平面"]
LITE["LLM 路由网关<br/>192.168.1.12<br/>LiteLLM Proxy"]
end
subgraph Compute["GPU 推理层"]
VM3090["3090 推理 VM<br/>192.168.1.20<br/>vLLM"]
VMV100["V100 推理 VM<br/>192.168.1.21<br/>vLLM"]
end
%% 业务流
OC -->|"HTTPS+Key"| LITE
N8N -->|"HTTPS+Key"| LITE
LITE -->|"localhost(回环) / 直连"| VM3090
LITE -->|"直连"| VMV100
%% 审计和监控流
LITE -.->|"异步 tracing"| LF
PROM -.->|"拉取指标"| VM3090
PROM -.->|"拉取指标"| VMV100
PROM -.->|"拉取指标"| PVE1
PROM -.->|"拉取指标"| PVE2
classDef vm fill:#e3f2fd,stroke:#1565c0,stroke-width:2px;
classDef host fill:#f5f5f5,stroke:#424242,stroke-width:2px;
classDef gw fill:#fff3e0,stroke:#e65100,stroke-width:2px;
class OC,N8N,VM3090,VMV100,LF vm;
class LITE,PROM gw;
class PVE1,PVE2 host;
架构注记:
- LLM 路由网关 (
1.12) 是所有流量的汇聚点。无论是 OpenClaw (1.11) 还是 n8n (1.13),都禁止直连底层的推理 VM (1.20和1.21)。- 只有 LLM 路由网关拥有底层推理 VM 的访问权限(端口
8001-8003等),从而将安全边界收拢到网关一层。
平台差异与透传策略
两个平台的差异不只是卡的型号,而是体现在整个 I/O 架构上:
平台 A(4 卡 3090,ASRock TRX40) 的核心挑战是无板载显卡。这不是一个小问题——它直接决定了管理面的可用性。如果 4 张 GPU 全部 VFIO 绑定,宿主机将没有任何帧缓冲设备,GRUB 引导画面都不会有。
本文针对平台 A 采用的方案是:纯 SSH + PVE Web UI 管理。PVE 管理面本身不依赖 GPU。只要网络通,Web UI 照常可用。一旦配置出错导致断网,唯一的退路是接上硬盘离线修改网络配置,或者在主板上临时插一张亮机卡。
平台 B(8 卡 V100,浪潮 NF5468M5) 是标准机架式 AI 服务器,带有独立的 BMC/IPMI 管理口,所以无图形界面管理不是问题。但它的复杂度在于 NUMA 节点绑定和 PCIe 拓扑。双路 Intel Xeon Gold 设计意味着 8 张卡实际上是挂载在不同的 CPU Socket 上的。如果虚拟机跨 NUMA 节点调度,或者直通了不属于同一个 CPU 的网卡和 GPU,会导致极大的 QPI/UPI 互连延迟惩罚。
BIOS 配置
BIOS 配置是 GPU 透传的地基。漏一项可能就是启动黑屏或者 BAR 分配失败。以下是必须确认的项:
| BIOS 选项 | 要求 | 备注 |
|---|---|---|
| VT-d / AMD-Vi | 启用 | IOMMU 的基础,不开则直接不可用 |
| SR-IOV | 建议启用 | 为后续 vGPU 方案预留 |
| Above 4G Decoding | 必须启用 | 多卡必备。3090 单卡 BAR1 就 256MB,4 卡的 BAR 空间远超 4GB 寻址上限 |
| Resizable BAR (ReBAR) | 视情况关闭 | 部分固件/QEMU 版本下 ReBAR 会导致 VM 内 GPU 初始化失败(Code 43) |
| CSM | 关闭 | OVMF 需要纯 UEFI 环境,CSM 兼容模式会引入不可预期的问题 |
经验之谈:Above 4G Decoding 是多卡透传中最常被忽略的选项。症状是虚拟机内
nvidia-smi输出Unable to determine the device handle for GPU,dmesg 里有BAR 1: can't assign错误。
PVE 宿主机配置
IOMMU 与内核参数
编辑 /etc/default/grub,在 GRUB_CMDLINE_LINUX_DEFAULT 中添加:
Intel 平台:
GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt pcie_acs_override=downstream,multifunction video=efifb:off"
AMD 平台:
# AMD 平台用 amd_iommu=on,Intel 平台换成 intel_iommu=on
GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt pcie_acs_override=downstream,multifunction video=efifb:off"
逐项说明:
| 参数 | 作用 | 为什么需要 |
|---|---|---|
intel_iommu=on / amd_iommu=on |
启用 IOMMU | 透传的前提条件 |
iommu=pt |
Pass-Through 模式 | 非透传设备走 DMA 直通路径,避免不必要的 IOMMU 翻译开销 |
pcie_acs_override=downstream,multifunction |
强制拆分 IOMMU 分组 | 多卡服务器上极大概率需要——PCIe Switch 下挂的多张 GPU 往往被分到同一个 IOMMU Group |
video=efifb:off |
关闭 EFI 帧缓冲 | 阻止宿主机内核绑定 GPU 的帧缓冲设备,确保 VFIO 能干净地接管 |
update-grub && reboot
重启后确认:
dmesg | grep -e DMAR -e IOMMU
# 应出现 "DMAR: IOMMU enabled" 或 "AMD-Vi: Initialized"
VFIO 模块加载
编辑 /etc/modules:
vfio
vfio_iommu_type1
vfio_pci
Linux 6.2 之后
vfio_virqfd已经被合并进 VFIO,不用再单独加载。旧教程里如果还有这行,删掉即可。
IOMMU 分组——决定透传可行性的关键
IOMMU 分组直接决定了透传的可行性和颗粒度。组内设备要么全部透传,要么全留给宿主机。
#!/bin/bash
shopt -s nullglob
for g in $(find /sys/kernel/iommu_groups/* -maxdepth 0 -type d | sort -V); do
echo "IOMMU Group ${g##*/}:"
for d in $g/devices/*; do
echo -e "\t$(lspci -nns ${d##*/})"
done;
done;
平台 A(4卡 RTX 3090)
实际分组输出因主板和 BIOS 而异,这里不贴固定结果。运行上面的脚本后,重点看两件事:
- 每张 3090 是否和它的 HDMI Audio Controller 在同一个 Group。透传时要把显示和音频两个 function 一起给虚拟机。
- 多张卡是否被合并到同一组。这在多路 PCIe Switch 拓扑中很常见,必要时只能靠
pcie_acs_override拆组。拆组会绕过 ACS(Access Control Services)的隔离作用,安全敏感场景要评估风险。
平台 B(8卡 V100)
V100 SXM2 的 PCIe 拓扑比消费级平台复杂得多,NVLink Bridge 设备和 PLX Switch 都可能出现在 IOMMU 分组中。需要重点确认:分组是否允许独立透传单张 GPU,以及 NVLink 互联设备有没有被一起分组。
VFIO-PCI 绑定
lspci -nn | grep -i nvidia
输出示例(实际 bus 地址按你的机器替换):
41:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA102 [GeForce RTX 3090] [10de:2204] (rev a1)
41:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio Controller [10de:1aef] (rev a1)
绑定 VFIO:
echo "options vfio-pci ids=10de:2204,10de:1aef disable_vga=1" > /etc/modprobe.d/vfio.conf
disable_vga=1是关键。没有这个选项,VFIO 在绑定 VGA class 设备时会尝试做 VGA arbitration,在无板载显卡的平台上直接导致绑定失败或内核 hang。
屏蔽宿主机 NVIDIA 驱动
cat >> /etc/modprobe.d/blacklist.conf << 'EOF'
blacklist nouveau
blacklist nvidia
blacklist nvidiafb
blacklist nvidia_drm
EOF
update-initramfs -u -k all && reboot
重启后确认绑定状态:
lspci -nnk -s 41:00
# Kernel driver in use: vfio-pci
如果看到 Kernel driver in use: nouveau 或 nvidia,说明 blacklist 没生效——检查 initramfs 是否正确更新,或检查是否有其他 modprobe 配置文件在加载 NVIDIA 模块。
虚拟机配置
关键参数
| 配置项 | 推荐值 | 原因 |
|---|---|---|
| 机器类型 | q35 |
i440fx 的 PCIe 模拟有限,q35 提供完整的 PCIe 拓扑支持 |
| BIOS | OVMF (UEFI) |
NVIDIA GPU 的 VBIOS 需要 UEFI 环境初始化 |
| CPU 类型 | host |
暴露真实 CPU 特性(如 AVX-512),避免透传后 GPU 驱动因 CPU feature 缺失报错 |
| 内存 | 固定分配,禁用 ballooning | 内存气球和 VFIO 设备的 DMA 映射冲突,开 ballooning 大概率导致 VM crash |
PCI 设备透传
直接编辑 /etc/pve/qemu-server/<VMID>.conf:
# 单卡透传
hostpci0: 0000:41:00,pcie=1,x-vga=1
# 4 卡 RTX 3090 全部透传至同一 VM
hostpci0: 0000:41:00,pcie=1,x-vga=1
hostpci1: 0000:61:00,pcie=1
hostpci2: 0000:81:00,pcie=1
hostpci3: 0000:a1:00,pcie=1
x-vga=1只在主 GPU 上设置。它通知 QEMU 将该设备的 VGA 仲裁权交给虚拟机。多卡场景下,只有hostpci0需要这个标志。
Web UI 中添加 PCI 设备时需要注意:勾选 All Functions(同时透传GPU和关联的 Audio Controller)、ROM-Bar、PCI-Express。Primary GPU 仅对需要 VGA 输出的主卡勾选。
纯净 Debian 13 (Trixie) 基础镜像构建
为了保证推理节点绝对干净且没有冗余服务的开销,我们在 PVE VM 中放弃常规 ISO 安装,转而使用 debootstrap 手工构建极简版 Debian 13 工作环境。你可以使用任何 Live CD(比如 SystemRescue)引导 VM。
1. 磁盘分区与挂载
# 假设目标磁盘为 /dev/sda,使用 fdisk 分区:
# sda1: EFI (FAT32)
# sda2: Boot (Ext4)
# sda3: Root (Ext4)
apt install -y dosfstools cryptsetup efibootmgr debootstrap
mkfs.fat -F32 /dev/sda1
mkfs.ext4 /dev/sda2
mkfs.ext4 /dev/sda3
# 挂载目录结构
mkdir -pv /mnt/debinst
mount /dev/sda3 /mnt/debinst/
mkdir -pv /mnt/debinst/boot
mount /dev/sda2 /mnt/debinst/boot/
mkdir -pv /mnt/debinst/boot/efi
mount /dev/sda1 /mnt/debinst/boot/efi/
2. 系统核心拉取与 Chroot 进入
加速拉取 Debian 13 (Trixie) 的 amd64 基础包:
debootstrap --arch amd64 trixie /mnt/debinst https://mirrors.tuna.tsinghua.edu.cn/debian/
# 绑定必要的核心文件系统
mount -v -t proc none /mnt/debinst/proc
mount -v --rbind /sys /mnt/debinst/sys
mount -v --rbind /dev /mnt/debinst/dev
mount -v --make-rslave /mnt/debinst/sys
mount -v --make-rslave /mnt/debinst/dev
# 切换到真实的 VM 环境
LANG=C.UTF-8 chroot /mnt/debinst /bin/bash
3. Chroot 内环境初始化
安装基础工具、配置挂载表及网络拓扑(以 V100 节点 192.168.1.21 为例):
apt update
apt install -y apt-transport-https ca-certificates aptitude vim dosfstools efibootmgr lvm2
# 填入刚装好的系统分区 UUID(可通过 blkid 获取)
cat <<EOF > /etc/fstab
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx / ext4 defaults,relatime 0 1
UUID=xxxx-xxxx /boot/efi vfat defaults 0 1
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /boot ext4 defaults,relatime 0 1
EOF
# 主机名与 DNS
echo v100-cluster > /etc/hostname
echo nameserver 114.114.114.114 > /etc/resolv.conf
# 静态网络配置
cat <<EOF > /etc/network/interfaces
auto ens18
iface ens18 inet static
address 192.168.1.21
network 192.168.1.0
netmask 255.255.255.0
broadcast 192.168.1.255
gateway 192.168.1.1
EOF
更新清华 Trixie Apts 源并配置本地化:
cat <<EOF > /etc/apt/sources.list
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ trixie main contrib non-free non-free-firmware
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ trixie-updates main contrib non-free non-free-firmware
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ trixie-backports main contrib non-free non-free-firmware
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security trixie-security main contrib non-free non-free-firmware
EOF
aptitude update
aptitude install locales tzdata
dpkg-reconfigure locales
dpkg-reconfigure tzdata
4. 内核、GRUB 与 SSH 收尾
apt-get install linux-image-amd64
# 必须安装 EFI 引导程序
aptitude install grub-efi
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Proxmox
grub-mkconfig -o /boot/grub/grub.cfg
# 开启 root用户的 SSH 登录,对于无头服务器环境非常重要
apt-get install ssh -y
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
passwd
exit
umount -R /mnt/debinst
reboot
虚拟机侧驱动编译与环境基准验证
重建并重启进入刚搭建好的 Trixie 系统,首要任务是编译加载 NVIDIA 闭源驱动,并跑通 PyTorch 测试,确认底层透传完全可用。
1. DKMS 安装 NVIDIA 驱动
通过 DKMS 手动编译驱动模块,在系统内核更新时具备更好的兼容性和可控性:
apt update
apt-get install -y pciutils linux-headers-$(uname -r) build-essential dkms
# 安装闭源驱动
apt install -y nvidia-driver
dpkg-reconfigure nvidia-kernel-dkms
modprobe nvidia
# 验证透传状态
nvidia-smi
2. PyTorch GPU 验证环境
apt install -y python3-venv python3-pip
python3 -m venv gpu_test_env
source gpu_test_env/bin/activate
# 镜像加速
python -m pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple --upgrade pip
pip config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
# 只安装核心计算库
pip install torch torchvision torchaudio
# 测试 GPU 是否被 PyTorch 识别
python -c "import torch; print(f'CUDA 核心可用: {torch.cuda.is_available()}'); print(f'GPU 数量: {torch.cuda.device_count()}')"
3. 多卡拓扑验证——这一步不可跳过
测试不仅要认出卡,还要认出它们是怎么连通的:
平台 A:4x RTX 3090
nvidia-smi topo -m
GPU0 GPU1 GPU2 GPU3 CPU Affinity NUMA Affinity GPU NUMA ID
GPU0 X PHB PHB PHB 0-31 0 N/A
GPU1 PHB X PHB PHB 0-31 0 N/A
GPU2 PHB PHB X PHB 0-31 0 N/A
GPU3 PHB PHB PHB X 0-31 0 N/A
...
解析:由于主板和 CPU PCIe 控制器的调度,RTX 3090 的互联关系是
PHB(通过 CPU 穿越 PCIe 主桥),没有 NVLink 支持。
平台 B:8x V100-SXM2
nvidia-smi topo -m
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 CPU Affinity NUMA Affinity GPU NUMA ID
GPU0 X NV2 NV2 NV1 PHB PHB PHB NV1 0-39 0 N/A
GPU1 NV2 X NV1 NV2 PHB PHB NV1 PHB 0-39 0 N/A
...
nvidia-smi nvlink -s
GPU 0: Tesla V100-SXM2-32GB (UUID: GPU-4a303885-0edf-7219-d89d-f863a9e11366)
Link 0: 25.781 GB/s
Link 1: 25.781 GB/s
...
解析:V100 集群中的
NV1和NV2标志以及 NVLink25.781 GB/s的链路状态说明 PVE 对 SXM2 架构底板的 PCIe 拓扑和 NVSwitch 直通支持极佳。nvidia-smi topo -m的输出决定了多卡训练的通信效率。在透传场景下,如果这里降级成了 SYS 或 PIX,会对分布式训练的 AllReduce 性能产生毁灭性打击。
性能基准测试
测试方法论
工具矩阵
| 工具 | 用途 | 版本 |
|---|---|---|
nvbandwidth |
PCIe 带宽、P2P 通信 | v0.8 (Release) |
gpu-burn |
计算正确性 + 热稳定性 | 1.1.0 (Commit: 8a24c…) |
PyTorch |
DDP 训练吞吐量 | 2.4.1+cu121 |
vLLM |
高吞吐 LLM 推理 | 最新稳定版(按模型支持选版本) |
Prometheus + Grafana + dcgm-exporter |
GPU 全链路可观测性 | v2.54 / v11.2 / 3.3.8 (debian12) |
PCIe 带宽测试
由于 P2P 通信和底层硬件带宽是评估透传损耗的核心,这里我们将使用 NVIDIA 官方最新的现代测量工具:nvbandwidth(已全面替代旧版的 cuda-samples 测速库)。
apt install -y git cmake libboost-program-options-dev nvidia-cuda-toolkit build-essential
# 编译最新的 NVIDIA NVBandwidth 工具
git clone https://github.com/NVIDIA/nvbandwidth.git
cd nvbandwidth
cmake . && make
# Host-to-Device / Device-to-Host 带宽
./nvbandwidth -t host_to_device_memcpy_ce
./nvbandwidth -t device_to_host_memcpy_ce
# GPU 间 P2P 带宽 (Device-to-Device)
./nvbandwidth -t device_to_device_memcpy_ce
nvbandwidth 默认已包含最优大小的拷贝策略,从物理层面确保测量结果准确反映透传后 PCIe 通道本身的能力。
H2D / D2H 带宽:
注:平台 A 由于主板通道划分原因,4 张 RTX 3090 的 PCIe 通道分布不均(GPU 0/1 运行在 PCIe 4.0 x16,GPU 2/3 运行在 x8)。
| 测试项 | RTX 3090 (透传) |
|---|---|
| H2D (GB/s) | ~25.4 (x16) / 13.4 (x8) |
| D2H (GB/s) | ~25.2 (x16) / 13.2 (x8) |
P2P 多卡通信:
P2P 用 ./nvbandwidth -t device_to_device_memcpy_ce 测。本文没有保留 4 卡 3090 的 P2P 数字,这里不列具体值;判断方法是看带宽有没有断崖式下降。QEMU 暴露给 VM 的 PCIe 拓扑是虚拟的,GPU 之间的 P2P 路径可能被截断,退化成 Host 中转。如果 P2P 带宽明显受限,大概率是 IOMMU 的 ACS 策略阻断了 peer-to-peer 的 DMA 路径。
GPU 透传信息采集验证
在运行高性能计算前,必须确认透传卡的状态。
nvidia-smi -q | grep -E "Product Name|GPU UUID|BAR1|Memory"
平台 A:4x RTX 3090
Product Name : NVIDIA GeForce RTX 3090
GPU UUID : GPU-1dc002ea-dc6e-5806-d798-43de15b92028
FB Memory Usage
BAR1 Memory Usage
Conf Compute Protected Memory Usage
Memory : 0 %
Memory Current Temp : N/A
Memory Max Operating Temp : N/A
GPU Memory Power Readings
Memory : 405 MHz
Memory : N/A
Memory : N/A
Memory : N/A
Memory : 9751 MHz
Product Name : NVIDIA GeForce RTX 3090
GPU UUID : GPU-5b7a34da-4cbe-86e8-9607-e6d2b336b944
FB Memory Usage
BAR1 Memory Usage
... (其余 3 张卡同上)
平台 B:8x V100-SXM2-32GB
Product Name : Tesla V100-SXM2-32GB
GPU UUID : GPU-4a303885-0edf-7219-d89d-f863a9e11366
FB Memory Usage
BAR1 Memory Usage
Conf Compute Protected Memory Usage
Memory : 0 %
Device Memory : 0
Texture Memory : N/A
Device Memory : 0
Texture Memory : N/A
...
Memory Current Temp : 37 C
Memory Max Operating Temp : 85 C
GPU Memory Power Readings
Memory : 877 MHz
...
差异分析:透传环境下,虽然两边的卡都正常识别了,但你可以明显察觉到消费级卡和数据中心卡的监控能力差异。RTX 3090 的 VBIOS 未针对企业级环境暴露 Memory Current Temp 等多项核心传感数据(显示为 N/A),而企业级的 V100 即便在虚拟化环境下,其 ECC 显存、纹理缓存状态以及确切的工作温度限制全部完好挂载传递。这在长时间的高压烤机中是极度关键的监控指标。
AI 训练性能测试
准备基准测试脚本 (train_benchmark.py)
在执行训练基准测试前,必须在虚拟环境中准备一个极简版的 PyTorch DDP 吞吐量压测脚本(将张量加载进 GPU 来摸平最大算力):
# train_benchmark.py
import torch, time, argparse, os
import torch.nn as nn
import torch.optim as optim
import torchvision.models as models
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--model', default='resnet50')
parser.add_argument('--batch-size', type=int, default=128)
parser.add_argument('--epochs', type=int, default=5)
parser.add_argument('--num-gpus', type=int, default=1)
parser.add_argument('--local_rank', type=int, default=-1)
args = parser.parse_args()
local_rank = int(os.environ.get("LOCAL_RANK", args.local_rank))
is_ddp = local_rank != -1
if is_ddp:
torch.distributed.init_process_group(backend='nccl')
torch.cuda.set_device(local_rank)
device = torch.device(f"cuda:{local_rank}")
else:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = models.resnet50().to(device)
if is_ddp:
model = nn.parallel.DistributedDataParallel(model, device_ids=[local_rank])
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
inputs = torch.randn(args.batch_size, 3, 224, 224).to(device)
targets = torch.randint(0, 1000, (args.batch_size,)).to(device)
for epoch in range(args.epochs):
torch.cuda.synchronize()
start = time.time()
for _ in range(50):
optimizer.zero_grad()
loss = criterion(model(inputs), targets)
loss.backward()
optimizer.step()
torch.cuda.synchronize()
if local_rank in [-1, 0]:
throughput = (50 * args.batch_size * (args.num_gpus if is_ddp else 1)) / (time.time() - start)
print(f"Epoch {epoch} Throughput: {throughput:.2f} images/sec")
if __name__ == '__main__':
main()
单卡:ResNet-50
python train_benchmark.py --model resnet50 --batch-size 128 --epochs 5 --num-gpus 1
参数说明:
| 参数 | 说明 |
|---|---|
--model resnet50 |
使用 torchvision.models.resnet50 作为基准模型,ResNet-50 是业界最通用的训练吞吐量评测标准 |
--batch-size 128 |
每次迭代喝入 128 张 224×224 图片,对于 24 GB 显存的 RTX 3090 可以安全填满;如果显存报 OOM,可降为 64 |
--epochs 5 |
跑 5 轮,每轮 50 次前向 + 反向传播,前 1-2 轮为预热期,后 3 轮的均值为有效吞吐量 |
--num-gpus 1 |
单卡模式,脑本不启用 DDP,纯粹测量单卡算力上限 |
测试期间同时开启另一个终端运行 watch -n 1 nvidia-smi 来实时监控 GPU 利用率和显存占用,将峰值记录到下方表格:
| 指标 | RTX 3090 (透传) |
|---|---|
| 吞吐量 (images/sec) | ~512 |
| GPU 利用率 (%) | 100% |
| 显存占用 (MB) | 12739MiB / 24576MiB |
显存压力补充测试
上面的标准基准测试中,RTX 3090 的显存利用率仅约 52%(12.7 GB / 24 GB)。虽然 GPU 计算单元已经满载,但显存映射路径并没有在压力下被充分验证。在透传环境中,VFIO 的 DMA 映射和 QEMU 的 BAR 地址空间分配都可能在高显存占用时出现问题——这类 Bug 只有在接近显存上限时才会暴露。
逐步加大 batch-size,直到触发 OOM(Out of Memory),记录最后一次稳定运行的配置:
# 逐级递增 batch-size,找到显存上限
python train_benchmark.py --model resnet50 --batch-size 256 --epochs 3 --num-gpus 1
python train_benchmark.py --model resnet50 --batch-size 384 --epochs 3 --num-gpus 1
python train_benchmark.py --model resnet50 --batch-size 512 --epochs 3 --num-gpus 1
# 如果 512 仍然不 OOM,继续尝试 640、768...
| 指标 | RTX 3090 (透传) |
|---|---|
| 最大稳定 batch-size | 256 (384 即 OOM) |
| 对应显存占用 | 23145 MiB / 24576 MiB (94.2%) |
| 对应吞吐量 (images/sec) | ~519 |
这个最大稳定 batch-size 的实际意义:它直接反映了透传环境下 GPU 可用显存的真实上限,而非
nvidia-smi报告的理论值。后续规划 vLLM 的--gpu-memory-utilization参数和 KV Cache 大小时,应以此为准而非卡面标称值。
多卡测试使用 PyTorch 官方的 torchrun 启动器,它会自动为每个进程分配 LOCAL_RANK 环境变量并初始化 NCCL 通信后端。
平台 A:4x RTX 3090
torchrun --nproc_per_node=4 train_benchmark.py --model resnet50 --batch-size 128 --epochs 5
平台 B:8x V100
torchrun --nproc_per_node=8 train_benchmark.py --model resnet50 --batch-size 64 --epochs 5
torchrun 参数说明:
| 参数 | 说明 |
|---|---|
--nproc_per_node=4 |
每台机器启动的进程数,必须等于可见 GPU 数量(RTX 3090 平台填 4,V100 平台填 8) |
--batch-size 128 |
每张卡的 local batch size,4 卡场景下全局等效 batch = 128 × 4 = 512 |
--epochs 5 |
多卡场景下同样 5 轮,观察 AllReduce 同步开销是否拖慢总吞吐量 |
V100 平台 batch-size 设为 64 而非 128,是因为 V100-32GB 的 HBM2 带宽虽高但计算单元较少,满载 batch-size 128 时计算单元在等待显存读取的时间反而会拉低单卡利用率。
| 指标 | 4x RTX 3090 (透传) |
|---|---|
| 单卡吞吐量 (images/sec) | ~455 |
| 总吞吐量 (images/sec) | ~1820 |
| 线性扩展比 | 0.89 |
线性扩展比 = 多卡总吞吐量 ÷ (单卡吞吐量 × 卡数)。理想值是 1.0,AllReduce 通信开销会让它低于 1。3090 平台没有 NVLink,梯度同步走 PCIe,0.89 已经算不错;V100 的 NVLink 平台如果后续补齐数据,可以再对比。
DDP 训练中,多卡吞吐量的瓶颈主要源于缺失 NVLink,导致 AllReduce 梯度同步只能依赖 PCIe,从而带来较大的通信开销。在大模型或多卡并行训练时,这种通信限制往往会成为影响性能的主导因素。
LLM 推理性能测试
LLM 推理是 GPU 透传下最典型的工作负载之一。不同框架对底层硬件的使用方式差异很大,因此需要多框架交叉验证,才能准确评估透传层的实际影响。
测试矩阵
| 模型 | 参数量 | 精度 | 显存需求 | 测试框架 |
|---|---|---|---|---|
| Qwen3.5-9B | 9B | FP16 | ~18 GB | vLLM |
| Qwen3.5-35B-A3B | 35B MoE | FP16 | ~15 GB (激活参数极低) | vLLM |
| Qwen3-Coder-Next | ~32B | FP16 | ~64 GB | vLLM |
| zai-org/GLM-5 | ~9B | FP16 | ~18 GB | vLLM |
| MiniMaxAI/MiniMax-M2.5 | MoE | FP16 | - | vLLM (多卡) |
| Qwen3.5-122B-A10B | 122B MoE | FP8 / INT4 | ~60 GB (量化) | vLLM (多卡 TP 必须) |
模型选择需要匹配显存容量。RTX 3090 的 24 GB GDDR6X 和 V100 的 16/32 GB HBM2 在带宽特性和容量上有本质差异——这不仅影响能跑多大的模型,也直接影响 KV Cache 容量和最大并发。
vLLM
vLLM 通过 Continuous Batching + PagedAttention 实现高吞吐推理,是目前生产部署的首选方案。
# 测试节点临时环境使用 pip 部署即可
# 1. 切换 PyPI 清华源加速安装
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip install vllm
# 2. 注入 HF 国内镜像站环境变量,防止起服动态拉取模型时卡死超时
export HF_ENDPOINT="https://hf-mirror.com"
# 9B 2卡
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3.5-9B \
--dtype float16 \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.9 \
--max-model-len 4096 \
--port 8000
# 9B 4卡
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3.5-9B \
--dtype float16 \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.9 \
--max-model-len 4096 \
--port 8000
# 多卡 TP=4,跑 122B MoE 模型
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3.5-122B-A10B \
--dtype float16 \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.9 \
--max-model-len 4096 \
--port 8000
基准测试直接用 vLLM 内置的 benchmark 工具,它会模拟真实的并发请求模式:
export HF_ENDPOINT=https://hf-mirror.com
vllm bench serve \
--backend openai \
--base-url http://localhost:8000 \
--model Qwen/Qwen3.5-9B \
--num-prompts 1000 \
--request-rate inf \
--temperature 0
单卡(9B 模型):
单卡 9B 的 vLLM 数据当时没有保留完整日志,这里不列数字。复现时用上面的 vllm bench serve,把 --num-prompts 和 --request-rate 固定下来再对比。
多卡 TP=4(9B 模型):
这是将 9B 模型切分到 4 张卡上(Tensor Parallel Size = 4)执行推理的结果。由于模型本身较小,将其强行切分到 4 张卡往往会导致显存带宽的收益低于多卡间频繁的通信(AllReduce)开销,这是非常有价值的架构折损测试。
| 指标 | 4x RTX 3090 (透传) |
|---|---|
| 成功并发请求数 | 1000 |
| Total Token Throughput (tokens/s) | 2404.30 |
| Generation Throughput (tokens/s) | 267.14 |
| Request Throughput (req/s) | 2.09 |
| Median TTFT (ms) | 258617.31 |
| Median TPOT (ms) | 754.74 |
指标解读:
- Total Token Throughput (2404 tok/s):系统整体的吞吐表现。包含了 Prompt 解析和 Token 生成。
- Generation Throughput (267 tok/s):实际生成新字符的速度。因为 TP=4 引入了大量的 PCIe 通信,对于仅 9B 的模型来说,通信延迟(ITL ~747ms)极其严重,导致实际生成速度并不高。
- Median TTFT (258s):首字延迟。在
--request-rate inf(瞬间并发 1000 个请求)的极端压力下,排队最久的请求花了 4 分多钟才拿到首字,体现了极端负载下的 PagedAttention 调度压力。
多卡 TP(122B 模型):
122B 的 MoE 模型没有跑完完整的 benchmark,本文不列数字。配置上 4 张 3090 需要做 INT4/FP8 量化并按 TP=4 切分;显存和并发上限要在自己的环境里实测。
GPU 压力与稳定性测试
透传环境的稳定性远比性能更值得关注。一张 GPU 在裸机上跑 24 小时没问题,不代表透传后同样稳定——VFIO 的 DMA 映射、IOMMU 的翻译表、QEMU 的中断路由都是潜在的故障点。用 gpu-burn 做长时间烤机,配合 Prometheus + Grafana + DCGM 做全链路持续监控,才能给出可信的稳定性结论。
gpu-burn 编译
git clone https://github.com/wilicc/gpu-burn.git
cd gpu-burn
make CUDAPATH=/usr/local/cuda
./gpu_burn 10 # 先跑 10 秒验证
gpu-burn 的原理是在所有可见 GPU 上并行执行大矩阵乘法,并定期校验计算结果的正确性。输出中出现 FAULTY 表示计算结果不一致——可能原因包括显存物理缺陷、过热导致的位翻转、PCIe 链路不稳定等。这在二手矿卡和长期高负载运行的 V100 上并不罕见。
监控栈:Prometheus + Grafana + DCGM Exporter
单纯跑 gpu-burn 只能告诉你"通过"或"失败",但不能回答"为什么"。DCGM Exporter 采集的细粒度时序数据(温度曲线、功耗曲线、时钟频率变化、XID 错误、PCIe Replay 计数等)才能帮你定位根因。
graph TB
subgraph Visualization["可视化层"]
GRAFANA["Grafana (:3000)<br/>GPU Monitoring Dashboard"]
end
subgraph Collection["数据采集与存储"]
PROM["Prometheus (:9090)<br/>时序数据采集与存储 (5s / 15s 间隔)"]
end
subgraph Exporters["指标导出层"]
DCGM["dcgm-exporter (:9400)<br/>GPU 指标"]
NODE["node_exporter (:9100)<br/>系统指标"]
end
GRAFANA -->|"PromQL 查询"| PROM
PROM -->|"scrape"| DCGM
PROM -->|"scrape"| NODE
DCGM Exporter
DCGM(Data Center GPU Manager)是 NVIDIA 的数据中心级 GPU 管理框架,比 nvidia-smi 暴露的指标更丰富,采集开销更低。
Docker 部署(推荐):
docker run -d --gpus all --restart always \
--name dcgm-exporter \
-p 9400:9400 \
nvcr.io/nvidia/k8s/dcgm-exporter:3.3.8-3.6.0-debian12
curl -s http://localhost:9400/metrics | head -20
裸装(不依赖 Docker):
# 方案 A:DCGM 全量安装
apt install -y datacenter-gpu-manager
nv-hostengine
dcgmi discovery
# 方案 B:轻量级 nvidia_gpu_exporter(仅依赖 nvidia-smi)
wget https://github.com/utkuozdemir/nvidia_gpu_exporter/releases/download/v1.2.1/nvidia-gpu-exporter_1.2.1_linux_amd64.tar.gz
tar -xzf nvidia-gpu-exporter_1.2.1_linux_amd64.tar.gz
./nvidia_gpu_exporter --web.listen-address=":9835" &
DCGM 关键指标:
| 指标 | 说明 | 单位 | 烤机中的意义 |
|---|---|---|---|
DCGM_FI_DEV_GPU_TEMP |
GPU 核心温度 | °C | 判断散热是否充足 |
DCGM_FI_DEV_GPU_UTIL |
GPU 利用率 | % | 应持续 ~100% |
DCGM_FI_DEV_POWER_USAGE |
实时功耗 | W | 观察是否触达 TDP 上限 |
DCGM_FI_DEV_SM_CLOCK |
SM 时钟 | MHz | 降频的直接体现 |
DCGM_FI_DEV_POWER_VIOLATION |
功耗限制触发时间 | us | 非零说明功耗墙在限制性能 |
DCGM_FI_DEV_THERMAL_VIOLATION |
温度限制触发时间 | us | 非零说明温度墙在限制性能 |
DCGM_FI_DEV_XID_ERRORS |
XID 错误计数 | 次 | 非零即故障 |
DCGM_FI_DEV_PCIE_REPLAY_COUNTER |
PCIe Replay 计数 | 次 | 反映 PCIe 链路信号质量 |
Node Exporter + Prometheus + Grafana
# Node Exporter
docker run -d --restart always \
--name node-exporter \
--net="host" --pid="host" \
-v "/:/host:ro,rslave" \
quay.io/prometheus/node-exporter:latest \
--path.rootfs=/host
Prometheus 配置 /opt/monitoring/prometheus/prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'dcgm-exporter'
scrape_interval: 5s # GPU 指标用更短的采集间隔
static_configs:
- targets: ['localhost:9400']
- job_name: 'node-exporter'
static_configs:
- targets: ['localhost:9100']
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Prometheus
docker run -d --restart always \
--name prometheus \
--net="host" \
-v /opt/monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \
-v /opt/monitoring/prometheus/data:/prometheus \
prom/prometheus:latest \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/prometheus \
--storage.tsdb.retention.time=30d \
--web.enable-lifecycle
# Grafana
mkdir -p /opt/monitoring/grafana
docker run -d --restart always \
--name grafana \
--net="host" \
-v /opt/monitoring/grafana:/var/lib/grafana \
-e "GF_SECURITY_ADMIN_PASSWORD=admin" \
grafana/grafana:latest
Grafana 中导入 DCGM 官方 Dashboard(ID: 12239),然后根据实际需要定制面板。烤机测试中重点关注的 PromQL 查询:
# 温度趋势
DCGM_FI_DEV_GPU_TEMP{job="dcgm-exporter"}
# SM 降频观察
DCGM_FI_DEV_SM_CLOCK{job="dcgm-exporter"}
# 功耗墙触发频率
rate(DCGM_FI_DEV_POWER_VIOLATION{job="dcgm-exporter"}[1m])
# XID 错误(任何非零都是红灯)
DCGM_FI_DEV_XID_ERRORS{job="dcgm-exporter"}
# PCIe Replay 错误趋势
DCGM_FI_DEV_PCIE_REPLAY_COUNTER{job="dcgm-exporter"}
告警规则:GPU 温度 > 85°C、XID 错误 > 0、连续降频 > 5 分钟——这三条覆盖了绝大多数会导致烤机失败的场景。
Docker Compose 一键部署
如果在多台机器上需要反复部署,用 Compose 更高效:
# /opt/monitoring/docker-compose.yml
version: '3.8'
services:
dcgm-exporter:
image: nvcr.io/nvidia/k8s/dcgm-exporter:3.3.8-3.6.0-debian12
restart: always
deploy:
resources:
reservations:
devices:
- capabilities: [gpu]
ports:
- "9400:9400"
node-exporter:
image: quay.io/prometheus/node-exporter:latest
restart: always
network_mode: host
pid: host
volumes:
- /:/host:ro,rslave
command:
- '--path.rootfs=/host'
prometheus:
image: prom/prometheus:latest
restart: always
network_mode: host
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus/data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
grafana:
image: grafana/grafana:latest
restart: always
network_mode: host
volumes:
- ./grafana:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
cd /opt/monitoring && docker compose up -d
烤机测试执行
分阶段递进,逐步延长负载时间:
| 阶段 | 时长 | 验证目标 |
|---|---|---|
| 预热 | 60s | VFIO 绑定正常、gpu-burn 能跑起来、所有 GPU 被加载 |
| 短时压力 | 10min | 温度能否在合理范围内收敛到稳态 |
| 中等负载 | 1h | 长时间满载下的降频行为和功耗墙表现 |
| 长时烤机 | 8-24h | 检测间歇性显存错误、PCIe 链路退化、热积累导致的渐进性问题 |
./gpu_burn 60 # 预热
./gpu_burn 600 # 短时
./gpu_burn 3600 # 中等
# 长时烤机建议放在 tmux 中
tmux new -s gpuburn
./gpu_burn 28800
配合终端侧的实时监控和日志记录:
# 实时观察
watch -n 1 nvidia-smi
# 结构化日志记录
while true; do
echo "=== $(date) ==="
nvidia-smi --query-gpu=timestamp,name,gpu_bus_id,temperature.gpu,utilization.gpu,utilization.memory,power.draw,clocks.sm,clocks.mem,memory.used,memory.total,pcie.link.gen.current,pcie.link.width.current --format=csv
sleep 5
done | tee gpu_burn_log_$(date +%Y%m%d_%H%M%S).csv
# DCGM 完整诊断(在烤机结束后跑一次)
dcgmi diag -r 3
烤机结果
8 小时长烤没有跑完,本文不列温度和功耗数字。复现时按上面的分阶段命令执行,把 nvidia-smi 的 CSV 日志留好;下面是对结果的判定标准。
稳定性判定标准
| 评估项 | 通过 | 关注 | 不通过 |
|---|---|---|---|
| GPU 稳态温度 | < 83°C | 83-89°C(检查风道/散热片) | >= 90°C(过热,必须解决才能投产) |
| 降频行为 | 无降频 | 偶发降频 (< 5% 时间) | 持续降频 (> 10% 时间) |
| XID 错误 | 0 | — | > 0(硬件或驱动故障) |
| PCIe Replay 错误 | 0 | 1-10(可能是信号抖动) | > 10(检查 PCIe riser/线缆/Switch) |
| gpu-burn FAULTY | 0 | — | > 0(显存或计算单元故障) |
| 功耗限制 | 偶发(正常行为) | 频繁(检查 TDP 配置) | 持续(需降低 power limit 或改善供电) |
模型服务部署架构
透传跑通、基准测试通过之后,真正的问题才开始:多模型并存时的 GPU 分配、请求路由、访问控制和可观测性。本节直接给出面向生产的方案——以 LiteLLM 网关为统一入口,安全策略内建而非后补。
整体架构
graph TB
subgraph Clients["调用方"]
OPENCLAW["OpenClaw Agent"]
WEBAPP["Web 应用"]
API_USER["API 调用方"]
end
subgraph Gateway["LLM 网关层 (VLAN 20)"]
TLS["TLS 终结<br/>Caddy / Nginx"]
LITE["LiteLLM Proxy<br/>:4000"]
TLS --> LITE
end
subgraph GPU_VM["GPU 推理层 (VLAN 20)"]
V1["vLLM :8001<br/>Llama-3.1-8B<br/>GPU 0 · fast"]
V2["vLLM :8002<br/>Qwen2.5-14B<br/>GPU 1 · balanced"]
V3["vLLM :8003<br/>DeepSeek-R1-32B<br/>GPU 2-3 · reasoning"]
end
subgraph Fallback["外部 Fallback"]
CLAUDE["Claude API"]
end
subgraph Observability["可观测性"]
LANGFUSE["Langfuse<br/>Tracing + 审计"]
PROM_OBS["Prometheus<br/>+ Grafana"]
end
Clients -->|"HTTPS + API Key"| TLS
LITE --> V1
LITE --> V2
LITE --> V3
LITE -->|"Fallback"| CLAUDE
LITE -->|"Callback"| LANGFUSE
GPU_VM --> PROM_OBS
架构要点:
- 所有调用方只和 LiteLLM 网关通信——vLLM 后端端口不对外暴露,鉴权、限流、审计集中在网关一个点实施
- TLS 终结在网关前端——API Key 和推理内容不在网络上明文传输
- vLLM 后端和网关部署在同一 VLAN——调用方(OpenClaw / Web App)在另一个 VLAN,通过防火墙仅放行网关端口
GPU 分配策略
GPU 分配的核心原则:按模型权重和并发需求分卡,而不是平均分。
平台 A(4x RTX 3090,24GB/卡):
| GPU | 模型 | 量化 | 显存占用 | 定位 |
|---|---|---|---|---|
| GPU 0 | Llama-3.1-8B-Instruct | FP16 | ~16 GB | fast — 日常对话、翻译、格式化 |
| GPU 1 | Qwen2.5-14B-Chat | INT4 (GPTQ) | ~8 GB | balanced — 内容生成、技术问答 |
| GPU 2-3 | DeepSeek-R1-Distill-Qwen-32B | INT4 (AWQ), TP=2 | ~10 GB/卡 | reasoning — 代码审查、复杂推理 |
平台 B(8x V100-32GB):
| GPU | 模型 | 量化 | 显存占用 | 定位 |
|---|---|---|---|---|
| GPU 0 | Llama-3.1-8B-Instruct | FP16 | ~16 GB | fast |
| GPU 1-2 | Qwen2.5-14B-Chat | FP16, TP=2 | ~14 GB/卡 | balanced |
| GPU 3-6 | Llama-3.1-70B-Instruct | INT4 (AWQ), TP=4 | ~10 GB/卡 | reasoning |
| GPU 7 | Embedding / Reranker | FP16 | ~4 GB | embedding — RAG 场景 |
模型下载与认证 (Hugging Face)
在生产环境或没有流畅外网的机房里,不建议让 vLLM 在启动时现拉模型图层。更稳妥的做法是提前将模型下载到本地。特别是像 Llama-3.1 这样的受限模型(Gated Model),必须先在 Hugging Face 官网申请通过并提供 HF_TOKEN 才能下载。
- 在 Hugging Face 获取 Access Token(Role 设为 Read 即可)
- 注入环境变量并使用原生工具大并发下载:
# 安装 HF 官方工具包
pip install -U "huggingface_hub[cli]"
# 环境变量注入
export HF_TOKEN="hf_xxxxx_your_token_xxxxx"
# 使用国内镜像站加速(避免 huggingface.co 连接超时)
export HF_ENDPOINT="https://hf-mirror.com"
# 指定模型下载的统一目录(比如挂载的高速 NVMe 盘)
export HF_HOME="/data/llm/models"
# 使用 CLI 高速下载(支持断点续传)
# Llama-3.1 是受限模型,必须有 HF_TOKEN 才能拉取
hf download Qwen/Qwen3.5-9B
hf download Qwen/Qwen3.5-35B-A3B
hf download Qwen/Qwen3-Coder-Next
hf download zai-org/GLM-5
hf download MiniMaxAI/MiniMax-M2.5
hf download Qwen/Qwen3.5-122B-A10B
有了本地缓存,即使断网 vLLM 仍能秒级启动。
Docker 环境准备
生产环境下 vLLM 必须使用容器化部署,而非裸 pip install——后者缺乏隔离、版本锁定和进程管理能力。先在 GPU VM 上完成 Docker Engine 和 NVIDIA Container Toolkit 的安装:
# 使用官方安装脚本,通过清华镜像源加速
export DOWNLOAD_URL="https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
curl -fsSL https://get.docker.com | sh
# 安装 NVIDIA Container Toolkit(让 Docker 能访问透传的 GPU)
apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
gnupg2
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | 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' | \
tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
export NVIDIA_CONTAINER_TOOLKIT_VERSION=1.18.2-1
apt-get install -y \
nvidia-container-toolkit=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
nvidia-container-toolkit-base=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
libnvidia-container-tools=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
libnvidia-container1=${NVIDIA_CONTAINER_TOOLKIT_VERSION}
nvidia-ctk runtime configure --runtime=docker
systemctl restart docker
# 验证 GPU 在容器内可见
docker run --rm --gpus all hub.infra.plz.ac/nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi
启动 vLLM 后端
使用 Docker Compose 统一管理所有 vLLM 推理实例(仅监听 localhost,不对外暴露):
# /opt/llm-inference/docker-compose.yml
services:
vllm-fast:
image: vllm/vllm-openai:latest
restart: always
ipc: host
ports:
- "127.0.0.1:8001:8000"
volumes:
- /data/llm/models:/root/.cache/huggingface
environment:
- HF_ENDPOINT=https://hf-mirror.com
- TRANSFORMERS_OFFLINE=1
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ['0']
capabilities: [gpu]
command: >
--model Qwen/Qwen3.5-9B
--dtype float16
--gpu-memory-utilization 0.9
--max-model-len 4096
--port 8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
vllm-balanced:
image: vllm/vllm-openai:latest
restart: always
ipc: host
ports:
- "127.0.0.1:8002:8000"
volumes:
- /data/llm/models:/root/.cache/huggingface
environment:
- HF_ENDPOINT=https://hf-mirror.com
- TRANSFORMERS_OFFLINE=1
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ['1']
capabilities: [gpu]
command: >
--model Qwen/Qwen3.5-35B-A3B
--dtype float16
--gpu-memory-utilization 0.9
--max-model-len 4096
--port 8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
vllm-reasoning:
image: vllm/vllm-openai:latest
restart: always
ipc: host
ports:
- "127.0.0.1:8003:8000"
volumes:
- /data/llm/models:/root/.cache/huggingface
environment:
- HF_ENDPOINT=https://hf-mirror.com
- TRANSFORMERS_OFFLINE=1
deploy:
resources:
reservations:
devices:
- driver: nvidia
device_ids: ['2', '3']
capabilities: [gpu]
command: >
--model Qwen/Qwen3.5-122B-A10B
--dtype float16
--tensor-parallel-size 2
--gpu-memory-utilization 0.9
--max-model-len 8192
--port 8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
cd /opt/llm-inference && docker compose up -d
# 验证所有实例健康
docker compose ps
curl -s http://127.0.0.1:8001/health
curl -s http://127.0.0.1:8002/health
curl -s http://127.0.0.1:8003/health
127.0.0.1:800x:8000端口映射确保 vLLM 只监听本地回环。restart: always+healthcheck实现进程级自愈:容器崩掉自动重启,健康检查失败自动标记为 unhealthy 供 LiteLLM 摘除。
LiteLLM 作为统一入口,负责模型路由、API Key 鉴权、限流、Fallback 和审计。
# /opt/llm-gateway/litellm_config.yaml
model_list:
- model_name: "fast"
litellm_params:
model: "openai/meta-llama/Llama-3.1-8B-Instruct"
api_base: "http://127.0.0.1:8001/v1"
api_key: "not-used"
- model_name: "balanced"
litellm_params:
model: "openai/Qwen/Qwen2.5-14B-Chat-GPTQ-Int4"
api_base: "http://127.0.0.1:8002/v1"
api_key: "not-used"
- model_name: "reasoning"
litellm_params:
model: "openai/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B"
api_base: "http://127.0.0.1:8003/v1"
api_key: "not-used"
# 外部 Fallback:本地 reasoning 不可用时自动降级
- model_name: "reasoning"
litellm_params:
model: "claude-sonnet-4-20250514"
api_key: "${ANTHROPIC_API_KEY}"
router_settings:
routing_strategy: "simple-shuffle"
num_retries: 2
timeout: 300
allowed_fails: 3 # 连续失败 3 次暂时摘除后端
general_settings:
master_key: "${LITELLM_MASTER_KEY}"
database_url: "sqlite:///litellm.db"
allow_user_auth: true
litellm_settings:
max_budget: 100
budget_duration: "monthly"
success_callback: ["langfuse"] # 请求级 tracing
store_model_in_db: true # 审计日志持久化
同名模型
reasoning定义了两个后端(本地 vLLM + Claude API)。LiteLLM 会优先使用本地实例,连续失败后自动 Fallback 到 Claude。
访问控制
多级 API Key
不同调用方分配不同权限的 Key——不存在一个无限权限的"万能 Key":
# OpenClaw Agent — 三个模型都可用,月预算 $50
curl -X POST http://localhost:4000/key/generate \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}" \
-d '{
"key_alias": "openclaw-agent",
"models": ["fast", "balanced", "reasoning"],
"max_budget": 50, "budget_duration": "monthly",
"max_parallel_requests": 5,
"tpm_limit": 100000, "rpm_limit": 60
}'
# Web 应用 — 只允许 fast,月预算 $10
curl -X POST http://localhost:4000/key/generate \
-H "Authorization: Bearer ${LITELLM_MASTER_KEY}" \
-d '{
"key_alias": "webapp-chatbot",
"models": ["fast"],
"max_budget": 10, "budget_duration": "monthly",
"max_parallel_requests": 10,
"tpm_limit": 50000, "rpm_limit": 100
}'
| 控制维度 | OpenClaw Key | Web App Key |
|---|---|---|
| 可用模型 | fast, balanced, reasoning | 仅 fast |
| 月度预算 | $50 | $10 |
| 最大并发 | 5 | 10 |
| TPM 限制 | 100K | 50K |
| RPM 限制 | 60 | 100 |
TLS + mTLS
# Caddy 一行搞定 TLS 终结(自签证书 / ACME)
caddy reverse-proxy --from llm.internal:443 --to localhost:4000
内网有自己 CA 的场景下用 mTLS 做双向认证:
server {
listen 443 ssl;
server_name llm.internal;
ssl_certificate /etc/ssl/llm-gateway.crt;
ssl_certificate_key /etc/ssl/llm-gateway.key;
ssl_client_certificate /etc/ssl/ca.crt;
ssl_verify_client on;
location / {
proxy_pass http://127.0.0.1:4000;
proxy_read_timeout 600s;
}
}
网络隔离
graph LR
subgraph VLAN20["VLAN 20 — AI 推理网络"]
VLLM["vLLM(仅 127.0.0.1)"]
LITELLM_GW["LiteLLM Gateway"]
VLLM ---|"localhost"| LITELLM_GW
end
subgraph VLAN30["VLAN 30 — 应用网络"]
OC["OpenClaw"]
WA["Web App"]
end
subgraph VLAN10["VLAN 10 — 管理网络"]
ADM["管理终端"]
end
OC -->|"仅 :443"| LITELLM_GW
WA -->|"仅 :443"| LITELLM_GW
ADM -->|"SSH + Grafana"| VLLM
| 规则 | 源 → 目标 | 端口 | 说明 |
|---|---|---|---|
| 允许 | VLAN 30 → LiteLLM | 443 | 调用方只能走网关 |
| 允许 | VLAN 10 → GPU VM | 22, 3000, 9090 | 运维管理 |
| 拒绝 | VLAN 30 → vLLM | 8001-8003 | 禁止绕过网关 |
| 拒绝 | * → GPU VM | * | 默认拒绝 |
Langfuse 可观测性与 Tracing
在多模型+Agent的架构中,仅仅有 Prometheus 监控 GPU 硬件层面的指标(利用率、显存、功耗)是不够的。在业务层,你需要知道:每个 Prompt 究竟长什么样、模型回复了什么包、哪一步生成发生了拒绝回答(Refusal)、不同 Agent 的实际 Token 消耗成本是多少。
Langfuse 是目前首选的开源 LLM 可观测性平台,它能和 LiteLLM 网关无缝整合。
部署 Langfuse 平台
使用 Docker Compose 在管理网络(VLAN 10)单独拉起 Langfuse 基础设施:
# docker-compose.langfuse.yml
version: '3.8'
services:
langfuse-server:
image: langfuse/langfuse:2
depends_on:
- db
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres
- NEXTAUTH_SECRET=my_secure_secret
- SALT=my_secure_salt
- NEXTAUTH_URL=http://192.168.1.14:3000
- TELEMETRY_ENABLED=false
db:
image: postgres:15
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
volumes:
- langfuse_data:/var/lib/postgresql/data
volumes:
langfuse_data:
LiteLLM 无侵入接入
LiteLLM 原生深度集成了 Langfuse。只需向 LiteLLM Gateway 提供凭据,即可自动拦截并上传所有的请求元数据,完全不需要修改 OpenClaw 或 Web 应用层面的代码。
在 LiteLLM 环境变量中注入 Langfuse API 密钥:
# 在启动 LiteLLM前设置(或写入系统 service 文件)
export LANGFUSE_PUBLIC_KEY="pk-lf-..."
export LANGFUSE_SECRET_KEY="sk-lf-..."
# 指向刚部署的 Langfuse 地址
export LANGFUSE_HOST="http://192.168.1.14:3000"
确保 litellm_config.yaml 中启用了回调:
litellm_settings:
success_callback: ["langfuse"]
failure_callback: ["langfuse"]
Agent 场景核心排障能力
接入后,无论是 Web App 还是 OpenClaw Agent 产生的对话,都能在 Langfuse 控制台获得以下业务级观测能力:
- Generation Replay (生成回放):完整查看原始请求头、System Prompt、工具定义(Tool definitions)和最终响应。对于 OpenClaw Agent 场景,Agent 组装的上下文非常复杂,当发生越狱漏洞(Prompt Injection)时,这是极其关键的留档证据。
- 多维度耗时拆解 (Latency Breakdown):Langfuse 会将流式请求(Streaming)精确拆解为 Time-to-First-Token (首字延迟 TTFT) 和 Tokens-per-Second (生成速度)。如果首字延迟异常飙升,往往说明 vLLM PagedAttention K-V Cache 碎化严重或并发队列在排队;如果生成速度骤降,可以通过回放观察是不是命中了复杂的推理生成。
- 用户粒度成本聚合 (Cost by User/Key):由于前文为 OpenClaw 和 WebApp 分配了不同的 LiteLLM Key,Langfuse 自动将 Key ID 映射归类。你可以直观看到不同调用方每日"吃掉"了多少请求、占用多少 Token,配合预算体系实现闭环 FinOps 管理。
动态模型调度(可选)
上面的方案是"每模型独占 GPU,常驻内存"——适合模型数量 <= GPU 数量的场景。当模型数量超过 GPU 数量时,需要动态调度。
vLLM LoRA 动态加载
同基座模型 + 多 LoRA 适配器的场景下,vLLM 原生支持 LoRA 热切换,无需多实例:
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-3.1-8B-Instruct \
--enable-lora \
--lora-modules \
code-assistant=./lora-adapters/code \
translator=./lora-adapters/translator \
summarizer=./lora-adapters/summarizer \
--max-loras 3 --max-lora-rank 16 \
--host 127.0.0.1 --port 8001
LoRA 适配器只有几十 MB,切换几乎零开销。适合企业内部多部门共用一个基座模型但需要不同领域能力的场景。
Ray Serve 弹性调度
GPU 数量够多(如 8x V100)且需要精细化资源管理时:
# serve_app.py
from ray import serve
from vllm import LLM, SamplingParams
@serve.deployment(num_replicas=1, ray_actor_options={"num_gpus": 1})
class LLMDeployment:
def __init__(self, model_name: str):
self.llm = LLM(model=model_name, dtype="float16", gpu_memory_utilization=0.9)
async def generate(self, prompt: str, max_tokens: int = 512):
params = SamplingParams(max_tokens=max_tokens)
outputs = self.llm.generate([prompt], params)
return outputs[0].outputs[0].text
light = LLMDeployment.bind("meta-llama/Llama-3.1-8B-Instruct")
heavy = LLMDeployment.bind("deepseek-ai/DeepSeek-R1-Distill-Qwen-32B")
Ray Serve 支持 GPU 分数分配(如 0.5 GPU 跑小模型)、按请求量自动扩缩、跨节点调度。代价是引入 Ray 集群的运维复杂度——4 卡场景不值得,8 卡以上可以考虑。
OpenClaw 集成
将上面的推理集群接入 OpenClaw,实现按任务复杂度自动路由模型。
架构
graph TB
subgraph Access["消息通道"]
TG["Telegram Bot"]
WEB["Control UI"]
end
subgraph Agent["OpenClaw (VLAN 30)"]
GW["Gateway :18789"]
PI["Agent 实例"]
SANDBOX["Docker Sandbox"]
SKILL["Skills<br/>model-router"]
end
subgraph LLM["LLM 网关 (VLAN 20)"]
LITE["LiteLLM :443<br/>TLS + Key 鉴权"]
end
subgraph GPUs["GPU 推理 (VLAN 20)"]
F["fast<br/>8B"]
B["balanced<br/>14B"]
R["reasoning<br/>32B"]
end
TG --> GW
WEB --> GW
GW --> PI
PI --> SANDBOX
PI --> SKILL
PI -->|"sk-openclaw-xxx"| LITE
LITE --> F
LITE --> B
LITE --> R
OpenClaw 配置
{
"models": {
"providers": {
"gpu-cluster": {
"type": "openai-compatible",
"baseUrl": "https://llm.internal/v1",
"apiKey": "${LITELLM_API_KEY}",
"models": [
{ "id": "fast", "name": "快速响应 (8B)", "contextLength": 4096 },
{ "id": "balanced", "name": "均衡 (14B)", "contextLength": 4096 },
{ "id": "reasoning", "name": "深度推理 (32B)", "contextLength": 8192 }
]
}
}
},
"agents": {
"defaults": {
"model": "gpu-cluster:fast"
}
},
"tools": {
"profile": "messaging",
"deny": ["group:automation", "group:runtime"],
"exec": { "security": "deny", "ask": "always" },
"elevated": { "enabled": false }
},
"channels": {
"telegram": {
"dmPolicy": "pairing",
"groups": { "*": { "requireMention": true } }
}
}
}
自动模型路由 Skill
通过 Skill 让 Agent 根据任务性质自动切换模型,用户无需手动 /model:
<!-- ~/.openclaw/skills/model-router/SKILL.md -->
---
name: model-router
description: 根据任务复杂度自动选择 LLM 模型
---
## 模型选择规则
根据以下规则选择模型,切换前无需询问用户:
1. **简单任务**(翻译、格式转换、日常对话、摘要)→ `gpu-cluster:fast`
2. **中等任务**(技术问答、内容创作、文档编写)→ `gpu-cluster:balanced`
3. **复杂任务**(代码审查、数学推理、架构设计、多步分析)→ `gpu-cluster:reasoning`
切换时使用 `/model` 命令。如果不确定复杂度,默认使用 `balanced`。
安全防线
多模型 + Agent 场景下的威胁模型是 prompt injection → 模型越权 → 命令执行。三层防御缺一不可:
| 防御层 | 实施点 | 措施 |
|---|---|---|
| 模型权限 | LiteLLM Key | models 白名单——Agent 只能调用被授权的模型 |
| 执行权限 | OpenClaw tools |
exec.security: "deny" + elevated: false——即使推理结果含危险命令也不执行 |
| 资源兜底 | LiteLLM Key | max_budget + rpm_limit——prompt 注入导致的循环调用有上限 |
| 审计追溯 | LiteLLM + Langfuse + OpenClaw Session | 完整请求链路:谁 → 哪个 Key → 哪个模型 → 多少 Token |
| 网络隔离 | VLAN + 防火墙 | Agent 无法绕过网关直连 vLLM,攻击面收窄到 API 层 |
即使攻击者成功注入恶意 prompt 让 Agent 切换到
reasoning模型并生成了rm -rf /指令:LiteLLM 的 RPM 限制阻止了批量请求;OpenClaw 的exec.security: "deny"拦截了命令执行;max_budget确保即使跑了一夜也只花 $50。
排错手册
GPU 初始化失败
症状: VM 内 nvidia-smi 报 No devices found 或 NVIDIA-SMI has failed
排查路径:
# 1. 宿主机侧:确认 VFIO 绑定
lspci -nnk -s <BDF>
# 期望: Kernel driver in use: vfio-pci
# 2. 宿主机 dmesg
dmesg | grep -i vfio
dmesg | grep -i nvidia
# 3. VM 配置
grep hostpci /etc/pve/qemu-server/<VMID>.conf
常见根因(按频率排序):
- NVIDIA 驱动在宿主机上先于 VFIO 加载了 GPU——检查 initramfs 中的模块加载顺序
- IOMMU 分组问题——同组内有其他设备未被绑定
- Above 4G Decoding 未开启——BAR 分配失败
- ReBAR 导致的
Code 43——关闭 BIOS 中的 Resizable BAR
无板载显卡时宿主机黑屏
这不是 Bug,这是预期行为。没有 framebuffer 设备自然没有画面。
解法:
- IPMI:最可靠的带外管理
- SSH:保证网络配置正确即可
- PVE Web UI:
https://<PVE_IP>:8006
多卡透传不完整
症状: 透传 4 张 GPU,VM 内只识别到 2-3 张
# 逐张确认 VFIO 状态
for dev in $(lspci -nn | grep NVIDIA | awk '{print $1}'); do
echo "=== $dev ==="
lspci -nnk -s $dev
done
大概率是 IOMMU 分组问题,或者是 VM 配置文件中 hostpci 地址写错了。另外检查 qm.conf 中是否所有 hostpci 行都被正确解析(PVE 有时会因为格式问题静默忽略某些行)。
GPU Reset Bug
症状: VM 关机后重启,GPU 无法初始化,必须重启宿主机
NVIDIA GPU 的 FLR(Function Level Reset)实现并不完善,部分型号在 VM 关机后无法干净地重置回初始状态。缓解措施:
- 使用
vendor-reset内核模块(AMD GPU 有效,NVIDIA 场景有限) - PVE 内核 6.x+ 对 NVIDIA FLR 有改善
- VM 关机使用 ACPI shutdown(QEMU Guest Agent)而非
stop/reset
性能异常
排查思路(从低到高):
- PCIe 链路降级:
lspci -vvv | grep -i lnksta确认链路速度和宽度是否符合预期 - NUMA 亲和性错误:GPU 在 NUMA Node 0,VM 的 vCPU 绑定在 Node 1——跨 NUMA 的内存访问延迟翻倍
- Hugepages 未启用:QEMU 默认用 4K 页,TLB miss 率高
- 宿主机 CPU 调度器:检查
scaling_governor是否为performance
性能调优
NUMA 亲和性
GPU 透传性能的关键在于数据路径最短——GPU、内存、CPU 必须在同一个 NUMA 域内。
# 查看 GPU 所属 NUMA 节点
cat /sys/bus/pci/devices/0000:41:00.0/numa_node
# 查看系统 NUMA 拓扑
numactl -H
VM 配置:
# /etc/pve/qemu-server/<VMID>.conf
cpu: host
cores: 16
numa: 1
cpuunits: 10000
理想状态是 vCPU pin 到 GPU 同侧的物理核上,内存也从同一 NUMA 节点分配。这在双路平台上尤为关键——跨路访问的延迟惩罚通常在 50-100ns。
Hugepages
# 宿主机
echo "vm.nr_hugepages = 16384" >> /etc/sysctl.conf
sysctl -p
# VM 配置
# /etc/pve/qemu-server/<VMID>.conf
hugepages: 1024
Hugepages 减少了 QEMU 进程的 TLB miss,对大内存 VM(GPU 工作负载通常分配 64-128 GB)效果显著。
PCIe ACS Override
pcie_acs_override=downstream,multifunction 在 GRUB 参数中已配置。关于它的安全影响:ACS Override 绕过了 PCIe 的 Access Control Services,意味着同一 PCIe Switch 下的设备之间不再有硬件级的 DMA 隔离。在单租户(所有 GPU 都给同一个 VM)场景下这完全可以接受;在多租户场景下需要评估风险。
结论
总结与建议
这轮做完,4 卡 RTX 3090 平台在 PVE 下完成了透传配置、VFIO 绑定、PyTorch 训练和 vLLM 推理的实测;V100 平台记录了配置差异,性能数据还没采集,不能拿它和 3090 做数字对比。
- RTX 3090 平台(无板载显卡):保留 SSH + Web 管理,4 张卡直通给同一台 Debian 13 VM 可行;单卡 ResNet-50 约 512 images/sec,4 卡 DDP 总吞吐约 1820 images/sec,线性扩展比 0.89。
- V100 平台(双路 NUMA):配置差异已经梳理,IOMMU 分组和 NUMA 绑定的注意事项写在上面;8 卡训练、推理和长时烤机数据还没采集,后续补。
透传本身在当前内核(6.x+)和 QEMU 下已经比较成熟,但“能跑”和“能稳定跑”是两回事。IOMMU 分组、NUMA 亲和性、Hugepages 和监控要一起配;比起追求最后一点带宽损耗,先把可观测性和模型网关做稳更有价值。
后续方向
- vGPU 方案评估(NVIDIA vGPU License / SR-IOV MIG)
- 不同 PVE 内核版本对透传稳定性的影响
- Windows VM 透传与 Anti-VM Detection
- K8s GPU Operator 在透传 VM 内的部署体验
- 补齐 V100 平台的训练、推理和 8 小时烤机数据
参考资料
- Proxmox VE PCI Passthrough 官方文档
- VFIO Tips and Tricks
- Arch Wiki - PCI passthrough via OVMF
- NVIDIA CUDA Toolkit Documentation
- gpu-burn - GPU stress test
- NVIDIA DCGM Documentation
- dcgm-exporter GitHub
- Grafana DCGM Dashboard
- vLLM Documentation
- LiteLLM Documentation
- Ray Serve Documentation
- OpenClaw Documentation
- Langfuse - LLM Observability
如果你觉得这篇文章对你有所帮助,欢迎赞赏~
赞赏