
队列变长,指标触发,控制器提高副本数,调度器找到节点,进程启动,模型加载,探针就绪,最后才开始接流量。这是一条链。把第一步发生的时间写成“扩容完成耗时”,会在大促时高估可用容量。
两个循环和一个容易漏掉的等待段
KEDA ScaledObject 文档区分轮询间隔以及 HPA 参与的扩缩阶段。Kubernetes HPA 文档也说明自动扩缩是周期控制,默认控制循环间隔为 15 秒。写一个更小的 pollingInterval,不代表每隔这么久就能得到一批已经加载模型的 GPU Pod。
还要区分 desired、scheduled、ready 和真正可服务的吞吐。预热探针若只检查 HTTP 端口而不检查模型装载,ready 也会过早乐观。

一个容量估算里应保留哪些量
下面只算“活跃任务加排队任务”的粗略副本目标,不模拟 Kubernetes。所有输入是教学数字,不是测量值。
public class Demo {
static int desired(long queued, long active, int perReplica,
int min, int max) {
if (queued < 0 || active < 0 || perReplica <= 0 || min < 0 || max < min) {
throw new IllegalArgumentException();
}
long work = Math.addExact(queued, active);
long units = work / perReplica + (work % perReplica == 0 ? 0 : 1);
return (int)Math.max(min, Math.min(max, units));
}
static int gap(int desired, int ready) {
if (ready < 0) throw new IllegalArgumentException();
return Math.max(0, desired - ready);
}
public static void main(String[] args) {
int target = desired(65, 15, 4, 2, 30);
if (target != 20 || gap(target, 8) != 12) throw new AssertionError();
if (desired(0, 0, 4, 2, 30) != 2) throw new AssertionError();
if (desired(10000, 0, 4, 2, 30) != 30) throw new AssertionError();
if (gap(10, 12) != 0) throw new AssertionError();
System.out.println("target=20, ready=8, pending capacity gap=12");
}
}
保存为 Demo.java 后运行 javac --release 11 Demo.java、java Demo。模型请求长短不同,四个请求未必耗费同样算力。实际指标可能要改成排队 Token、等待时间分位数或模型分组工作量,并通过压测标定,而不是直接复制 perReplica=4。
把“扩不起来”也纳入降级
持续记录目标与就绪容量差、最老排队年龄、启动阶段耗时和扩容失败原因。指标过期时,不应因为读到旧的空队列就突然缩容。GPU 配额、镜像拉取和模型文件读取都可能成为独立瓶颈。
竞价节点还需要更保守的退出方案。AWS Spot 通知文档说明通知是尽力而为,休眠场景也不是统一的两分钟预留。不能把收到通知当成唯一的状态保存入口。
本文没有接入 KEDA、Kubernetes 或真实 GPU。算式只帮助暴露容量缺口;需要靠环境中的启动数据决定保底副本、排队上限和拒绝策略。
本地运行环境为 Java/javac 12.0.1,使用 --release 11 编译并执行了文中的检查;没有安装新依赖。该结果只覆盖示例列出的逻辑,不代表真实模型、浏览器或生产链路已经验证。
本文剖析KEDA与Kubernetes HPA协同扩缩容时的关键瓶颈:从指标触发到真正可服务存在多阶段延迟,包括调度、Pod启动、模型加载及就绪探针校验。强调不能将desired副本数等同于可用算力,需区分scheduled/ready/可服务状态;指出GPU场景下镜像拉取、模型加载、竞价节点通知不确定性等独立瓶颈,并建议将‘扩不起来’纳入降级策略,通过持续监控目标与就绪容量差、排队年龄、启动耗时等指标优化容量规划。

283

被折叠的 条评论
为什么被折叠?



