文章

LLM Serverless GPU 冷启动生产实战:用权重预取、Warm Pool 与 Scale-to-Zero 平衡成本和 TTFT

本文从 LLM Serverless GPU 推理的冷启动问题入手,深入讲解权重预取、镜像预热、Warm Pool、Scale-to-Zero、TTFT 监控和上线门禁,帮助工程团队在 GPU 成本与首 token 延迟之间建立可控的精细化平衡。

背景:冷启动不是”启动慢”这么简单

把 LLM 推理做成 Serverless GPU 形态,表面上很诱人:没有流量时缩到 0,流量来了再拉起 GPU worker,按请求或活跃时长付费。对于内部工具、低频 Agent、批量摘要、长尾微调模型和试验性模型,这种模式能显著减少闲置 GPU 成本。

LLM Serverless GPU 冷启动生产实战:用权重预取、Warm Pool 与 Scale-to-Zero 平衡成本和 TTFT

问题在于,LLM 的冷启动和普通 Web 函数不同。普通函数冷启动通常是容器启动、依赖加载、连接初始化;LLM 冷启动还要处理:

  • 模型权重从远端对象存储、镜像层或本地缓存读取;
  • checkpoint 反序列化和 tensor shape 解析;
  • 权重从 CPU 内存、NVMe 或网络存储搬到 GPU 显存;
  • 推理引擎初始化 CUDA graph、KV cache 预分配、tokenizer 和 serving runtime;
  • 第一个请求进入队列后,要等待 worker 进入 ready 状态,才能开始 prefill 并返回首 token。

所以冷启动最终会体现在 TTFT(Time To First Token) 上。用户看到的不是”容器已经启动”,而是”第一段回答什么时候出现”。如果这个时间从几百毫秒变成几十秒,即使 TPOT 很好,交互体验也已经失败。

ServerlessLLM 论文把这个问题拆得很清楚:LLM checkpoint 很大,冷启动会被远端下载、加载、显存分配和 tensor 搬运放大;论文提出多层 checkpoint 加载、locality-aware scheduling 和 live migration 来降低启动延迟。生产系统不一定直接复现论文系统,但它指出了一个关键事实:LLM 冷启动治理的核心是权重本地性和启动路径缩短,而不只是 autoscaling 参数。

核心原理:从请求到首 token 的冷启动链路

一个冷启动请求通常经过下面几段路径:

request arrives → gateway / external queue → scheduler decides model placement
→ provision GPU worker → pull image / mount volume → load tokenizer and runtime
→ fetch or locate model weights → deserialize and copy weights to GPU memory
→ initialize inference engine → prefill first request → emit first token

这条链路里,最容易被低估的是 模型权重加载。即使镜像已经在节点上,权重也可能仍在远端对象存储、网络文件系统或共享卷中。对于几十 GB 到上百 GB 的模型,网络带宽、NVMe 带宽、CPU 反序列化、PCIe 搬运、GPU 显存分配都会影响 TTFT。

因此,生产上的设计目标不是”完全消灭冷启动”,而是把冷启动拆成几个可控变量:

  • 权重位置:远端对象存储、本地 NVMe、主机内存、GPU 显存;
  • 启动阶段:镜像启动、runtime 初始化、权重加载、engine warmup;
  • 调度决策:是否选择已有权重缓存的节点,是否排队等待 warm worker;
  • 成本边界:保留多少 warm worker,哪些模型允许 scale-to-zero;
  • 用户体验:TTFT 上限、排队提示、异步降级和超时策略。

工程落地:把冷启动治理做成一套控制面

1. 模型分层:先区分热模型、温模型和冷模型

不要把所有模型都放进同一种 Serverless 策略。一个可落地的分层方式如下:

模型层级典型流量推荐策略目标
热模型高频在线交互最小副本 + 扩缩容保证低 TTFT
温模型周期性访问、业务时段明显小 Warm Pool + 预热计划控制尾延迟
冷模型长尾、低频、实验模型Scale-to-Zero + 队列降低闲置成本
大模型权重大、启动慢本地权重缓存 + locality scheduling避免远端重复下载

这一步很重要。很多团队一开始把 Serverless 当作统一省钱开关,结果把强交互入口也缩到 0,最后省下的是 GPU 空闲成本,损失的是核心用户体验。

