这次折腾的是一台 GPU 节点上的 KVM 虚拟机:把 8 张 H800 和 4 个 NVSwitch 直通进去,再在里面跑单节点 K8s、GPU Operator 和 Prometheus。
过程不复杂,但坑都挺经典:黑屏、PCI 资源不够、DHCP 租约乱掉、ServiceMonitor 标签没对上。这里按我实际排障顺序记一遍,免得下次又从头翻日志。
环境和目标 #
- 宿主机:GPU 节点
- 虚拟机:
ubuntu_gpu(KVM/libvirt, UEFI/OVMF) - 目标:8 卡 H800 + 4 NVSwitch 直通,单节点 K8s 能调度 GPU,Prometheus 能抓到 DCGM 指标
大致排障路线 #
flowchart TD A[VM 启动 + GPU 直通] --> B[黑屏 / 部分 GPU 无法初始化] B --> C[切换 OVMF 非 Secure Boot] C --> D[扩大 PCI hole64] D --> E[NUMA + vCPU 亲和] E --> F[8 GPU + NVSwitch 正常] F --> G[K8s + GPU Operator + Prometheus] G --> H[DCGM 指标验证通过]
1. 黑屏和 PCI 资源不足 #
最开始的现象比较吓人:GPU 直通后 VNC 没画面,进系统后驱动日志里还能看到 PCI I/O region invalid。这类问题先别急着怀疑驱动,Q35 的 64-bit PCI hole 很容易不够用,尤其是多卡加 NVSwitch。
我这里做了两件事:
- OVMF 换成 non-secure 版本,先排除 Secure Boot 干扰
- 把
pci-hole64-size拉到 2048G
<!-- OVMF non-secure -->
<loader readonly='yes' type='pflash'>/usr/share/edk2/ovmf/OVMF_CODE.cc.fd</loader>
<!-- Q35 PCIe 64-bit hole -->
<qemu:commandline>
<qemu:arg value='-global'/>
<qemu:arg value='q35-pcihost.pci-hole64-size=2048G'/>
</qemu:commandline>改完后,GPU 初始化和拓扑识别就正常了。这个参数平时很少碰,一旦多卡直通出怪问题,值得优先看一眼。
2. IP 不通:其实是 DHCP 绑定 #
中间还遇到一个很没技术含量但很浪费时间的问题:虚拟机 IP 不通。最后发现是网卡 MAC 被改过,DHCP 绑定没匹配上。
恢复旧 MAC 后,地址回到 192.168.122.146。
# 旧 MAC
52:54:00:a9:a2:11这种问题日志不会多热情地提醒你,只能老老实实把 libvirt XML、DHCP lease 和路由都对一遍。
3. 内存、NUMA 和 vCPU 亲和 #
虚拟机内存调到 256GB:
<memory unit='KiB'>268435456</memory>
<currentMemory unit='KiB'>268435456</currentMemory>vCPU 分两组 pin 到对应 NUMA node,GPU 也按 NUMA 归属放置。多卡环境里这一步不一定决定能不能启动,但会影响后面跑任务时的稳定性和延迟,顺手做好比较省心。
4. 验证 GPU、K8s 和监控 #
GPU 侧先确认卡、拓扑和 NVLink:
nvidia-smi -L
nvidia-smi topo -m
nvidia-smi nvlink -sK8s 侧确认节点和 Pod:
kubectl get nodes -o wide
kubectl get pods -APrometheus 侧直接查一条 DCGM 指标:
curl http://127.0.0.1:9090/api/v1/query?query=DCGM_FI_DEV_SM_CLOCK能查到数据,说明 DCGM Exporter 到 Prometheus 这条链路基本通了。
5. 关键配置片段 #
containerd 代理 #
这台机器没有直连外网,containerd 需要走代理拉镜像:
# /etc/systemd/system/containerd.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://100.64.0.5:8888"
Environment="HTTPS_PROXY=http://100.64.0.5:8888"
Environment="NO_PROXY=127.0.0.1,localhost,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10"GPU Operator 和 Prometheus #
helm upgrade --install gpu-operator nvidia/gpu-operator \
-n gpu-operator --create-namespace \
--set driver.enabled=false --set dcgmExporter.enabled=true
helm upgrade --install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
-n monitoring --create-namespaceDCGM 指标接入 Prometheus #
这里最容易漏的是 ServiceMonitor 标签。Prometheus 默认只抓自己 release 标签匹配的对象,所以要补上:
# ServiceMonitor 需要有与 Prometheus release 一致的标签
kubectl -n gpu-operator label servicemonitor nvidia-dcgm-exporter release=kube-prometheus-stack --overwrite复盘 #
- 多 GPU 直通出问题,先看 OVMF 和
pci-hole64-size - Secure Boot 先关掉验证,别把变量留太多
- DHCP 突然不对劲时,检查 MAC 是否被 libvirt 改过
- DCGM Exporter 有了不代表 Prometheus 能抓到,
release标签要对齐
这次的坑基本都是“配置差一点点就完全不工作”的类型。好在修完以后,8 卡、NVSwitch、单节点 K8s 和监控链路都稳定了。