3、K8s资源管理:Pod资源请求与限制、GPU资源的调度方式、Node资源监控

聊到vLLM部署,K8s资源管理这块儿,我敢说,是绝大多数人翻车的重灾区。你想想看,一个大模型推理服务,显存动不动就几十个G,CPU和内存也得跟上。K8s要是没管好,Pod说崩就崩,报错信息里全是OOMKilled或者GPU调度失败。嗯,今天咱们就把这块儿彻底捋清楚。

3.1 Pod资源请求与限制:别让Pod饿着,也别让它撑着

在K8s里,每个Pod都可以声明两样东西:requests(请求)和limits(限制)。

  • requests:告诉调度器,我这个Pod至少需要这么多资源。调度器会找一个能满足requests的Node来放你。
  • limits:告诉Kubelet,我这个Pod最多能用这么多资源。超过这个数,要么被限流(CPU),要么被干掉(内存)。

我个人习惯,对于vLLM这种推理服务,requests和limits最好设成一样。为什么?因为推理服务对延迟敏感,如果CPU被限流,推理速度会忽快忽慢,用户体验极差。内存更不用说了,一旦超过limits,Pod直接OOMKilled,服务就断了。

核心原则:对于vLLM推理Pod,建议requests == limits,避免资源争抢导致的性能抖动。

来看一个实际的Pod YAML示例:

apiVersion: v1
kind: Pod
metadata:
  name: vllm-inference-pod
spec:
  containers:
  - name: vllm
    image: vllm/vllm-openai:latest
    resources:
      requests:
        memory: "32Gi"
        cpu: "8"
        nvidia.com/gpu: 1
      limits:
        memory: "32Gi"
        cpu: "8"
        nvidia.com/gpu: 1
    args: ["--model", "meta-llama/Llama-2-7b-chat-hf", "--tensor-parallel-size", "1"]

这里我特意把GPU也写进了requests和limits。注意,GPU资源只能设置limits,不能设置requests?其实K8s 1.8之后,GPU资源是支持requests的,但实际调度时,GPU的requests和limits必须相等,否则调度器会报错。我在项目中遇到过有人把GPU requests写成0.5,结果Pod一直Pending,调度器日志里明确写着「Invalid GPU resource quantity」。

避坑指南:我曾经见过一个团队,把CPU requests设得很低,limits设得很高,想着「弹性利用」。结果vLLM推理时CPU被打满,但K8s认为你只请求了2核,所以不会给你更多CPU时间片,推理速度直接掉到原来的1/4。嗯,这就是典型的「省资源反被资源误」。

3.2 GPU资源的调度方式:不只是加个nvidia.com/gpu那么简单

GPU调度,说白了就是让K8s知道「哪个Node上有GPU,并且能分配给Pod」。这里涉及两个关键组件:NVIDIA Device PluginNode Feature Discovery

NVIDIA Device Plugin 是必须装的。它会把Node上的GPU资源上报给Kubelet,这样你才能在Pod里声明 nvidia.com/gpu: 1。安装方式很简单,用DaemonSet部署就行:

kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.0/nvidia-device-plugin.yml

装完之后,你可以用 kubectl describe node 看看GPU资源是否被识别:

Capacity:
  nvidia.com/gpu: 4
Allocatable:
  nvidia.com/gpu: 4

看到这个输出,说明GPU已经被K8s管理起来了。

但这里有个坑:GPU的调度是整卡调度的。也就是说,你声明 nvidia.com/gpu: 1,K8s会给你一整张GPU卡。如果你想要一张卡上的部分显存?抱歉,原生K8s不支持。这时候就需要用到 MIG(Multi-Instance GPU) 或者 vGPU 技术了。

注意:MIG只支持A100、H100等高端卡。如果你用的是V100或T4,MIG用不了。这时候可以考虑NVIDIA的Time-Slicing方案,但性能会有损失。

我个人建议,对于vLLM推理,尽量用整卡调度。因为vLLM本身支持张量并行(Tensor Parallelism),你可以把一个大模型切到多张卡上。比如用2张A100跑一个70B的模型,比用1张卡跑要稳得多。

3.3 Node资源监控:别等崩了才看日志

资源监控这事儿,我吃过不少亏。以前有个项目,vLLM服务跑得好好的,突然有一天开始频繁OOM。查了半天,发现是Node上其他Pod把内存吃光了,vLLM被挤死了。从那以后,我养成了一个习惯:监控Node资源,比监控Pod更重要

K8s自带的监控工具有 kubectl top nodekubectl top pod,但说实话,只能看个大概。真正要深入分析,得用 Prometheus + Grafana 这套组合拳。

先看几个关键指标:

指标名 含义 告警阈值建议
node_memory_MemAvailable_bytes 节点可用内存 < 10% 总内存时告警
node_cpu_seconds_total CPU使用率 > 80% 持续5分钟告警
nvidia_gpu_memory_used_bytes GPU显存使用量 > 90% 时告警
nvidia_gpu_utilization GPU计算利用率 > 95% 时关注

这里我特别想强调一下 nvidia_gpu_memory_used_bytes 这个指标。vLLM在加载模型时,会一次性把模型参数和KV Cache都加载到显存里。如果显存不够,vLLM会直接报 CUDA out of memory。所以,我建议在部署前,先用 nvidia-smi 看看Node上的显存总量,再根据模型大小估算一下。

一个小技巧:用 kubectl describe node 查看GPU的Allocatable数量,再结合 nvidia-smi 查看实际显存。有时候K8s上报的GPU数量和实际物理卡数不一致,我就遇到过Device Plugin挂了但K8s还显示有GPU的情况。

最后,放一张我画的资源管理流程图,帮你理清整体逻辑:

K8s GPU资源管理核心流程 用户/运维 vLLM Pod K8s Scheduler GPU Node Node内部组件 Kubelet NVIDIA Device Plugin GPU (nvidia.com/gpu) 资源监控 Prometheus + Grafana kubectl top node/pod 资源状态上报 用户操作 Pod 调度器 Node 监控 反馈/上报

这张图展示了从用户提交Pod到最终调度到GPU Node的完整流程。注意看虚线部分——监控系统会持续从Node拉取资源数据,一旦发现资源不足,就要及时调整Pod的requests和limits,或者扩容Node。

再次提醒:GPU资源是独占的,不要想着「超卖」。我曾经见过有人把GPU limits设成2,但Node上只有1张卡,结果Pod一直Pending,调度器日志里全是「Insufficient nvidia.com/gpu」。嗯,这种低级错误,排查起来真的很浪费时间。

好了,K8s资源管理这块儿,核心就是三件事:合理设置requests和limits确保GPU调度组件正常工作持续监控Node资源。把这三点做到位,vLLM部署的稳定性至少能提升80%。剩下的20%,就是模型本身和推理参数调优了。


专注资料整理