← 返回资讯

Serverless 推理方案对比:按请求付费,零运维跑大模型

2026-07-08

Serverless 推理是一种”按请求付费、流量为零时不产生费用”的大模型托管方案——你不需要管理 GPU 实例、不需要关心 CUDA 环境、不需要处理服务器宕机,只需要把模型部署上去,调用 API 就行。对于流量不稳定、峰谷差异大的场景,Serverless 推理可以比常驻 GPU 实例便宜数倍。

Serverless 推理 vs 常驻 GPU 实例

先理解核心差异再选方案:

维度Serverless 推理常驻 GPU 实例(vLLM 等)
计费方式按 token 或按请求时长按 GPU 小时(不管有没有请求)
冷启动存在(几秒到几十秒)无(服务常驻)
流量为零时成本继续计费
峰值并发扩容自动(有上限)手动扩容或提前预留
运维负担极低中(需维护 GPU 服务器)
适合流量特征低频/峰谷大稳定/高并发

冷启动是 Serverless 推理的主要劣势:当模型在一段时间无请求后被”冷却”,下一次请求需要重新加载权重到 GPU,耗时通常 10–60 秒,视模型大小而定。对于实时对话场景,冷启动是明显体验问题。

冷启动到底在等什么

第一次踩这个坑的人经常搞不清楚——明明日志显示请求已经进来了,为什么客户端还要等半天才收到第一个字?拆开看,一次冷启动大概经过三段:

  1. 调度 & 拉容器:平台先找一台空闲的 GPU 机器,把你的镜像(Docker 或平台自带的运行时)拉起来,这一步通常是秒级,镜像越大越慢。
  2. 加载权重到显存:这是大头。模型权重要从对象存储(S3 之类)或本地磁盘读到内存,再搬进 GPU 显存。7B 模型的权重文件大概 14GB(FP16),从普通网络存储读取,几秒到十几秒很正常;70B 模型权重上百 GB,冷启动动辄一两分钟,这也是为什么大模型很少有人直接上 Serverless 跑推理——冷启动成本随参数量线性甚至超线性增长。
  3. CUDA / 推理引擎初始化:vLLM、TensorRT 这类推理引擎首次加载时会做算子编译、KV cache 预分配,这一步也要占几秒。

看懂这三段就知道怎么”作弊”:Modal、RunPod 都提供 keep_warm(或叫 min containers / 预留实例)配置,本质是花钱让容器不冷却,牺牲掉”流量为零不计费”的优势换首字延迟。所以选 Serverless 之前先问自己一句——你到底是要”省钱”还是要”低延迟”,这两者在 Serverless 语境下是互斥的,鱼和熊掌很难兼得。

主流 Serverless 推理平台

Replicate

  • 模式:托管社区模型(Hub 上数万个模型)或上传自定义模型
  • 计费:按 GPU 秒计费,冷启动也计费
  • 优点:模型库极丰富,API 极简单,文档友好
  • 缺点:价格在 Serverless 平台中偏高,自定义模型需要打包 Cog 镜像
  • 适合:快速调用开源模型做原型,不介意冷启动
  • 模式:Python 原生 Serverless,代码即部署
  • 计费:按 GPU 秒计费,有免费额度(每月约 30 GPU 小时)
  • 优点:工程体验极佳,可以用普通 Python 函数定义推理逻辑,冷启动优化做得好
  • 缺点:需要会写 Python,不是纯”无代码”
  • 适合:有 Python 能力的开发者,想要最大灵活度的 Serverless 推理
import modal

app = modal.App("llm-inference")
image = modal.Image.debian_slim().pip_install("vllm")

@app.function(gpu="A100", image=image)
def run_inference(prompt: str) -> str:
    from vllm import LLM, SamplingParams
    llm = LLM(model="Qwen/Qwen2.5-7B-Instruct")
    outputs = llm.generate([prompt], SamplingParams(max_tokens=256))
    return outputs[0].outputs[0].text

