生产 9:版本管理(SGLang / 模型 / API)
谁该读这一篇? 平台运维 / 上线负责人 —— 处理"换 SGLang 版本怎么平滑"、"模型怎么灰度切换"等问题。 前置阅读:
07-router.md、05-incident-playbook.md。 耗时: 25 分钟 学完能: 1. 区分 SGLang 版本、模型版本、API 版本; 2. 用 router + 双副本灰度切版本; 3. 处理 OpenAI API 兼容协议的版本差异; 4. 写一份发布检查表; 5. 知道权重热更新的几种路径(用于 RL / online learning)。
1. 三个独立的版本维度
| 维度 | 频率 | 影响 |
|---|---|---|
| SGLang 版本 | 月度 release | 性能 / 修 bug / 偶尔 breaking |
| 模型版本 | 不定(业务驱动) | 输出质量 / 兼容性 |
| API 版本 | 季度 / 半年 | 客户端代码兼容 |
三者独立但要协调。
2. SGLang 版本升级
升级前检查
- Read release notes(breaking changes / 新特性 / 废弃)。
- 跑 benchmark 对比新版 vs 旧版。
- 跑业务 eval 验证模型行为不变。
- 准备回滚预案。
灰度发布
将旧版和新版作为独立 worker pool,通过 Kubernetes Service、Gateway API 或
sgl-model-gateway 的 worker registry 分阶段切流。当前 router CLI 没有可依赖的
--worker-weights 合同;权重灰度应由流量入口完成,并为两个 pool 分别打上
版本标签以便比较指标。
配置变化
新版常加新参数。配置管理(Helm values / kustomize)要同步更新。
3. 模型版本切换
场景
- 新训练 checkpoint 发布。
- 切量化版本(BF16 → FP8)。
- 换基础模型(Llama-3.1 → Llama-3.3)。
切换方法
方案 A:滚动重启
简单粗暴。逐 Pod 改 --model-path,等加载完再起下一个。
代价:滚动期间容量减半,需 N+1 副本。
方案 B:影子副本
新模型起独立副本组,router 权重灰度切。 代价:双倍 GPU。 优势:随时回滚。
方案 C:热权重更新
模型架构不变(fine-tune),只换权重:
# SGLang Engine API(不重启)
curl -X POST localhost:30000/update_weights_from_disk \
-H 'Content-Type: application/json' \
-d '{"model_path": "/models/qwen-7b-v2"}'
weight_updater.py:update_weights_from_disk 实现。
4. RL / Online Learning 场景的权重同步
训练侧每 N step 给推理侧一份新权重。实际请求 schema 由
UpdateWeightsFrom*ReqInput 定义,部署前先查看 /docs 或
io_struct.py,不要把任意
tensor JSON 当作通用接口:
# 训练侧
import torch.distributed as dist
dist.send(weights, dst=inference_rank)
# 推理侧
curl -X POST localhost:30000/update_weights_from_distributed \
-H 'Content-Type: application/json' \
-d '{"names":["lm_head.weight"],"dtypes":["float16"],"shapes":[[32000,4096]],"group_name":"weight_update_group"}'
或者用 CUDA IPC(适合同机协同,句柄 schema 由服务端生成):
# 训练侧把 tensor 注册为 IPC handle
handle = tensor.share_memory_().get_handle()
# 推理侧通过 IPC handle 直接读
curl -X POST localhost:30000/update_weights_from_ipc \
-H 'Content-Type: application/json' -d '{"zmq_handles": {"GPU-...":"tcp://..."}}'
weight_updater.py:update_weights_from_ipc。
详见 srt/weight_sync/。
5. API 版本兼容
SGLang 对外有几套 API:
| API | 路径 | 用途 |
|---|---|---|
| OpenAI 兼容 v1 | /v1/chat/completions |
主流 |
| 原生 | /generate |
DSL / 内部 |
| Embedding | /v1/embeddings |
embedding |
| Anthropic 兼容 | /v1/messages |
Anthropic SDK |
| 控制 | /update_weights* /abort /flush_cache |
运维 |
字段兼容
OpenAI 协议偶尔加字段(如 response_format、reasoning_effort)。
SGLang 跟进相对快,但有时滞后。
客户端用最新 SDK 时记得验证 SGLang 版本是否支持。
弃用字段
不推荐:直接删字段。 推荐:保留字段 + 加 deprecation warning(log + metric)。
6. 灰度发布检查表
Pre-flight:
[ ] Release notes 已读 + 内部 review
[ ] 新版本镜像 build + push 完成
[ ] 业务 eval 通过
[ ] Benchmark 没退化
[ ] 回滚预案准备好
[ ] 监控面板 ready
Phase 1: 5% canary (24 hr)
[ ] 流量入口将新版比例设为 5%(Kubernetes/Gateway/router 配置)
[ ] 监控 canary 副本: TTFT / TPOT / cache hit / abort
[ ] 业务方观察输出质量
[ ] Postmortem 候选: 任何异常都记
Phase 2: 50% (24 hr)
[ ] 流量入口将新版比例设为 50%
[ ] 监控总体 SLO 不退化
Phase 3: 100%
[ ] 流量入口将新版比例设为 100%
[ ] 旧版本副本下线
[ ] 镜像 retention 至少 30 天
Post-release:
[ ] 写 release report
[ ] Update internal docs
7. 回滚
任何阶段发现 SLO 退化:
- 立刻在流量入口将比例切回旧版本。
- 通知 oncall 监控。
- 不立刻删 canary —— 留着诊断。
- 写 incident report。
回滚时间目标:< 5 分钟。
8. 配置版本化
把所有部署相关配置存 git:
ops/
├── helm/
│ ├── values-prod.yaml
│ └── values-staging.yaml
├── kubectl/
│ └── deployments/
└── scripts/
├── upgrade.sh
└── rollback.sh
每次变更 git tag,与镜像 tag 一一对应:
git tag deploy-2026-05-29-sglang-0.6
docker push mycompany/sglang:0.6-2026-05-29
9. 模型权重版本管理
模型 checkpoint 也要版本化:
s3://models/qwen-7b/v1.0/ # 初版
s3://models/qwen-7b/v1.1/ # fine-tune 增量
s3://models/qwen-7b/v1.2/ # bug fix
每个版本:
- 元信息(训练数据、checkpoint hash、训练 loss)。
- Eval 结果(GSM8K / MMLU / 业务 eval)。
- 切换记录(哪天切到这版,切回哪版)。
10. 兼容性矩阵
维护一张表:
| SGLang 版本 | 兼容 model | 兼容 client SDK | 兼容 router |
|---|---|---|---|
| 本教程基线 | 024a7a1031fd7d789076f90e203e3a51b9656cb3(2026-08-27) |
以 sglang/python/pyproject.toml 与 lockfile 为准 |
同一提交下的 experimental/sgl-router / sgl-model-gateway |
不要把历史 0.4/0.5/0.6 小版本号当作能力保证。发布前应锁定 SGLang commit/tag、
镜像 digest、模型 revision、router/gateway commit,并用实际端点做兼容性测试:
python -c 'import sglang; print(sglang.__version__)'
curl -fsS http://localhost:30000/health
curl -fsS http://localhost:30000/v1/models
curl -fsS http://localhost:30000/metrics >/dev/null
每次升级前看这张表。
11. 小结
- 三个独立版本维度:SGLang / 模型 / API。
- 升级走灰度 + canary,5% → 50% → 100%。
- 模型可以热权重更新(disk / distributed / tensor / IPC)。
- 配置 git 化,与镜像 tag 一一对应。
- 维护兼容性矩阵防交叉冲突。
12. 自检
- SGLang 版本升级前必做的 3 件事?
答案
(1) **Read release notes**:看 breaking changes(参数改名 / 删字段 / 行为变化)、新特性、性能 regression(有时新版个别场景慢)。 (2) **Benchmark 对比**:同硬件 / 同模型 / 同 dataset 跑 `bench_serving.py`,至少 TTFT / TPOT / Throughput 三个指标不退化。 (3) **业务 eval**:用业务私有 eval set 跑两版本,输出质量 / 业务指标不退化(特别是受约束输出、tool calling 行为)。 还有第 4 件强烈推荐:(4) **回滚预案准备**:旧版本镜像保留、Helm values 备份、Router weight 切换脚本就绪。出问题时 5 分钟内能回滚。 不做的话:上线发现 regression 时手忙脚乱、决策迟缓、用户感知差。- 热权重更新有 4 种路径,哪个最快?
答案
(1) `update_weights_from_disk`:磁盘读 safetensors → 反序列化 → CPU → GPU。慢(10s-几分钟,看模型大小)。 (2) `update_weights_from_distributed`:训练侧通过 NCCL broadcast 给推理侧。中速(GPU 间 IB / NVLink,几秒)。 (3) `update_weights_from_tensor`:直接传 torch.Tensor 对象(同进程)。快(仅 device-device copy)。 (4) `update_weights_from_ipc`:CUDA IPC handle 共享同 GPU 内存。**最快**(零拷贝,毫秒级,因为不真复制,是引用)。 排序:IPC > tensor > distributed > disk。 RL online learning 场景一般用 IPC(训练 worker 和推理 worker 同节点);多机训练时用 distributed;冷启动 / 模型切换用 disk。- 模型切换为什么推荐影子副本而非滚动重启?
答案
滚动重启:逐 Pod 改 `--model-path` 重启 → 滚动期间容量减半(已下线的 Pod + 新 Pod 还在 capture cuda graph)→ 需要 N+1 副本扛压力。 影子副本:新模型起独立副本组(双倍资源),router 灰度切流量 → 老副本一直能用,验证完一键切换。 影子的优势: (a) **零容量损失**:滚动期间无能力下降。 (b) **随时回滚**:流量切回老副本秒级生效。 (c) **AB test**:可以对比两版本的业务指标。 (d) **金丝雀**:按流量入口能力选择 1%/5% → 50% → 100% 的渐进切换;这些不是 router CLI 参数。 代价:双倍 GPU 几小时 - 几天(视验证周期)。对核心业务这个成本完全可接受。 滚动重启只用于:(a) 资源极紧无法双倍;(b) 测试 / 内部环境。- 客户端 SDK 比 SGLang 新可能出现什么问题?
答案
openai SDK 跟随 OpenAI API 协议演进,会先于 SGLang 加入新字段: (1) **新字段被忽略**:客户端发 `response_format: {type: "json_schema"}`,老 SGLang 不识别,按 free-form gen 处理;输出可能不符合 schema。 (2) **行为差异**:新 SDK 默认开 `stream_options.include_usage`,老 server 不返回 usage 字段,客户端报错。 (3) **404 端点**:客户端调 `/v1/responses`(新 API),server 没注册路由,404。 (4) **类型不匹配**:新 SDK 用 typed dataclass 接收响应,server 返回字段缺失导致 pydantic 校验失败。 解决: - 客户端和服务端一起做 endpoint contract test,并在 lockfile 中固定经过验证的 SDK 版本。 - 维护兼容性矩阵([§10](#10-兼容性矩阵))。 - 升 SGLang 前先在 staging 用最新客户端跑完整 API 测试。- 回滚时为什么不立刻删 canary?
答案
保留用于诊断: (1) **现场保留**:canary 的容器、日志、metric、core dump 都还在;删了就丢了;重现这种 regression 可能要花几天。 (2) **附加测试**:在 canary 上跑特定输入复现问题,不影响生产。 (3) **变量隔离**:canary 是出问题的版本,停接流量但保留,避免"问题不见了"误判已修复。 (4) **postmortem 证据**:写报告时引用 canary 当时的状态。 做法:rollback 时把流量入口切回 stable,但 canary Pod 不删;postmortem 完成、根因明确后再删(通常 1-7 天)。 K8s 配 PDB 防止 canary 被自动清理;标 `app.kubernetes.io/role: canary-retained` 让运维知道。13. 下一步
05-incident-playbook.md— 出问题时的 runbook。09-advanced-features/— 高级功能进阶。- 源码:
srt/model_executor/model_runner.py的update_weights_*方法、srt/weight_sync/。
上游源码:
sglang/python/sglang/srt/model_executor/model_runner_components/weight_updater.py。