2. 权重预取:让请求不要等完整下载链路

权重预取(weight prefetching) 的目标是把”请求到达后才开始搬权重”改成”调度器已经知道哪些节点该提前准备哪些模型”。

常见做法包括:

  • 构建模型热度表:按最近访问频次、业务时段、租户等级和模型大小计算优先级;
  • 在节点本地 NVMe 缓存常用 checkpoint,避免每次从对象存储下载;
  • 在扩容前预取权重到目标节点,而不是等 worker 启动后才拉取;
  • 对大模型使用分片权重和并行读取,避免单线程顺序加载;
  • 对灰度模型设置短 TTL,避免实验模型长期占据本地缓存。

Modal 的冷启动文档也强调,应尽早完成可以保存到磁盘的初始化工作,例如下载模型权重,并把初始化工作移出第一次请求路径。它还指出,进入 warm 阶段并不消除初始化成本,只是把成本挪到预热阶段。

3. Warm Pool:用小规模常驻换稳定 TTFT

Warm Pool 不是”永远保留所有模型副本”,而是保留一小部分已经 ready 的 worker,覆盖突发请求的第一波。

设计 Warm Pool 时建议明确 4 个参数:

model: qwen-xxb-instruct
min_warm_replicas: 1
max_warm_replicas: 4
warm_ttl_seconds: 900
cold_start_budget_ms: 8000

这几个参数背后的含义是:

  • min_warm_replicas:低于这个值就主动补 warm worker;
  • max_warm_replicas:避免预热过度,守住成本上限;
  • warm_ttl_seconds:模型多久无人访问后允许降温;
  • cold_start_budget_ms:如果从冷状态启动预计超过预算,就应该排队、降级或路由到共享模型。

BentoML 的 autoscaling 文档提到,scale-to-zero 可以在服务空闲时把副本缩到 0,并在请求到来后通过外部队列等待服务扩起。这类机制适合低频任务,但对于强交互 LLM,必须结合冷启动预算和排队体验使用。

4. Scale-to-Zero:不是默认值,而是分层策略

Scale-to-Zero 的适用场景很明确:

  • 内部低频工具;
  • 异步批处理任务;
  • 用户可接受等待的非实时生成;
  • 长尾模型、实验模型和临时租户模型;
  • 通过队列回调、Webhook 或任务状态页交付结果的场景。

不适合的场景也很明确:

  • Chatbot 首屏响应要求很高;
  • 语音 Agent、实时协作和 coding agent;
  • 高价值租户 SLA;
  • 业务高峰流量不可预测,但用户不能等待;
  • 大模型权重加载时间远高于用户可接受延迟。

正确做法是把 Scale-to-Zero 放到 策略引擎 中,而不是写死在部署配置里。例如:

policies:
  - match:
      tenant_tier: enterprise
      model_class: interactive
    scaling:
      min_replicas: 1
      warm_pool: enabled
      scale_to_zero: false
  - match:
      tenant_tier: free
      model_class: async_batch
    scaling:
      min_replicas: 0
      warm_pool: disabled
      scale_to_zero: true

5. Locality-aware scheduling:调度到”有权重”的节点

如果节点 A 已经缓存了某个模型的权重,而节点 B 需要从远端重新下载,那么调度器不应该只看 GPU 空闲率,还要看 checkpoint locality

一个实用的调度评分可以包含:

score = gpu_available_score + local_weight_cache_score + warm_worker_score
        - queue_delay_penalty - eviction_risk_penalty

这里的重点不是公式本身,而是要把”权重在哪里”纳入调度。ServerlessLLM 的核心思想之一就是利用 GPU 服务器上的多层存储,并基于 checkpoint locality 做启动时间优化调度。生产系统可以简化实现:先做本地 NVMe 缓存和模型热度评分,再逐步扩展到主机内存缓存、分片预取和迁移。

6. 外部队列:把冷启动等待变成可控等待

Ray Serve、BentoML 等框架都强调并发、队列和 autoscaling 的关系。LLM 冷启动下,队列不只是削峰工具,也是用户体验治理工具。