这段代码有几个细节值得抠一下,不然照抄容易踩坑:

  • image = modal.Image.debian_slim().pip_install("vllm")——这是在构建阶段打镜像,vllm 这类依赖装一次会被缓存,不会每次冷启动都重新 pip install(新手常见误解,以为每次调用都要装包,那样冷启动几分钟都不够)。真正拖慢冷启动的是下一行 from vllm import LLM 里模型权重的加载,这也是为什么 import 语句写在函数体内而不是文件顶部——只有函数真正被调度到 GPU 容器里执行时才去加载权重,避免本地开发环境也去尝试初始化 GPU。
  • gpu="A100" 这个参数决定了计费单价和显存上限,7B 模型用 A100-40GB 绰绰有余,但如果你换成 32B 以上的模型还这么写,大概率会遇到 CUDA out of memory 报错,得改成 gpu="A100-80GB" 或者干脆上多卡(gpu="A100:2")。
  • 这个例子每次调用都重新 LLM(model=...) 实例化,等于每次都触发一次完整加载,仅适合演示。生产上会用 Modal 的 @app.cls + @modal.enter() 装饰器,把模型加载放到容器启动时执行一次,后续请求复用同一个已加载好的实例——这才是把冷启动成本摊薄的正确姿势。

RunPod Serverless

  • 模式:Docker 镜像 + Handler 函数定义,部署到 RunPod 的 GPU 池
  • 计费:按请求执行时长计费,冷启动不计费(等待阶段免费)
  • 优点:价格相对低,GPU 型号丰富,冷启动后的推理速度快
  • 缺点:需要写 Handler 代码,配置比 Replicate 复杂
  • 适合:有一定工程能力,追求低成本 Serverless

RunPod 的 Handler 长这样,理解了这个骨架基本就能自己改:

import runpod

def handler(job):
    prompt = job["input"]["prompt"]
    # 这里调用你已经加载好的模型对象做推理
    result = model.generate(prompt)
    return {"output": result}

runpod.serverless.start({"handler": handler})

这里的关键坑在于模型加载要放在 handler 函数外面(模块级别,进程启动时执行一次),而不是每次调用都在 handler 里重新加载——这是新手最容易搬错的地方,一旦模型加载写进了 handler 内部,等于把”冷启动”变成了”每次请求都冷启动”,价格和延迟直接爆炸。RunPod 的计费口径是”冷启动等待免费、真正跑推理才计费”,所以理论上你可以把模型设计得”重一点”(比如量化程度低、精度高),只要单次推理时间可控,账单不会因为加载慢而变贵;但客户端等待体验依然会差,这点和”计费”是两回事,别搞混了。

together.ai / Fireworks.ai

  • 模式:专注 LLM 推理的 API 服务,类 OpenAI 协议
  • 计费:按 token 计费(输入/输出分开定价)
  • 优点:无冷启动(常驻多实例),API 最简单,延迟低
  • 缺点:不支持自定义私有模型,只有平台提供的模型列表
  • 适合:直接调用开源模型 API,不需要私有化部署

各平台核心对比

平台冷启动自定义模型计费粒度适合技术水平
Replicate有(10–60s)支持(需 Cog)GPU 秒
Modal有(可优化到 < 10s)完全自定义GPU 秒
RunPod Serverless有(10–30s)支持(Docker)请求时长
together.ai不支持token低(直接调 API)
Fireworks.ai支持(微调后)token

适合 Serverless 的典型场景

  • 低频 API 调用:日均请求量不足以撑起一台常驻 GPU 实例的成本
  • 内部工具:只在工作时间使用,夜间/周末无流量,Serverless 省掉大量空跑成本
  • 峰谷差异大:白天高峰、晚上低谷,Serverless 自动伸缩
  • 原型验证:快速验证模型效果,不想花时间搭推理服务器

实际接入时会踩的几个坑

真上手接 Serverless 推理 API,光看文档是不够的,下面这几个报错和现象基本每个人都会遇到一遍:

请求排队甚至直接超时——冷启动的实例数量是有上限的(每个平台的 quota 不同),如果你的调用方一次性发了几十个并发请求,超出上限的部分会在队列里等,客户端表现就是响应时间忽长忽短,甚至触发你自己设的 HTTP 超时。排查思路:先去平台后台看这次调用是”排队中”还是”执行中”,如果长期排队,说明并发配额不够,要么加钱升配额,要么在客户端做限流(比如用信号量把并发压到平台承受范围内)。

