vLLM 服务在 Kubernetes 中做 HPA 时,最常踩的坑是把“显存指标”误以为可以从 kubelet 默认暴露的指标接口拿。kubelet 的 stats/metrics 端点只覆盖 CPU、内存、网络、磁盘这类基础指标,GPU 显存使用量并不在内,也不是调 kubelet 的 feature gate 就能直接补上。这条链路通常由「指标来源(exporter)→ 指标采集(Prometheus)→ 指标适配器(Prometheus Adapter / Custom Metrics API)→ HPA」组成,先确认你缺哪一环,再回头看 kubelet 配置不迟。
显存指标采集不到,通常不是 kubelet 单点配置问题,而是指标未被容器化方式暴露。先确认节点已上报 nvidia.com/gpu 扩展资源;再部署 DCGM/GPU exporter 暴露指标;最后通过 Prometheus Adapter 将指标以自定义或外部指标供 HPA 使用。kubelet 配置只影响设备识别和资源上报,不影响显存值的采集。
先确认 HPA 当前用的是什么指标源
Kubernetes HPA 支持三类指标源:基础资源指标(metrics.k8s.io)、自定义指标(custom.metrics.k8s.io)、外部指标(external.metrics.k8s.io)。显存指标通常不在基础资源指标内,所以如果 HPA 引用 metrics.k8s.io,那采集不到是预期行为。如果你写的是 custom 或者 external,先看对应的 API 端点是否返回需要的数据。
kubectl get `--raw` /apis/custom.metrics.k8s.io/v1beta1
kubectl get `--raw` /apis/external.metrics.k8s.io/v1beta1如果返回空列表或 404,说明适配器没有注册该指标,问题不在 kubelet,而在指标链路后段。
检查 kubelet 是否正确识别 GPU 并上报扩展资源
kubelet 通过 device plugin 将 GPU 作为扩展资源上报,HPA 虽然不直接读这个资源,但后续调度和指标绑定都需要节点有 nvidia.com/gpu 的 capacity。
kubectl describe node <node> | grep -A5 Capacity没有 nvidia.com/gpu 时,先看 device plugin 是否运行,再看 kubelet 是否禁用了 DevicePlugins feature gate。通常修改 /var/lib/kubelet/config.yaml 里的 featureGates 后重启 kubelet。
注意:即便正确上报资源,kubelet 的 /stats/summary 和 /metrics/resource 也不包含显存使用量。所以不要把采集希望放在 kubelet 上。
kubelet metrics 端点不含显存,准备好独立 exporter
要让显存进入 Prometheus,需要额外部署 exporter。NVIDIA 环境一般用 DCGM exporter,暴露类似 dcgm_memory_used_bytes 的指标。下面的 DaemonSet 是部署骨架:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: dcgm-exporter
spec:
selector:
matchLabels:
app: dcgm-exporter
template:
metadata:
labels:
app: dcgm-exporter
spec:
restartPolicy: Always
containers:
- name: dcgm-exporter
image: nvidia/dcgm-exporter:3.x
ports:
- containerPort: 9400
env:
- name: DCGM_EXPORTER_INTERVAL
value: '5000'确认 exporter 能暴露指标后,再配置 Service 和 Prometheus 采集。通常还需要在 ServiceMonitor 中指定 namespace 和 label 匹配。
把显存指标接入 HPA 的配置示例
通过 prometheus-adapter,把 exporter 的指标映射成 custom.metrics.k8s.io 的指标。以下是一段适配规则,关键是 seriesQuery 和 metricsQuery 要能匹配到 Pod 维度。
rules:
- seriesQuery: 'dcgm_memory_used_bytes'
resources:
overrides:
namespace:
resource: namespace
pod_name:
resource: pod
name:
matches: '^(.*)$'
as: 'gpu_mem_used'
metricsQuery: 'dcgm_memory_used_bytes{<<.LabelMatchers>>}'然后 HPA 可以这样写:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vllm-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vllm-deployment
metrics:
- type: Pods
pods:
metric:
name: gpu_mem_used
target:
type: AverageValue
averageValue: 8Gi如果你的指标按节点而不是 Pod 暴露,可能需要改用 External 类型,并在 external 块里指定 metricName 和 metricSelector。还要确认 exporter 的 pod 标签能被 adapter 识别,否则指标匹配不到目标 Pod。
验证与排障
- 先在 Prometheus 里查询 dcgm_memory_used_bytes,确认指标存在且有 namespace/pod 标签。
- 再查 adapter 是否拉取到指标:kubectl get `--raw` /apis/custom.metrics.k8s.io/v1beta1,看看是否有 gpu_mem_used。
- 最后看 HPA 状态:kubectl describe hpa vllm-hpa。如果显示 <unknown> 或 missing series,优先查 adapter 的 metricsQuery 和 HPA 的 metric 名是否一致。
- 如果指标长期没更新,检查 DCGM exporter 的抓取周期和 Prometheus 滚动窗口,指标更新慢会导致 HPA 判定延迟,但不属于 kubelet 配置问题。