建议在 gateway 层记录:

  • 请求入队时间;
  • 选择的模型和版本;
  • 是否命中 warm worker;
  • 是否触发冷启动;
  • 权重加载开始/结束时间;
  • worker ready 时间;
  • TTFT;
  • 请求是否因冷启动超时而降级。

这样排障时才能回答:这次慢,是因为模型没有 warm worker,还是因为权重缓存 miss,还是因为 GPU worker 已启动但 engine warmup 慢。

适用场景

这套方案适合以下场景:

  1. 企业内部多模型平台:模型数量多、访问频率差异大,不可能全部常驻 GPU。
  2. 长尾微调模型服务:每个租户有自己的模型或 adapter,但访问频率不均匀。
  3. 异步生成任务:摘要、报告、批量改写、离线标注等任务可以排队。
  4. 试验模型平台:新模型上线前需要低成本试运行。
  5. 多区域成本优化:低峰区域允许 scale-to-zero,高峰区域保留 warm pool。

不适合把这套方案直接套到所有在线入口。对于核心交互业务,最小副本和预热成本通常是必要成本。

常见误区

误区一:只看 GPU 利用率,不看 TTFT

GPU 利用率高不代表用户体验好。冷启动期间 GPU 可能还没真正进入生成阶段,用户已经在等待。LLM Serverless 需要把 TTFT、cold_start_rate、warm_hit_rate 放到核心看板。

误区二:把权重放进镜像就万事大吉

把权重放进镜像可以减少运行时下载,但会带来镜像巨大、发布慢、回滚慢和节点缓存污染。更稳妥的方式是把镜像、权重和运行时配置分离,再通过本地缓存和预取策略控制加载路径。

误区三:Warm Pool 永远省不了钱

Warm Pool 确实有常驻成本,但它可以只保留少量热模型副本,并用 TTL、热度和租户等级控制。问题不在 Warm Pool,而在没有边界的 Warm Pool。

误区四:Scale-to-Zero 一定会伤害体验

对于异步任务和低频任务,Scale-to-Zero 是合理选择。关键是要在产品层提示排队状态,在工程层设置冷启动预算,在调度层避免每次都从远端重新加载权重。

上线检查清单

上线前至少检查以下项目:

  • 每个模型都有热度分层:hot / warm / cold。
  • 每个模型配置了 min_replicaswarm_ttlcold_start_budget_ms
  • 权重下载、权重加载、engine 初始化、TTFT 都有独立埋点。
  • 节点本地缓存有容量上限、淘汰策略和命中率指标。
  • 调度器能识别本地已有权重的节点。
  • Scale-to-Zero 入口有外部队列和用户可见状态。
  • 冷启动超时后有降级策略:排队、切换小模型、异步回调或明确失败。
  • 灰度新模型时不会清空已有热模型缓存。
  • 镜像、权重、tokenizer、runtime 版本可追踪。
  • 成本看板同时展示 warm pool 成本和冷启动带来的用户等待成本。

结语

LLM Serverless GPU 的难点不在”能不能缩到 0”,而在”从 0 回来时,用户要等多久、为什么等、是否值得等”。

生产系统需要把冷启动拆成可观测、可调度、可降级的链路:权重预取解决加载路径,Warm Pool 兜住交互体验,Scale-to-Zero 管住闲置成本,TTFT 指标把用户感知拉回工程决策中心。

当这些机制形成闭环后,Serverless GPU 才不只是省钱按钮,而是一个可以按模型、租户和业务场景精细调度的推理基础设施。

参考资料

常见问题

LLM 推理为什么比普通 Serverless 函数更怕冷启动?
因为冷启动不只是启动容器,还包括镜像拉取、依赖初始化、权重下载、权重反序列化、GPU 显存分配和推理引擎预热,模型越大,首 token 延迟越容易被权重加载主导。
Warm Pool 是不是越大越好?
不是。Warm Pool 本质是在用常驻 GPU 成本换低 TTFT,应该按业务 SLO、流量波峰、模型热度和冷启动预算设置,而不是简单维持所有模型常热。
Scale-to-Zero 适合所有 LLM 服务吗?
不适合。低频、异步或可排队任务适合 Scale-to-Zero;强交互、低延迟和高价值租户通常需要最小副本、预热副本或按模型热度分层。