预计阅读 7 分钟

生产 9:版本管理(SGLang / 模型 / API)

谁该读这一篇? 平台运维 / 上线负责人 —— 处理"换 SGLang 版本怎么平滑"、"模型怎么灰度切换"等问题。 前置阅读: 07-router.md05-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 验证模型行为不变。
  • 准备回滚预案。

灰度发布

# 用 router 权重灰度
spec:
  args:
    - --worker-urls
    - http://sglang-v0_5:30001
    - http://sglang-v0_6_canary:30001
    - --worker-weights
    - "0.95,0.05"     # 5% canary

观察 canary 的 SLO 指标 24 小时,没问题再 10% → 50% → 100%。

配置变化

新版常加新参数。配置管理(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 \
    -d '{"model_path": "/models/qwen-7b-v2"}'

model_runner.py:update_weights_from_disk 实现。


4. RL / Online Learning 场景的权重同步

训练侧每 N step 给推理侧一份新权重:

# 训练侧
import torch.distributed as dist
dist.send(weights, dst=inference_rank)

# 推理侧
curl -X POST localhost:30000/update_weights_from_distributed \
    -d '{"name": "lm_head.weight", "dtype": "float16", "shape": [...]}'

或者用 CUDA IPC(最快):

# 训练侧把 tensor 注册为 IPC handle
handle = tensor.share_memory_().get_handle()
# 推理侧通过 IPC handle 直接读
curl -X POST localhost:30000/update_weights_from_ipc -d '{"handle": "..."}'

model_runner.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_formatreasoning_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)
[ ] Router weight 5%
[ ] 监控 canary 副本: TTFT / TPOT / cache hit / abort
[ ] 业务方观察输出质量
[ ] Postmortem 候选: 任何异常都记

Phase 2: 50% (24 hr)
[ ] Router weight 50%
[ ] 监控总体 SLO 不退化

Phase 3: 100%
[ ] Router weight 100%
[ ] 旧版本副本下线
[ ] 镜像 retention 至少 30 天

Post-release:
[ ] 写 release report
[ ] Update internal docs

7. 回滚

任何阶段发现 SLO 退化:

  1. 立刻 router weight 切回旧版本。
  2. 通知 oncall 监控。
  3. 不立刻删 canary —— 留着诊断。
  4. 写 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
0.4.x Qwen2.5 / Llama-3.1 openai>=1.30 sgl-router>=0.2
0.5.x + DeepSeek-V3 / Qwen3 openai>=1.45 sgl-router>=0.3
0.6.x + GPT-OSS openai>=1.50 sgl-router>=0.3

每次升级前看这张表。


11. 小结

  • 三个独立版本维度:SGLang / 模型 / API。
  • 升级走灰度 + canary,5% → 50% → 100%。
  • 模型可以热权重更新(disk / distributed / tensor / IPC)。
  • 配置 git 化,与镜像 tag 一一对应。
  • 维护兼容性矩阵防交叉冲突。

12. 自检

  1. 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 时手忙脚乱、决策迟缓、用户感知差。
  1. 热权重更新有 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。
  1. 模型切换为什么推荐影子副本而非滚动重启?
答案 滚动重启:逐 Pod 改 `--model-path` 重启 → 滚动期间容量减半(已下线的 Pod + 新 Pod 还在 capture cuda graph)→ 需要 N+1 副本扛压力。 影子副本:新模型起独立副本组(双倍资源),router 灰度切流量 → 老副本一直能用,验证完一键切换。 影子的优势: (a) **零容量损失**:滚动期间无能力下降。 (b) **随时回滚**:流量切回老副本秒级生效。 (c) **AB test**:可以对比两版本的业务指标。 (d) **金丝雀**:1% → 50% → 100% 渐进切。 代价:双倍 GPU 几小时 - 几天(视验证周期)。对核心业务这个成本完全可接受。 滚动重启只用于:(a) 资源极紧无法双倍;(b) 测试 / 内部环境。
  1. 客户端 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 校验失败。 解决: - 客户端钉住兼容版本(`openai==1.45.x` 与 SGLang 0.5.x 配套)。 - 维护兼容性矩阵([§10](#10-兼容性矩阵))。 - 升 SGLang 前先在 staging 用最新客户端跑完整 API 测试。
  1. 回滚时为什么不立刻删 canary?
答案 保留用于诊断: (1) **现场保留**:canary 的容器、日志、metric、core dump 都还在;删了就丢了;重现这种 regression 可能要花几天。 (2) **附加测试**:在 canary 上跑特定输入复现问题,不影响生产。 (3) **变量隔离**:canary 是出问题的版本,停接流量但保留,避免"问题不见了"误判已修复。 (4) **postmortem 证据**:写报告时引用 canary 当时的状态。 做法:rollback 时把 router weight 切回 stable,但 canary Pod 不删;postmortem 完成、根因明确后再删(通常 1-7 天)。 K8s 配 PDB 防止 canary 被自动清理;标 `app.kubernetes.io/role: canary-retained` 让运维知道。

13. 下一步

上游源码:sglang/python/sglang/srt/model_executor/model_runner.py:1598+