uvicorn 的 --workers 是启动时静态分配的进程数,不感知请求量、cpu 或内存变化,无法自动增减,因此不能用于 autoscaling;它仅设定了并发上限,与 kubernetes hpa 或云厂商自动扩缩容有本质区别。

为什么 uvicorn 自带的 --workers 不能当 autoscaling 用
因为 --workers 是启动时静态分配的进程数,不感知请求量、CPU 或内存变化,也不会自动增减。它只是预设的并发能力上限,和 Kubernetes 的 HPA 或云厂商的自动扩缩容完全不是一回事。
常见错误现象:uvicorn --workers 4 部署后,QPS 从 100 突增到 2000,响应延迟飙升但 worker 数纹丝不动;或者流量低谷期 4 个 worker 全空转,浪费资源。
- 适用场景:稳定流量、离线服务、本地调试——此时固定 worker 更可控
- 不适用场景:Web API、用户直连模型服务、突发流量明显的推理接口
- 性能影响:worker 过少会排队阻塞;过多则加剧 GIL 争抢(尤其 CPU 密集型模型)或内存溢出(如加载多个
transformers模型实例)
在 FastAPI + Uvicorn 中接入 kubernetes HPA 的关键配置点
核心不是改 Python 代码,而是让 K8s 能正确采集指标并触发扩缩容。Python 层只需暴露健康/就绪探针和资源用量信号。
常见错误现象:HPA 显示 unknown metrics,或 targetCPUUtilizationPercentage 一直为 <unknown></unknown>,扩缩容不触发。
立即学习“Python免费学习笔记(深入)”;
- 必须启用
metrics-server(不是heapster,后者已弃用) - Deployment 中需设置
resources.requests(如memory: 2Gi),否则 HPA 无法计算利用率 - 就绪探针(
readinessProbe)建议指向/health,避免把正在加载大模型的 Pod 提前纳入流量 - 避免在
livenessProbe中调用模型推理逻辑——可能因冷启延迟导致误杀
torch.cuda.memory_allocated() 不能直接喂给 HPA,但可以用来做内部过载保护
GPU 显存是模型服务最关键的瓶颈,但 K8s 原生 HPA 不支持自定义 GPU 指标(除非部署 prometheus-device-plugin-exporter + custom-metrics-apiserver)。所以更务实的做法是在 Python 层拦截过载。
常见错误现象:多个请求同时触发 model.generate(),显存爆掉抛出 CUDA out of memory,整个 worker crash。
- 用
torch.cuda.memory_allocated()在每次推理前检查,超过阈值(如 90%)直接返回503 Service Unavailable - 注意:该函数返回字节,别写成
> 0.9这种无单位比较 - 别依赖
torch.cuda.empty_cache()来“腾地方”——它不释放被缓存的显存,只释放未被引用的缓存块 - 搭配
asyncio.Semaphore限制并发请求数,比纯靠显存判断更稳定
用 ray serve 替代手写 autoscaling 逻辑的取舍
如果你的模型服务需要细粒度扩缩(比如按每秒请求数、按 pipeline 阶段、按模型版本隔离),ray serve 的内置 autoscaler 比自己搭 K8s + custom metrics 快得多,但代价是引入新运行时。
常见错误现象:用 ray.init(address="auto") 在容器里启动失败;或 serve.run() 后请求 502,查日志发现 RayActorError。
- 必须在容器中启动
ray start --head --port=6379(非默认行为),否则 Serve 控制面起不来 -
num_replicas设为"auto"时,依赖autoscaling_config中的min_replicas/max_replicas和lookback_window_s - 每个
Deployment默认共享一个 Python 进程,模型加载开销小;但若需多模型隔离,得用ray_actor_options={"num_gpus": 0.5}显式切分资源 - 兼容性注意:不支持 Windows 容器;与某些 C++ 扩展(如
flash-attn)偶发 ABI 冲突
真正难的不是配参数,是厘清你要扩的是什么:是并发连接数?是每秒请求数?还是 GPU 显存使用率?三者对应的监控点、响应延迟、扩缩节奏完全不同。没想清楚这点,所有配置都是临时止痛。