429 Too Many Requests——和排队不同,这是平台明确拒绝了请求,通常是你打到了 QPS 上限。正确的处理方式是指数退避重试,而不是立刻重发:

import time
import random

def call_with_backoff(fn, max_retries=5):
    for attempt in range(max_retries):
        try:
            return fn()
        except RateLimitError:
            wait = (2 ** attempt) + random.random()
            time.sleep(wait)
    raise Exception("重试次数用尽,仍然被限流")

冷启动期间客户端连接被判定为”死连接”——很多 HTTP 客户端库默认超时是 10–30 秒,如果模型冷启动要 40 秒以上,客户端会先一步断开,你看到的错误往往是 ReadTimeout 而不是平台侧的任何报错,容易误判成”服务挂了”。解决办法很直接:把客户端超时设长(比如 90 秒),或者用支持流式响应(streaming)的接口——只要平台在冷启动完成后开始吐第一个 token,流式连接就不会被判定为空闲超时。

账单和预期对不上——尤其是 Replicate 这种”冷启动也计费”的平台,如果你的调用方式是”每次都新建一个容器”(比如没有复用模型实例,或者请求间隔恰好卡在冷却阈值上下),实际花费会比”理想情况下按 token 计费”贵出一截。粗算一笔账:假设单次冷启动占用 GPU 20 秒,稳定推理只需要 3 秒,如果你的请求频率恰好让每次都触发冷启动,实际付费时长是理想值的将近 7 倍。这也是为什么流量稍微上来一点之后,很多人会从纯 Serverless 切到”Serverless + 少量常驻实例兜底”的混合架构。

不适合 Serverless 的场景

  • 实时对话产品:冷启动延迟对用户体验影响大,常驻实例更合适
  • 高并发稳定服务:常驻 GPU 实例的边际成本更低,且延迟可控
  • 私有化/合规要求:Serverless 平台数据经过第三方,不满足数据不出域要求

常见问题

Serverless 推理能处理高并发吗?
可以,但有限制——平台会自动启动多个实例(冷启动),但每个平台都有并发上限。突发超高并发时可能出现排队。真正的高并发稳定场景,常驻 GPU 实例加 vLLM 更合适。

Serverless 推理和直接调 OpenAI/Claude API 有什么区别?
直接调用 API 是调用别人托管的闭源模型;Serverless 推理通常是在第三方平台上部署自己选择的开源模型,保留了模型选择和一定的定制空间,但不如直接调 API 简单。

Modal 的免费额度够做什么?
每月约 30 GPU 小时,7B 模型推理大约 30 × 3600s / 每请求 ~2s ≈ 5 万多次请求,足够做原型验证和小规模实验。这个估算的前提是模型常驻不冷启动——如果每次都触发冷启动重新加载权重,实际能跑的请求数会打个大折扣,具体够不够用,先按你的真实调用间隔粗算一遍再决定要不要升级付费额度。

怎么判断我的场景该用 Serverless 还是常驻实例?
给自己算一笔账最靠谱:先估出你一天的总调用量和调用时间分布,如果画出来的曲线大部分时间贴着零、只有零星几个尖峰,Serverless 基本稳赢;如果曲线一天到晚都有一定的底噪流量(比如从早八点到晚十点持续有请求),常驻一台小规格 GPU 实例的总成本大概率比”频繁冷启动”更划算——因为冷启动本身也是要花钱占用 GPU 秒的,流量一旦稳定到某个阈值以上,Serverless 的”按需付费”优势就被冷启动的重复开销吃掉了。没有一个万能的百分比阈值,实际部署前先拿两三天的真实/预估流量分别按两种计费方式估算一遍,谁便宜选谁。

能不能白天用 Serverless、晚上低峰用常驻实例反过来省钱?
方向反了——常驻实例是”不管有没有请求都计费”,应该反过来:稳定的白天高峰用常驻实例兜底(保证延迟和吞吐),流量稀疏的夜间/周末让 Serverless 接管长尾请求,零流量时段直接零成本。这也是不少团队从纯 Serverless 走向”常驻+Serverless”混合架构的真实路径:先用 Serverless 验证产品是否有稳定流量,流量起来后再补一层常驻实例兜底延迟敏感的核心场景,两条腿走路。


延伸阅读: