混合 LLM 工作负载的 GPU 调度:那个没人解决好的装箱问题
大多数运行 LLM 推理的 GPU 集群正在浪费 30% 到 50% 的可用算力。这并非因为工程师粗心,而是因为调度问题本身极为困难——而大多数团队首先想到的工具根本不是为此设计的。
标准做法是搭建 Kubernetes,为每个 Pod 申请完整的 GPU,然后让调度器自行处理。这对训练任务运行良好。但对于处理异构模型集合的推理场景,这种方式会悄悄摧毁利用率。一个运行三个不同 7B 模型且流量稀疏的集群,每个 GPU 的实际繁忙时间可能不足 15%,同时却处于完全"已分配"状态,拒绝调度任何新任务。
根本原因在于 Kubernetes 理解 GPU 的方式与 LLM 推理实际需求之间的错配。
Kubernetes 为何在这里失效
Kubernetes 将 GPU 视为原子单元。一个申请 nvidia.com/gpu: 1 的 Pod 会获得一整块物理 GPU——A100 上 40GB 的 HBM,H100 上 80GB——无论工作负载实际需要多少。这在工作负载是训练任务或批量推理作业、确实需要占用整个设备时是合理的设计决策。但在混合服务场景下,这种设计彻底失效。
具体失效方式如下:
无法感知 KV 缓存。 Kubernetes 调度器对运行中 Pod 的 KV 缓存占用率毫无感知。在 LLM 推理中,KV 缓存占用率是运行时最重要的资源约束——它决定实例能处理多少并发请求,以及新请求是进入队列还是直接失败。将一个长上下文请求路由到缓存利用率 90% 的 Pod,与路由到 10% 利用率的 Pod,会产生截然不同的延迟。调度器根本看不到这种区别。
模型加载延迟不可见。 启动一个新的推理 Pod 并非即时完成。仅容器镜像拉取就可能需要 2–5 分钟,因为镜像包含数 GB 的模型权重。之后还有 CPU 上的权重反序列化、传输到 GPU 内存,以及预热前向传播。生产环境中的冷启动总时间通常为 2–5 分钟。Kubernetes 会毫不犹豫地将 Pod 调度到一个让用户等待四分钟才能得到响应的节点上,因为它没有模型加载状态的概念。
批次合并不可能实现。 水平 Pod 自动扩缩(HPA)依据 CPU 和内存指标触发。而在 LLM 推理最需要扩容的时刻——队列深度积压、请求等待 token 生成——内存利用率可能只有 30%(空闲推理服务器的正常水平),CPU 利用率也很平稳。系统看起来一切正常,而用户却在经历无限制的队列延迟。
结果是:团队大量过度配置,为每个服务的模型保持热实例,用持续的 GPU 闲置成本来规避冷启动惩罚。这不是配置问题,而是 Kubernetes 所暴露的能力与 LLM 调度所需能力之间的根本性鸿沟。
