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

问题在于,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 慢。
适用场景
这套方案适合以下场景:
- 企业内部多模型平台:模型数量多、访问频率差异大,不可能全部常驻 GPU。
- 长尾微调模型服务:每个租户有自己的模型或 adapter,但访问频率不均匀。
- 异步生成任务:摘要、报告、批量改写、离线标注等任务可以排队。
- 试验模型平台:新模型上线前需要低成本试运行。
- 多区域成本优化:低峰区域允许 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_replicas、warm_ttl、cold_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 才不只是省钱按钮,而是一个可以按模型、租户和业务场景精细调度的推理基础设施。
参考资料
- Modal Docs:Cold start performance — https://modal.com/docs/guide/cold-start
- BentoML Docs:Concurrency and autoscaling / Scale-to-Zero — https://docs.bentoml.com/en/latest/scale-with-bentocloud/scaling/autoscaling.html
- Ray Serve Docs:Autoscaling — https://docs.ray.io/en/latest/serve/autoscaling-guide.html
- Runpod Docs:Serverless Workers Overview — https://docs.runpod.io/serverless/workers/overview
- ServerlessLLM: Low-Latency Serverless Inference for Large Language Models — https://arxiv.org/abs/2401.14351