外观
Serverless 推理:AWS Lambda 部署与冷启动攻坚
一句话定义:Serverless 推理是把模型跑在"按调用计费、自动扩缩到零"的无服务器平台(如 AWS Lambda)上——没有常驻实例,来了请求才拉起,跑完就释放。
为什么值得动手:如果你的推理流量是"低频、突发、实验性"的,常驻 GPU 实例的钱就是白烧的——一个 A10 实例月租几千元,每天却只有几十次调用,成本利用率不到 1%。Serverless 把这些场景的成本降到"一次调用几分钱",还能自动扛住突发。本文用 AWS Lambda 容器镜像部署一个图像分类服务,重点攻克冷启动,最后给出与常驻服务的成本分界点。
一、Serverless 推理适用场景
先给结论,四类场景适合:
- 低频业务:每天几百~几千次调用,没有稳定流量曲线;
- 突发流量:秒杀、活动、批量任务峰谷差 100 倍以上;
- 实验性模型:验证期模型,随时废弃,不值得长期挂实例;
- 事件驱动:模型由消息/对象存储事件触发(图片上传即打标)。
不适用:稳定高 QPS 的核心链路(Lambda 并发上限 + 单实例吞吐低于常驻 GPU,QPS 一高反而贵且慢)、需要 GPU 的模型(Lambda 无 GPU,见第五节)、毫秒级超低延迟(冷启动 + 网络跳数多)。
场景判断框架与部署架构模式里"事件驱动/无服务器模式"对应。
二、架构与打包:Lambda 容器镜像
text
API Gateway ──▶ AWS Lambda(容器镜像)──▶ 模型推理 ──▶ 返回结果Lambda 支持自定义容器镜像(镜像 ≤ 10GB,模型和依赖全打进),比 ZIP 包(250MB 上限)适合带权重的模型。Dockerfile:
dockerfile
# 必须用 AWS 提供的 Lambda 基础镜像
FROM public.ecr.aws/lambda/python:3.11
WORKDIR /var/task
COPY requirements.txt .
# 注意:模型权重也在镜像里(模型不大时最省事)
COPY models/resnet18.pth models/
COPY app/ app/
# 自定义运行时入口指向 handler
CMD ["app.handler.lambda_handler"]python
# app/handler.py
import io
import torch
from PIL import Image
import torchvision.transforms as T
from torchvision.models import resnet18
# 全局单例:冷启动时加载一次,同一实例内的热请求复用
_model = None
def _get_model():
global _model
if _model is None:
model = resnet18(weights=None)
model.load_state_dict(torch.load("/var/task/models/resnet18.pth", map_location="cpu"))
model.eval()
_model = model
return _model
def lambda_handler(event, context):
import base64
body = event.get("body", "")
img_bytes = base64.b64decode(body)
transform = T.Compose([T.Resize(256), T.CenterCrop(224), T.ToTensor(),
T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
tensor = transform(Image.open(io.BytesIO(img_bytes)).convert("RGB")).unsqueeze(0)
with torch.inference_mode():
logits = _get_model()(tensor)
top5 = torch.topk(torch.softmax(logits, dim=1)[0], k=5)
return {"statusCode": 200,
"body": [{"score": float(s), "index": int(i)} for s, i in zip(top5.values, top5.indices)]}与 FastAPI 服务对照:同样的"模块级加载 + 单例模型"原则,但 Lambda 的并发模型是"实例数由平台扩缩",每个实例内只有一个并发请求在跑(默认),所以模块级加载的收益在实例复用期间——冷启动只付一次加载成本,随后几十秒内的请求都走热路径。
三、冷启动:头号敌人
冷启动时间 = 容器拉起 + 运行时初始化 + 模型加载。参考数字(ResNet18 CPU):
| 阶段 | 耗时 |
|---|---|
| 容器/运行时拉起(平台侧) | 300ms~2s |
| Python + PyTorch 导入 | 2~5s |
| 模型权重加载 | 0.5~2s |
| 冷启动合计 | 约 3~8s(热请求 < 100ms) |
四个手段,按成本从低到高:
- 预热(concurrency warm-up):定时器(如每 2 分钟一个 EventBridge 规则)打一个探活请求,让实例保持热状态——零成本,但没流量时实例也会被回收,只治标;
- Provisioned Concurrency(预留并发):提前 N 个实例就绪,冷启动从路径里消除——按预留量计费,把省下的钱又花回去一部分,适合"必须稳"的核心场景;
- 减少冷启动本身:用 Amazon Linux 2 类镜像、避免冷门依赖、模型从 S3 惰性加载(不随镜像、用
--mount-type挂载)或换用更快运行时(如把 CPU 模型换成 ONNX Runtime、用 Rust/Go 写 handler); - 函数超时与内存调大:Lambda 超时上限 900s(15 分钟),默认 3s 肯定不够模型加载;内存调到 1024MB 以上也提高 CPU 配额(内存与 CPU 成正比)。
冷启动雪崩
突发流量瞬间拉起几十个实例,每个实例都要跑一次 3~8 秒的冷启动——第一个请求的 P99 可能 8 秒+。缓解:预留并发只保最低水位、网关层对冷启动请求做降级提示、业务侧用异步(先返回 task_id 再回调结果)。别指望"压测时冷启动会自己消失"。
事件触发:把"请求-响应"改成"事件-完成"
低频推理的另一个常见形态是事件驱动——对象存储上传、消息队列到件即触发推理,不必等同步返回。Lambda 的 S3/事件触发天然支持:
python
# 图片上传 S3 即触发打标,结果写回另一个桶或数据库
import boto3
from PIL import Image
import io
s3 = boto3.client("s3")
def lambda_handler(event, context):
for record in event.get("Records", []):
bucket = record["s3"]["bucket"]["name"]
key = record["s3"]["object"]["key"]
obj = s3.get_object(Bucket=bucket, Key=key)
predictions = predict(obj["Body"].read()) # 复用第三节的 predict
s3.put_object(Bucket=bucket, Key=key.replace("uploads/", "tags/"),
Body=json.dumps(predictions))
return {"statusCode": 200}事件驱动的好处:没有网关请求方等着,冷启动的"第一个请求慢"不再伤用户体验,模型加载慢一点无所谓。这是 Serverless 推理最舒服的用法之一,与部署架构模式里的"事件驱动模式"一致。
异步长任务:先回 task_id,再回调结果
推理超过 Lambda 同步超时预算(网关 29s / 函数最长 900s)时,改成异步两步:请求进来只负责排任务(写 SQS/DB),立即返回 task_id;Step Functions 编排的工作流(甚至带重试、条件分支)随后执行推理,完成后写结果、通知业务方。成本上异步与同步几乎一样,但把超时和重试都工程化了——这套模式同样适合接批处理推理管道的轻量场景。
四、成本模型:Lambda vs 常驻 GPU
先给结论:QPS 低于 1~5 的推理,Lambda 通常比常驻 GPU 实例便宜一个数量级;QPS 稳定高于 5~10,常驻实例+自动扩缩开始反超。做一次具体估算:
Lambda 计费 ≈ 调用次数 × (内存GB × 时长秒) × 单价(当前约 $0.00001667/GB·s,另有每百万次 $0.20 请求费)。一个 1024MB、平均耗时 2s 的推理函数,单次调用约 1GB × 2s × 0.00001667 ≈ $0.000033,即约 0.00023 元/次。
| 方案 | 月成本(1 万次/月) | 月成本(300 万次/月) |
|---|---|---|
| AWS Lambda(1024MB,均耗 2s) | 约 $0.5 | 约 $150 |
| 常驻 CPU 实例(2 核 4G,常开) | 约 $30 | 约 $30(无限跑) |
| 常驻 GPU 实例(A10,常开) | 约 $700+ | 约 $700+ |
换算成 QPS 视角更直观:1 万次/月 ≈ 平均 0.004 QPS,300 万次/月 ≈ 平均 1.2 QPS。也就是说"平均 1 QPS 以下"的稳定业务,Lambda 在 300 万次/月这个量级仍然和常驻 CPU 实例打平;只有持续高于 1~5 QPS 的常流量,常驻 GPU 才真正省钱。成本估算别只算算力——常驻实例还要算维护、扩缩、闲置 GPU 的机会成本,Serverless 把这些都打包进单价了。
数据说明一切:低频下 Lambda 便宜 2~3 个数量级,高频下常驻实例摊薄到接近免费。拐点大约在"稳定 QPS ≈ 1~5"。完整容量与成本规划方法见性能优化与容量规划。
五、限制与规避
| 限制 | 数值 | 规避 |
|---|---|---|
| 内存上限 | 10240MB(10GB) | 模型 + 权重必须装进内存;大模型压缩/量化 |
| 超时上限 | 900s(15 分钟) | 长任务转批处理或 Step Functions 异步编排 |
| 镜像大小上限 | 10GB | 权重放 S3 按需拉取,或模型服务化拆开 |
| ZIP 包上限 | 250MB(解压后) | 用容器镜像(≤10GB) |
| 并发上限 | 区域级配额(默认 1000) | 预留并发 + 请求队列削峰 |
| GPU | 不可用 | 需要 GPU 就换容器平台(见下) |
| CPU 配额 | 内存越高 CPU 越多 | 推理密集任务加大内存档位 |
GPU 不可用是 Serverless 推理的最大边界:Lambda 无 GPU,超过几个 B 的模型、需要 GPU 实时推理的场景只能走带 GPU 的容器平台(AWS 的 SageMaker Serverless Inference、Google Cloud Run + GPU、阿里云函数计算 GPU 实例、自建 K8s + KEDA 按流量扩缩到 0)。这批平台计费同样是按调用/按扩缩,但保留了 GPU。
六、平台对比一句话定位
| 平台 | 一句话定位 |
|---|---|
| AWS Lambda | 生态最全、触发源最多,但无 GPU、限制最严 |
| Google Cloud Run | 容器即服务,自动扩缩到 0,支持 GPU(Beta),冷启动快 |
| 阿里云函数计算(FC) | 国内生态好,支持 GPU 实例与自定义运行时 |
| 腾讯云 SCF | 与腾讯生态集成(微信、COS 触发),轻量好上手 |
| 自建 K8s + KEDA | 完全可控、可带 GPU,代价是自己运维"到 0"与冷启动 |
选型原则:先确认要不要 GPU——要 GPU 就在 Cloud Run/FC/K8s 里选;不要 GPU 且重度依赖云生态,Lambda 最省心。平台决策框架见框架与平台怎么选。
常见坑与排查
| 坑 | 现象 | 排查 |
|---|---|---|
| 冷启动雪崩 | 突发时首个请求 5~10s | 预留并发保底 + 异步化,见第三节 |
| 默认超时 3s | 模型还没加载完就超时 | 调大 Timeout(模型加载类任务至少 30s) |
| 内存不足 OOM | 权重加载 OutOfMemoryError | 调内存档位;权重 S3 惰性加载;量化 |
| handler 报模块找不到 | Unable to import module | 容器镜像 WORKDIR/路径与 handler 声明一致 |
| 镜像超 10GB | 部署被拒 | 权重从 S3 拉取,别全打镜像 |
| 并发超配额 | TooManyRequestsException | 预留并发 + 请求队列(SQS)削峰 |
| GPU 模型放 Lambda | 运行报 no CUDA device | Lambda 无 GPU,换带 GPU 的容器平台 |
延伸阅读
- 部署架构模式 —— 无服务器/事件驱动模式在部署架构谱系中的位置
- 性能优化与容量规划 —— 成本模型、QPS 拐点与容量估算的完整方法
- FastAPI + Docker 在线服务 —— 常驻服务的对照实现,成本对比的另一端
- 服务化与推理 API —— Serverless 的 handler 同样是"服务化",接口设计原则通用
- MLOps 部署流水线 —— 模型打进镜像/从 S3 拉取的产物管理链路
- 批处理推理管道 —— 低频大流量场景的另一个更优解:批量跑完就释放
参考资料
- AWS Lambda 官方文档:https://docs.aws.amazon.com/lambda/latest/dg/welcome.html
- AWS Lambda 容器镜像:https://docs.aws.amazon.com/lambda/latest/dg/images-create.html
- Google Cloud Run 官方文档:https://cloud.google.com/run/docs
- 阿里云函数计算文档:https://help.aliyun.com/zh/fc/
- KEDA(K8s 自动扩缩到 0):https://keda.sh/docs/