DisaggregatedSet 原理深挖:N 维协调滚动更新算法如何让 2-10 个角色同步发布

📅 2026/8/20 18:22:00
DisaggregatedSet 原理深挖:N 维协调滚动更新算法如何让 2-10 个角色同步发布
DisaggregatedSet 原理深挖N 维协调滚动更新算法如何让 2-10 个角色同步发布【免费下载链接】lwsLeaderWorkerSet: An API for deploying a group of pods as a unit of replication项目地址: https://gitcode.com/gh_mirrors/lws2/lwsDisaggregatedSet 是 LeaderWorkerSetLWS项目推出的高级 Kubernetes CRD专为分离式推理disaggregated inference场景设计一个 DisaggregatedSet 可以把 2-10 个角色如 prefill、decode、encode打包成单个部署单元并依靠内部的N 维协调滚动更新算法让所有角色齐步走式同步发布。本文深入拆解这套算法的数学原理、执行流程与工程实现带你理解为什么它能在多角色升级时既保持服务容量、又不会出现某个角色先升级导致版本错配的经典事故。为什么需要 DisaggregatedSet一次升级要动 2-10 个角色 LLM 推理的 prefill预填充计算密集型与 decode解码显存带宽密集型两个阶段计算特征差异巨大vLLM、SGLang 等框架因此支持把两者拆到不同 GPU 资源池上独立部署、独立扩缩容。但拆开部署也带来了新问题一次模型升级要同时更新 prefill 和 decode 两套资源手工协调极易出错某一边先升级、另一边还是旧版本会导致版本不匹配、推理结果异常升级期间还要保证流量不被打断。DisaggregatedSet 的答案很直接它不替代 LeaderWorkerSet而是编排多个 LeaderWorkerSet。每个 role 对应一个独立的 LWS 资源由一个统一控制器协调它们的创建、滚动更新与服务生命周期。这套设计思路的完整论证记录在 KEP-766 设计文档中。上图是 LeaderWorkerSet 的基础架构一个 Leader StatefulSet 管理多个 Worker StatefulSetDisaggregatedSet 正是在这一层层 LWS 之上再叠加多角色协调这一层控制逻辑。一条 YAML 定义多个角色DisaggregatedSet API 速览 DisaggregatedSet 的 API 非常克制核心就是roles列表apiVersion: disaggregatedset.x-k8s.io/v1 kind: DisaggregatedSet metadata: name: disaggregatedset-sample spec: roles: - name: prefill spec: replicas: 2 leaderWorkerTemplate: size: 1 leaderTemplate: { ... } workerTemplate: { ... } - name: decode spec: replicas: 2 leaderWorkerTemplate: size: 1 leaderTemplate: { ... } workerTemplate: { ... }完整示例见config/samples/disaggregatedset_v1_disaggregatedset.yaml三个角色的版本见config/samples/disaggregatedset_v1_3role.yaml。API 校验规则定义在api/disaggregatedset/v1/disaggregatedset_types.go保证了几个关键不变量✅ 角色数量最少 2 个、最多 10 个MinItems2, MaxItems10✅ 角色名必须全局唯一✅ 所有角色的副本数要么全为 0、要么全不为 0防止半套服务状态。控制器为每个角色生成独立的 LeaderWorkerSet命名规则为{DisaggregatedSet名}-{切片}-{revision哈希}-{角色名}例如my-inference-0-a1b2c3-prefill。其中revision 哈希由所有角色模板共同计算——只要任何一个角色的模板变化哈希就会改变从而保证所有角色必然一起进入新版本从机制上杜绝版本漂移。N 维协调滚动更新算法核心公式看懂同步发布的数学 这是整个 DisaggregatedSet 的灵魂。想象你有 N 个角色、每个角色有各自的旧版本副本initialOld和目标副本数target控制器需要把旧副本逐步替换为新副本同时满足每个角色各自的 surge 约束。算法采用线性插值离散化的方式源码见pkg/controllers/disaggregatedset/planner.gonewAtStep(i) ceil(i * target / totalSteps) // 扩容0 → target oldAtStep(i) initialOld - floor(i * initialOld / totalSteps) // 缩容initialOld → 0其中每个角色的批次大小与总步数batchSize maxSurge 如果 maxSurge 0否则 max(1, maxUnavailable) roleSteps ceil(max(initialOld, target) / batchSize) totalSteps 所有角色 roleSteps 的最大值totalSteps由最慢的角色决定这让所有角色共享同一个时间轴是N 维协调的关键。算法还遵循六条安全不变量解耦步进每一步只改变旧副本或新副本中的一边绝不两边同时动便于推理和排错N 维协调扩容按所有角色的min(step)对齐缩容按max(step)对齐保证齐步走协同排空任一角色排空到 0 时强制所有角色一起排空避免出现孤儿单角色Surge 约束任意时刻满足old new target maxSurge防止副本数超限先扩容后缩容先起新副本、再删旧副本容量永远不低于下限稳定性检查只有当ReadyReplicas Replicas新副本全部就绪后才进入下一步。实战推演5 个 prefill 2 个 decode 的同步滚动发布 以 KEP-766 中的经典例子推演prefill 从 5 到 5、decode 从 2 到 2纯模板升级所有副本都要替换maxSurge2, maxUnavailable1。先计算总步数batchSize 2maxSurge 优先prefill 需要ceil(5/2) 3步decode 需要ceil(2/2) 1步totalSteps max(3, 1) 3。于是理想的轨迹是步数新 prefill新 decode旧 prefill旧 decodei00052i12142i24221i35200实际执行时控制器每一轮协调reconcile会按tryScaleUp → tryProportionalDrain → tryForceDrain的顺序尝试三种动作最终用了7 轮才走完这 3 步理想轨迹因为 surge 阻塞时需要额外的强制排空轮次初始状态旧 52扩容新 prefill 2、新 decode 1按比例排空旧 prefill -1强制排空旧 prefill -1为下一次扩容腾出 surge 空间扩容新 prefill 2、新 decode 1按比例排空旧 prefill -1、旧 decode -1扩容新 prefill 1 到位最终排空旧副本全部清零。全程总副本数被严格限制在 surge 范围内任何时刻容量都没有跌破最小值——这就是同步发布的直观含义。这种用最慢角色决定节奏、每一步只动一边的设计正是 N 维协调滚动更新算法与普通单角色滚动更新的本质区别。上图展示了典型的分角色推理服务拓扑控制面Leader/Admin负责发布与生成请求多个角色Worker/Model Server各自承载不同阶段的计算——DisaggregatedSet 要做的就是保证这套多角色拓扑在任何一次升级中都保持版本一致。无状态控制器滚动更新中断也能安全恢复 ️一个很容易被忽视但极其重要的工程细节DisaggregatedSet 控制器是无状态的。它不保存任何内存中的滚动进度所有状态都从集群中已存在的资源推导。启动滚动更新时控制器先把旧 LWS 的当前副本数写入initial-replicas注解作为基线之后每次协调都从观察当前新旧副本数 → 用 planner 计算下一步 → 执行开始。这意味着控制器随时可以安全重启滚动更新会从断点继续不会重复或丢失步骤即使有人手动改动了副本数correctAbnormalState也会在下一次协调时把异常状态纠正回轨迹上每个动作都会产生RollingUpdateStarted / RollingUpdateCompleted / ScalingUp / ScalingDown等 Kubernetes 事件升级全程可观测。核心执行逻辑在pkg/controllers/disaggregatedset/executor.go算法决策集中在pkg/controllers/disaggregatedset/planner.go的ComputeNextStep每个角色 revision 对应的 headless Service 编排在pkg/controllers/disaggregatedset/service_manager.go命名形如my-llm-a1b2c3-prefill-prv让负载均衡器能按 revision 统计各角色副本数做流量切分。进阶玩法slices 切片让整套拓扑按域复制 如果一套 prefill/decode 拓扑不够还需要在多个 GPU 域如 NVL72 机柜上各跑一套KEP-846 引入的spec.slices字段解决了这个问题一个整数即可把整套角色拓扑复制成 N 份独立副本每份切片是一个独立的滚动更新域——模板升级时每个切片按自己的时钟滚动但始终保证每个切片内部是完整同版本的一组角色。设计细节见keps/846-disaggregatedset-slices/README.md。快速上手体验 DisaggregatedSet 安装 LeaderWorkerSet含 DisaggregatedSet控制器应用示例清单kubectl apply -f config/samples/disaggregatedset_v1_disaggregatedset.yaml观察控制器自动创建各角色的 LeaderWorkerSet 与 headless Service修改任意角色的镜像后重新 apply即可通过kubectl get events --watch亲眼看到 7 轮协调滚动发布的完整过程。总结 DisaggregatedSet 用一条 YAML 把2-10 个角色凝聚成一个部署单元用 N 维协调滚动更新算法解决了多角色版本同步这一分布式系统难题线性插值统一节奏、先扩容后缩容保容量、协同排空防孤儿、无状态设计保可恢复。如果你正在部署 vLLM、SGLang 的分角色推理服务这套机制值得深入理解——它可能是你在 Kubernetes 上做多角色发布最省心的方案。【免费下载链接】lwsLeaderWorkerSet: An API for deploying a group of pods as a unit of replication项目地址: https://gitcode.com/gh_mirrors/lws2/lws创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考