Prefill 和 Decode 分离之后,真正棘手的是两侧的耦合:Router 必须在 Prefill/Decode 就绪后才能对外服务,扩缩容要保持两侧配比,升级不能让一个角色先变成孤岛。传统 StatefulSet/Deployment 把这些逻辑都丢给了外部脚本,而 sgl-project/rbg 把"一组角色 = 一个推理服务"建模成了 Kubernetes CRD,让 PD 分离部署有了一个可以整体操作的管理单元。
信息快照:2026-09-21,基于 RBG v0.7.0(
v1alpha2API)与 SGLang v0.5.9 示例。项目演进很快,生产前请核对官方文档。
RBG 解决什么问题
RBG 的核心抽象是 RoleBasedGroup:一个 CR 内声明多个 Role(如 router、prefill、decode),每个 Role 有独立的副本数、模板和生命周期策略。它带来三件 PD 分离场景里最需要的事:
- 启动顺序:
dependencies字段声明 Role 依赖,decode 等 prefill 就绪、router 等两侧就绪,避免 Router 把流量打到未初始化的引擎。 - 服务发现:控制器自动生成形如
<group>-<role>-<index>.s-<group>-<role>的确定性域名,Router 配置直接写死 Prefill/Decode 实例地址,不需要自己维护 headless Service 拼接逻辑。 - 整体运维:滚动更新、扩缩容、故障恢复都以 RBG 为单位协调,
CoordinatedPolicy还能控制多角色之间的maxSkew和推进节奏。
单机 PD 分离:最小可用拓扑
最小拓扑是三个 Role:Router(SGLang Model Gateway)、Prefill、Decode。以 Qwen3-0.6B 为例,核心配置如下:
apiVersion: workloads.x-k8s.io/v1alpha2
kind: RoleBasedGroup
metadata:
name: sglang-pd-inference
spec:
roles:
- name: router
replicas: 1
standalonePattern:
template:
spec:
containers:
- name: router
image: lmsysorg/sglang-router:v0.2.4
command:
- python3
- -m
- sglang_router.launch_router
- --pd-disaggregation
- --prefill
- "http://sglang-pd-inference-prefill-0.s-sglang-pd-inference-prefill:8000"
- --decode
- "http://sglang-pd-inference-decode-0.s-sglang-pd-inference-decode:8000"
- --host
- "0.0.0.0"
- --port
- "8000"
ports:
- name: http
containerPort: 8000
- name: prefill
replicas: 1
standalonePattern:
template:
spec:
containers:
- name: prefill
image: lmsysorg/sglang:v0.5.9
command:
- python3
- -m
- sglang.launch_server
- --model-path
- "Qwen/Qwen3-0.6B"
- --host
- "0.0.0.0"
- --port
- "8000"
- --disaggregation-mode
- "prefill"
- --tp-size
- "1"
resources:
requests:
nvidia.com/gpu: "1"
- name: decode
replicas: 1
standalonePattern:
template:
spec:
containers:
- name: decode
image: lmsysorg/sglang:v0.5.9
command:
- python3
- -m
- sglang.launch_server
- --model-path
- "Qwen/Qwen3-0.6B"
- --host
- "0.0.0.0"
- --port
- "8000"
- --disaggregation-mode
- "decode"
- --tp-size
- "1"
resources:
requests:
nvidia.com/gpu: "1"
完整示例见 pd-disagg-standalone.yaml,其中还包含 /dev/shm 的 Memory emptyDir(KV 传输的中间计算需要共享内存)、readiness/liveness 探针和 InPlaceIfPossible 滚动升级策略。
几个容易踩的坑:
- 共享内存必须配。PD 分离的 KV 传输依赖
/dev/shm,示例里给到 30Gi 的 Memory emptyDir。漏掉这个配置,大 context 下会先在共享内存上失败,而不是在 GPU 显存上。 - 探针初始延迟要给足。SGLang 加载模型权重需要时间,示例用 60 秒 readiness、120 秒 liveness。换成大模型时要按实际加载时间调整,否则会在启动阶段被反复重启。
- Router 地址是确定性拼接。
<name>-<role>-<index>.s-<name>-<role>这个格式来自 RBG 的服务发现机制,改了 RBG 名字就要同步改 Router 参数。
多机 Tensor Parallel:LeaderWorker 模式
Decode 侧需要更大 TP 规模时,用 leaderWorkerPattern 代替 standalonePattern。每个实例由 1 个 Leader + N 个 Worker 组成,RBG 注入环境变量完成分布式初始化:
- name: decode
replicas: 4
leaderWorkerPattern:
size: 2 # 1 leader + 1 worker,tp-size=2
restartPolicyConfig:
type: None
template:
spec:
containers:
- name: sglang
command:
- python3
- -m
- sglang.launch_server
- --disaggregation-mode
- "decode"
- --tp-size
- "2"
- --dist-init-addr
- $(RBG_LWP_LEADER_ADDRESS):6379
- --nnodes
- $(RBG_LWP_GROUP_SIZE)
- --node-rank
- $(RBG_LWP_WORKER_INDEX)
resources:
requests:
nvidia.com/gpu: "1"
示例 pd-disagg-leader-worker.yaml 的配置是 Prefill 2 实例 × TP4、Decode 4 实例 × TP2,两侧 GPU 总量相等(各 8 卡)。这体现了 PD 分离的一个基本约束:两侧算力配比要按负载特征调,Prefill 偏计算密集,Decode 偏显存带宽密集,长输出场景 Decode 侧通常要更多实例。
RBG_LWP_LEADER_ADDRESS、RBG_LWP_GROUP_SIZE、RBG_LWP_WORKER_INDEX 是控制器注入的拓扑变量,不需要自己写 initContainer 拼 IP。这比手搓 StatefulSet + 环境变量脚本可靠得多,也是 RBG 相对原生原语的核心价值。
KV 传输后端:Mooncake
SGLang 默认的 KV 传输走引擎内置通道,跨节点高并发场景建议换 Mooncake Transfer Engine。RBG 提供了现成示例,关键差异只在两个启动参数:
# Prefill 侧
- --disaggregation-mode
- prefill
- --disaggregation-transfer-backend
- mooncake
Decode 侧只需 --disaggregation-mode=decode,不需要 transfer backend 参数。完整示例见 sgl-pd-disagg-with-mooncake-te.yaml。
注意这个示例依赖外部部署的 Mooncake 服务(mooncake-store/standalone-mooncake-store.yaml),不是 sidecar 模式。生产环境里 Mooncake Master 的可用性和网络带宽是 PD 分离的新增故障域,要纳入监控。
灰度升级与扩缩容
RBG 的 InPlaceIfPossible 策略在只改镜像或资源限额时原地更新 Pod,不重建实例——这对 PD 分离很关键,因为重建一个 Decode 实例意味着丢掉它持有的 KV cache。
rolloutStrategy:
type: RollingUpdate
rollingUpdate:
type: InPlaceIfPossible
maxUnavailable: 1
inPlaceUpdateStrategy:
gracePeriodSeconds: 30
扩缩容通过 scalingAdapter 自动创建 RoleBasedGroupScalingAdapter CR,直接对接 HPA。CoordinatedPolicy 则用于多角色协同:控制 Prefill 和 Decode 扩缩容之间的 maxSkew,避免一侧先扩导致 KV 传输积压。
社区还有 rbg-planner,基于 ARIMA 负载预测做 SLA 驱动的自动扩缩容,按 TTFT/ITL 目标分别调整 Prefill/Decode 副本数,适合不想手写 HPA 指标规则的团队。
NVIDIA Dynamo 运行时
如果团队已经用 NVIDIA Dynamo 做推理编排,RBG 也提供了 dynamo/pd-disagg.yaml 示例:Processor(Dynamo frontend)做请求路由,Prefill/Decode 用 nvcr.io/nvidia/ai-dynamo/sglang-runtime 镜像。Dynamo 依赖外部 etcd 和 NATS 做服务发现,这比纯 SGLang Router 多两个运维组件,换来的是 Dynamo 生态的调度和加速能力(如 Model Express P2P 权重分发)。
与 LWS DisaggregatedSet 对比
如果已经读过本站基于 LeaderWorkerSet 的 PD 分离实践,你会关心两者怎么选。两者都解决"成组 Pod 管理",但抽象层级不同:
| 维度 | RBG(RoleBasedGroup) | LWS(DisaggregatedSet) |
|---|---|---|
| API 成熟度 | v1alpha2(v0.7.0),API 仍在 alpha 阶段 |
LWS v0.9.0,LeaderWorkerSet API 更早进入生态 |
| 角色建模 | 一个 CR 内声明多 Role,原生多角色拓扑 | DisaggregatedSet 组合多个 LWS,每个角色底层仍是一个独立 LeaderWorkerSet |
| 启动依赖 | dependencies 字段原生支持启动顺序 |
无内置依赖声明,靠 Pod 就绪顺序和外部编排 |
| 服务发现 | 确定性域名 <group>-<role>-<index>.s-<group>-<role> |
每个角色有独立 Service,revision-aware 路由需要按 label 成对发现 |
| 协同升级/扩缩容 | CoordinatedPolicy 控制 maxSkew 与推进节奏 |
无跨角色协调原语,滚动更新混版本风险需自行处理 |
| 原地更新 | InPlaceIfPossible 原生支持 |
Pod 重建为主,KV cache 随 Pod 丢失 |
| 生态集成 | Mooncake、Dynamo、rbg-planner 官方示例 | vLLM/SGLang 官方文档路径成熟,NIXL 传输示例完善 |
| 社区规模 | 较新,贡献者集中在 sgl-project 组织 | kubernetes-sigs 项目,社区和审计面更广 |
选 RBG 的场景:需要跨角色协调(配比扩缩容、协同灰度)、想用声明式方式表达启动依赖、或者计划用 rbg-planner 做 SLA 驱动自动扩缩。RBG 把这些 PD 分离特有的运维问题做进了 API。
选 LWS 的场景:追求稳定性和社区背书、已经在用 vLLM/SGLang 官方 LWS 部署路径、或者 KV 传输走 NIXL 且不需要跨角色协调原语。LWS 的 LeaderWorkerSet API 更成熟,出问题时可参考的实践更多。
一个务实的判断标准:如果你的痛点是"角色之间怎么协调"(启动顺序、配比、灰度),RBG 的抽象更贴合;如果痛点只是"多机 TP 怎么成组管理",LWS 更稳。两者不互斥——RBG 本身就借鉴并复用了 LWS 代码。
上线前检查清单
- GPU 节点已安装 CUDA 驱动,
nvidia.com/gpu资源可用 -
/dev/shmMemory emptyDir 已配置,大小按模型 context 和并发估算 - Router 的 Prefill/Decode 地址与 RBG 名称一致
- 探针初始延迟按模型加载时间调整
- 多机 TP 时确认节点间网络满足 KV 传输带宽(RDMA 优先)
- 使用 Mooncake 时,Mooncake 服务先于 RBG 部署并纳入监控
- 滚动更新策略确认
InPlaceIfPossible,避免不必要的 KV cache 丢失 - HPA 指标对接
RoleBasedGroupScalingAdapter,或评估 rbg-planner
PD 分离不是"把一个 Deployment 拆成两个"这么简单,它引入了角色间协调这个新问题域。RBG 的价值在于把这个问题域变成声明式 API:启动顺序、服务发现、协同升级、配比扩缩容都在一个 CR 里表达,而不是散落在脚本和运维约定里。如果你的 PD 分离部署还在靠 bash 脚本粘合 StatefulSet,值得试一次 RBG。