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 Plugin 和 Node 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 node 和 kubectl 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的情况。
最后,放一张我画的资源管理流程图,帮你理清整体逻辑:
这张图展示了从用户提交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%,就是模型本身和推理参数调优了。