![]()
作者 | 招商銀行信息技術部 - 架構管理團隊
技術方向 | AI 基礎設施、大模型推理優(yōu)化
大模型推理正在從單機走向分布式集群和分離式架構,但 Kubernetes 原生的工作負載原語(Deployment、StatefulSet)并不是為"多角色協(xié)作、拓撲敏感、快速和可靠升級、故障聯(lián)動"的推理場景設計的。本文介紹招商銀行基于 SGLang RBG 組件,在國產 AI 芯片上落地 DeepSeek-V4 Flash 大 EP 推理服務的實踐,重點剖析動態(tài)端口分配、服務發(fā)現(xiàn)、多級故障自愈與原地升級四個核心機制的設計與實現(xiàn)。
問題:DeepSeek-V4 國產 AI 芯片大 EP 部署的挑戰(zhàn)
當模型參數(shù)量達到數(shù)百 B 級別,單機已無法承載完整的推理計算。以 DeepSeek-V4 Flash 為例,它采用 MoE(Mixture of Experts)架構,專家參數(shù)分布在多張卡上通過 EP(Expert Parallelism)并行計算;同時,為了提升吞吐和降低延遲,業(yè)界通常將推理過程拆分為 Prefill(預填充)和 Decode(解碼)兩個階段,分別由不同的實例組承擔,再通過 Router 統(tǒng)一調度請求——這就是所謂的 PD 分離 + 大 EP 架構。
這個架構在算法和系統(tǒng)層面已經成熟,但在基于 Kubernetes 納管異構算力卡時,其部署與運維面臨的工程化挑戰(zhàn),在復雜度上遠超傳統(tǒng)的無狀態(tài)微服務。以下,我們將這些痛點按層次進行系統(tǒng)性剖析。
多角色拓撲的配置復雜度
大 EP 部署本質上是一個三級嵌套的拓撲結構:最外層是角色(Router、Prefill、Decode),中間層是每個角色的多個實例(如 2 個 Prefill 實例組),最內層是每個實例內的多個 Worker Pod(如一個 Prefill 實例跨 16 張 AI 芯片,由 1 個 Leader + 15 個 Worker 組成)。
傳統(tǒng) Kubernetes 的工作負載原語無法自然表達這種結構:Deployment 是無狀態(tài)的,不支持 Pod 間的拓撲關系;StatefulSet 只管理單一角色的有序副本,無法表達跨角色的依賴。因此運維人員不得不手動維護三組獨立的 YAML 配置,并在其中硬編碼角色間的網絡引用關系——Router 的啟動命令里要列出所有 Prefill 和 Decode 的地址,Prefill 的配置里要寫明數(shù)據(jù)傳輸對端 Decode 的地址。
以一個 2 Prefill + 2 Decode 的部署為例,僅 Router 的啟動參數(shù)中就需要寫入 32 個 --prefiller-hosts 和 32 個 --decoder-hosts 條目(每個實例 16 張卡各一個端點)。這些地址一旦寫錯或遺漏,輕則部分專家不可達導致推理精度下降,重則整個服務無法啟動。
hostNetwork 下的端口管理
在大 EP 部署中,Prefill 和 Decode 之間的 KV Cache 傳輸對帶寬和延遲極其敏感,通常需要使用 RDMA 實現(xiàn)高速數(shù)據(jù)通道。這要求 Pod 以 hostNetwork: true 模式運行,直接使用宿主機網絡棧。
hostNetwork 模式帶來兩個層面的端口管理問題:
第一是同節(jié)點端口沖突。每個推理 Pod 至少需要占用 3-4 個端口(HTTP API 端口、gRPC 通信端口、數(shù)據(jù)傳輸端口、指標暴露端口)。如果調度器將兩個推理 Pod 放在同一節(jié)點上,硬編碼的端口號必然沖突。傳統(tǒng)做法是通過 nodeSelector 或反親和性保證每個節(jié)點只跑一個推理 Pod,但這犧牲了資源靈活性和彈性伸縮能力。
第二是跨角色的端口發(fā)現(xiàn)。即使端口不沖突,Router 也需要知道每個 Prefill/Decode Pod 實際監(jiān)聽的端口號。在端口硬編碼的方案下這不是問題,但一旦引入動態(tài)端口分配(解決沖突后的必然選擇),Router 如何獲知每個下游節(jié)點的端口就成了新的挑戰(zhàn)——傳統(tǒng)的 Kubernetes Service 在 hostNetwork 模式下并不能很好地工作。
服務發(fā)現(xiàn)的時序依賴
即便不考慮端口問題,大 EP 部署中的服務發(fā)現(xiàn)本身也比普通微服務復雜得多。
首先是啟動順序依賴。EP 并行要求同一實例內的所有 Worker 必須在通信組建立階段(HCCL/NCCL init)同時就緒;Prefill 和 Decode 節(jié)點需要互相發(fā)現(xiàn)后才能建立 KV Cache 傳輸通道;Router 則需要等待所有下游節(jié)點就緒后才能正確路由。這些依賴關系不是簡單的"先啟動 A 再啟動 B",而是多層嵌套的拓撲感知。
其次是地址解析的競態(tài)問題。若節(jié)點間的地址發(fā)現(xiàn)依賴啟動腳本執(zhí)行 DNS 查詢(如 getent hosts),會面臨明顯的時序風險:Kubernetes 中 DNS 記錄的刷新依賴目標 Pod 變?yōu)?Ready 狀態(tài)。如果查詢發(fā)生在目標 Pod 就緒之前,將導致解析失敗或獲取到陳舊(Stale)地址。在 Pod 動態(tài)擴縮容或漂移重啟場景下,這種競態(tài)條件極易引發(fā)節(jié)點的級聯(lián)啟動失敗。
故障域的級聯(lián)效應
大 EP 架構中的故障傳播路徑比單體服務復雜得多,呈現(xiàn)出三級級聯(lián)的特征(如:NPU 場景下集合通信庫為 HCCL,GPU 場景下對應 NCCL,下文統(tǒng)稱"集合通信庫"):
實例內級聯(lián)。一個 EP 實例(如 Prefill-0)由多個 Worker Pod 組成,它們通過集合通信庫(HCCL/NCCL)建立 communicator 協(xié)同計算。這類集合通信庫本身不具備容錯能力——任何一個 Worker 故障都會導致 collective 操作阻塞或失敗,整個通信組進入不可用狀態(tài),且原生不支持單 rank 的 "hot rejoin"。Kubernetes 原生的 Pod 級重啟只會重啟出問題的那個 Worker,但它重啟后需要與其他 Worker 一起銷毀舊 communicator 并重新建組——如果其他 Worker 不感知到這次中斷并配合重置狀態(tài),通信組將無法恢復,重啟的那個 Worker 也只能空轉。
跨角色級聯(lián)。Prefill 和 Decode 之間通過 Bootstrap server 注冊會話、并基于 RDMA 建立 KV Cache 傳輸通道。需要強調的是,這里的對端關系不是靜態(tài) 1:1 配對的——Router 按請求維度動態(tài)選擇 Prefill/Decode 組合,真正"持有狀態(tài)"的是 Bootstrap server 的會話注冊表和 Transfer engine(如 Mooncake、NIXL)的 RDMA QP 緩存。當一個 Decode 實例故障重啟后,即使它自身的推理服務恢復了:
Bootstrap server 上殘留的舊 worker 注冊記錄、Transfer engine 上殘留的 RDMA QP 握手緩存,不會隨 Pod 重啟而自動清理;
其他 Prefill 實例新發(fā)起的 KV 傳輸會因為會話 ID 不匹配或 QP 失效而失敗,表現(xiàn)為"對端在線但傳輸始終建立不起來";
更上游的 Router 如果不感知 Decode 的狀態(tài)變化,還會繼續(xù)將新請求路由到這條已經失效的鏈路上。
這意味著單獨重啟 Decode Pod 并不能真正恢復服務——必須讓相關組件一起清理會話狀態(tài),這也是后文采用"實例級整體重建"而非"Pod 級重啟"的根本原因。
重啟風暴。在上述級聯(lián)效應下,如果每個節(jié)點都配置了簡單的健康檢查 + 自動重啟策略,理論上存在 A 節(jié)點故障 → B 節(jié)點通信超時 → B 的健康檢查失敗觸發(fā)重啟 → C 節(jié)點失去 B 的連接被判定不健康 → C 也被重啟…… 這樣的級聯(lián)路徑。實際生產中是否會真的"全集群崩"取決于健康檢查閾值、退避策略、PDB 等配置;更常見的表現(xiàn)形式是"局部反復重啟 + 容量持續(xù)抖動"——服務沒有完全死掉,但故障窗口內 SLA 已經塌了,且每一輪重啟都會觸發(fā)新一輪的會話狀態(tài)殘留與 Router 路由表錯配,使得故障恢復時間被進一步拉長。
Kubernetes 原生的restartPolicy: Always只能重啟單個容器,完全無法應對這種多層級、跨組件的故障傳播。
異構 AI 芯片適配的復雜度高
在異構的國產算力集群上,大 EP 部署也面臨較高的適配復雜度,主要源于兩點:
部署芯片的不確定性:為追求極致的資源利用率,會采取“擇優(yōu)部署”策略,即根據(jù)模型在不同芯片上的性能與精度表現(xiàn)來動態(tài)選擇部署目標。這就要求部署方案必須能夠兼容并適配多種國產 AI 芯片。
資源變動引起的遷移:生產環(huán)境里的資源經常調整,模型需要能靈活地在不同的國產 AI 芯片之間遷移。這就要求部署方案不再依賴具體的硬件配置,抹平底層差異,讓模型換到某一類卡都能正常跑。
升級的高昂代價
推理框架版本迭代快,特別是對于 DeepSeek V4 的支持依然在收斂過程中,因此不論是 SGLang 還是 vLLM 都在不斷出新版本進行優(yōu)化,但傳統(tǒng)的 Kubernetes 滾動更新對大模型推理而言代價極高。
一次標準的 Pod 重建(RecreatePod)需要經歷完整的生命周期:刪除舊 Pod → 釋放 AI 芯片資源 → 調度新 Pod → 等待 AI 芯片分配 → 拉取新鏡像 → 加載模型權重到顯存。對于 DeepSeek-V4 Flash 這樣的模型,僅模型加載就需要數(shù)分鐘。加上 AI 芯片資源的重新調度存在不確定性(如果節(jié)點資源被其他負載占用,新 Pod 可能長時間 Pending),單個實例的更新窗口難以預測。
更麻煩的是跨角色的版本一致性問題。Prefill 和 Decode 之間的數(shù)據(jù)傳輸協(xié)議可能因框架版本不同而不兼容。在滾動更新過程中,如果 Prefill 組已經全部升級到新版本,而 Decode 組還在用舊版本,就可能出現(xiàn)協(xié)議不匹配導致的傳輸失敗。傳統(tǒng)的 Deployment 滾動更新策略無法保證兩個獨立工作負載的更新進度同步。
方案選型:為什么是 RBG
SGLang RBG(RoleBasedGroup)是一個 Kubernetes API 擴展,專門為分布式推理工作負載設計。它的核心抽象是"角色組"——將 Router、Prefill、Decode 等角色定義為一個邏輯整體,由統(tǒng)一的控制面進行編排。
在確定方案前,我們做了兩層評估:第一層是"工作負載編排 API",這類方案和 RBG 處在同一抽象層級,是真正意義上的候選;第二層是更上層的"推理服務平臺"(比如 AIBrix 等)。它們的關注點是"模型 + 路由 + 彈性 + 可觀測"的端到端產品化,此類平臺的代價是與某個推理引擎(如 vLLM)、某套模型管理范式或者硬件深度綁定,定位與 RBG 不同,不適合直接并列比較。
同層方案對比
![]()
最終選擇 RBG,我們主要看重了三點:
第一,面向"hostNetwork + 國產 AI 芯片 + PD 分離"場景的工程化封裝最完整。動態(tài)端口分配、服務發(fā)現(xiàn) ConfigMap、EngineRuntime(服務注冊和 Metrics 歸一化的 sidecar 抽象)這幾件事,LWS/DisaggregatedSet 和 kthena 都沒有原生覆蓋,需要團隊自行擴展,且實現(xiàn)難度較大;RBG 把這些痛點直接變成了軟件能力。
第二,原地升級語義可用。在國產 AI 芯片資源緊張、模型加載耗時長的生產環(huán)境里,"保留調度位置和 AI 芯片的綁定、只換鏡像"是非常實際的需求。LWS / Kthena 目前都還停留在類似 StatefulSet 的重建式滾動更新,RBG 與 OpenKruise 是少數(shù)能拿出可用方案的項目。
第三,不侵入推理框架,輕量無依賴。RBG 工作在 Kubernetes 控制面,不修改 SGLang/vLLM 任何代碼,也不依賴額外的外部中間件,部署到已有 K8s 集群的成本幾乎為零。
生產實踐
本章節(jié)以在 NPU 生產集群中自動化部署 DeepSeek-V4 Flash 的大 EP 推理服務為例,詳細闡述如何基于 RBG 組件落地國產 AI 芯片的云原生推理方案。整體拓撲架構如下:
Router 組:1 個實例,負責請求路由和負載均衡
Prefill 組:2 個實例,每個實例跨 16 張 NPU 做 EP 并行
Decode 組:2 個實例,每個實例同樣跨 16 張 NPU
三個角色通過一個 RoleBasedGroup CR 統(tǒng)一定義和管理。Controller 自動完成端口分配、服務發(fā)現(xiàn) ConfigMap 生成、以及故障時的自愈重建。
以下為整體部署架構:
![]()
部署配置示例(以 SGLang 為例,可支持 vLLM)
--host 0.0.0.0實踐初期,我們通過 getent hosts 解析 IP,并硬編碼端口列表的方式去獲取 IP 和端口。為了簡化運維并支持動態(tài)擴縮容,后來我們引入動態(tài)端口分配和服務發(fā)現(xiàn)機制(見高級特性章節(jié)),將所有的地址發(fā)現(xiàn)和端口管理都交給 Controller 處理。運維人員只需要關注業(yè)務語義層面的配置,例如"幾個 Prefill、幾個 Decode、用什么鏡像"等,極大地降低了運維工作量。
生產中的注意事項
在 NPU 環(huán)境下落地這套方案,有幾個實際碰到的問題值得分享:
第一,RDMA 網絡要求。跨機的 Prefill-Decode 數(shù)據(jù)傳輸依賴 RDMA 網卡。我們通過 hostNetwork: true + 端口動態(tài)分配解決了端口沖突問題,但需要確保 RDMA 設備正確掛載到容器中(通過 EngineRuntimeProfile 中的 device plugin 配置)。
第二,健康檢查配置。我們?yōu)槊總€推理 Pod 配置了 readinessProbe 和 livenessProbe。readinessProbe 用于判斷模型是否加載完成(模型加載可能需要數(shù)分鐘),livenessProbe 用于檢測推理服務是否卡死。RBG 的自愈機制依賴這些 Probe 來正確判斷 Pod 狀態(tài)。
第三,PID 1 與信號傳播。生產中啟動命令若采用sh -c "python3 ..."形式,此時容器內 PID 1 是 shell,推理進程是其子進程。POSIX shell 默認不會主動向子進程轉發(fā) SIGTERM,導致 gracePeriodSeconds(原地升級中用于優(yōu)雅停流的關鍵參數(shù))期間推理進程實際上收不到優(yōu)雅退出信號——寬限期一過,kubelet 直接 SIGKILL,長 Prefill 請求被一刀斬斷,原地升級承諾的"優(yōu)雅停流"形同虛設;與此同時 fork 出的 HCCL/tokenizer 等子孫進程容易成為孤兒,可能仍持有 NPU 設備或 RDMA 端口,導致下一次重建出現(xiàn) device busy 而啟動失敗,反過來觸發(fā) RBG 的實例級重建,放大故障域。因此建議引入 tini 作為 PID 1,或在啟動腳本中以 exec 替換 shell 進程,確保信號正確轉發(fā)并完成孤兒進程回收——這是原地升級機制能否真正成立的前提。
高級特性深入剖析
動態(tài)端口分配
如前所述,hostNetwork 模式下的端口沖突和端口發(fā)現(xiàn)是大 EP 部署的基礎性難題。傳統(tǒng)做法要么人工規(guī)劃端口分配表(運維成本高且容易出錯),要么通過 nodeSelector 保證節(jié)點互斥(犧牲資源靈活性,無法彈性伸縮)。RBG 在 Controller 層提供了一套系統(tǒng)化的解決方案。
RBG 在 Controller 層引入了一個全局端口分配器(Port Allocator),采用隨機分配 + 范圍隔離的策略:
}分配流程如下:
Controller 在創(chuàng)建 RoleInstanceSet 時,從配置的端口范圍 [startPort, startPort + portRange) 中隨機選取端口,寫入 RoleInstanceSet 的 annotation(RoleScoped 端口)。
創(chuàng)建 RoleInstance 時,為每個 Pod 獨立分配 PodScoped 端口,同樣寫入 RoleInstance annotation。
Pod 實際創(chuàng)建時,Controller 從 annotation 中讀取端口值,以環(huán)境變量的形式注入到容器中。
兩級作用域設計的考慮是:有些端口(如用于角色間通信的 bootstrap 端口)在整個角色內應保持一致,用 RoleScoped;有些端口(如 Pod 自身的 HTTP 服務端口)必須每個 Pod 不同,用 PodScoped。
跨 Pod 端口引用解決了另一個痛點——一個角色如何知道另一個角色分配到了哪個端口?通過 references 字段,Controller 會在創(chuàng)建 Pod 時自動解析 "prefill.leader.grpc" 這樣的引用,找到目標 Pod 的實際端口值并注入為環(huán)境變量。
在最初的部署方式中,端口是硬編碼在啟動命令里的:
--disaggregation-bootstrap-port 34000使用端口分配器后,啟動命令變?yōu)椋?/p>
--disaggregation-bootstrap-port ${BOOTSTRAP_PORT}這個改變看似簡單,但它意味著我們可以安全地在同一節(jié)點上調度多個推理副本,支持超分和彈性伸縮,而不再受限于"一個節(jié)點只能跑一個推理實例"的約束。
服務發(fā)現(xiàn)與 EngineRuntime
前面分析了大 EP 部署中服務發(fā)現(xiàn)的兩個核心困難:啟動順序依賴和地址解析的時序競態(tài)。在原始方案中,節(jié)點間地址通過啟動腳本中的 DNS 查詢獲取:
export decode_0_ip=$(getent hosts decode-0.svc | awk '{print $1}')這種方式的問題在于:DNS 解析發(fā)生在啟動時刻,如果目標 Pod 尚未就緒,解析會失敗或拿到舊地址;同時端口信息完全硬編碼,一旦引入動態(tài)端口分配就完全失效。RBG 通過三層遞進的服務發(fā)現(xiàn)機制系統(tǒng)性地解決了這些問題。
三層服務發(fā)現(xiàn)機制
第一層:環(huán)境變量注入。Controller 為每個 Pod 自動注入一組標準化環(huán)境變量,包括 RBG_GROUP_NAME、RBG_ROLE_NAME、RBG_ROLE_INDEX 等。對于 LeaderWorker 模式,還會注入 Leader 地址 RBG_LWP_LEADER_ADDRESS 和 Worker 索引。
第二層:拓撲 ConfigMap。Controller 維護一個全局的服務發(fā)現(xiàn) ConfigMap,包含整個角色組的完整拓撲信息,并以 Volume 形式掛載到每個 Pod 的 /etc/rbg/config.yaml:
# ...推理引擎只需讀取這個文件就能獲得完整的集群拓撲,不再依賴啟動時的 DNS 解析,也天然適配動態(tài)端口。
第三層:組件級發(fā)現(xiàn)。對于同一個 RoleInstance 內的多組件場景(比如一個推理實例包含 leader 和多個 worker),RBG 支持通過 annotation 聲明組件間的地址和端口引用:
}Controller 會在 Pod 創(chuàng)建時解析這些引用,將實際地址和端口值注入為環(huán)境變量。
EngineRuntime 配置解耦:ClusterEngineRuntimeProfile 的作用
在實際部署中,不同的硬件平臺需要不同的運行時配置——驅動初始化腳本、設備掛載方式、監(jiān)控 sidecar 等。RBG 通過 ClusterEngineRuntimeProfile 這個集群級 CRD 實現(xiàn)了運行時配置的復用:
path: /usr/local/Ascend角色定義中只需通過 engineRuntimes 字段引用 Profile 名稱,Controller 會自動完成 sidecar 和 init-container 的注入。當 Profile 更新時(比如升級驅動版本),Controller 可以根據(jù)配置的策略(NoUpdate 或 RollingUpdate)決定是否觸發(fā) Pod 滾動更新。
這種設計使得硬件適配層與推理服務定義解耦——同一份推理服務 YAML 可以通過切換不同的 EngineRuntimeProfile 運行在不同的硬件上,而不需要修改業(yè)務配置。
EngineRuntime 的能力擴展:統(tǒng)一服務注冊與 Metrics 歸一化
上面介紹了 ClusterEngineRuntimeProfile 在硬件驅動注入方面的作用,但 EngineRuntime 的能力遠不止于此。在分布式推理場景中,不同的推理引擎(SGLang、vLLM 等)在服務注冊和指標暴露上各有一套實現(xiàn),這給多引擎混合部署帶來了顯著的集成復雜度。RBG 通過 EngineRuntime 機制提供了兩個關鍵的可擴展抽象層:統(tǒng)一服務注冊和 Metrics 歸一化。
統(tǒng)一服務注冊
在前面的服務發(fā)現(xiàn)機制中,介紹了 RBG Controller 為每個 Pod 注入環(huán)境變量(RBG_GROUP_NAME、RBG_ROLE_NAME、RBG_ROLE_INDEX 等)并掛載服務發(fā)現(xiàn) ConfigMap(/etc/rbg/config.yaml)的機制。這些拓撲信息為服務注冊提供了基礎,但"如何將自己注冊到 Router"這個動作,則由 EngineRuntime 注入的服務注冊 Sidecar 來完成。
具體工作流程是:服務注冊 Sidecar 啟動后,讀取 Controller 注入的環(huán)境變量和 ConfigMap,獲取當前 Pod 的角色信息、實例索引和 Router 地址,然后主動調用 Router 提供的 Worker 注冊接口,將自己注冊為可用的推理節(jié)點。這種主動注冊模式相比被動發(fā)現(xiàn)有幾個顯著優(yōu)勢:
第一,服務有效性保障更強。Worker 只有在真正就緒(模型加載完成、推理服務可響應)之后才會發(fā)起注冊,Router 收到注冊請求時即可確認該節(jié)點可以接收流量。不像被動發(fā)現(xiàn)模式下,DNS 或 Endpoints 更新可能先于服務實際就緒,導致 Router 將請求路由到未準備好的節(jié)點。
第二,不依賴任何 Kubernetes 資源或外部中間件。整個注冊過程是 Worker 與 Router 之間的直接通信,不需要 Consul、ZooKeeper、etcd 等服務注冊中心,也不依賴 Kubernetes Service 的 Endpoints 更新機制。這極大簡化了部署架構,減少了故障點。
第三,引擎無關的可擴展性。不同的推理引擎的注冊協(xié)議和接口各不相同——SGLang 有自己的 bootstrap 注冊流程,vLLM 通過 gRPC 服務發(fā)現(xiàn)。通過 EngineRuntime,用戶可以為每種引擎編寫對應的注冊 Sidecar 實現(xiàn),封裝為獨立的 ClusterEngineRuntimeProfile。RBG 層面只關心"角色組"的編排語義,而"如何注冊到 Router"的具體實現(xiàn)則完全委托給 EngineRuntime Profile——切換推理引擎時,只需替換 Profile 即可,無需修改 RoleBasedGroup 配置。
Metrics 歸一化
另一個痛點是推理引擎的指標暴露不統(tǒng)一。SGLang、vLLM 等引擎在底層暴露的指標語義是一致的——都會報告吞吐量(tokens/s)、隊列深度、請求延遲、KV Cache 使用率、AI 芯片利用率等核心指標——但它們的 Prometheus Metric 命名卻各不相同。例如同樣是"當前排隊請求數(shù)",SGLang 叫sglang_num_queue_reqs,vLLM 叫vllm:num_requests_waiting,Dynamo 叫dynamo_pending_requests。
這種命名差異帶來的后果是:Prometheus 的告警規(guī)則、Grafana Dashboard、以及基于指標的 HPA/Autoscaler 策略,都需要針對每種引擎單獨配置一套。當生產環(huán)境中同時運行多種引擎(或頻繁切換引擎版本)時,運維配置的復雜度會線性增長。
因此通過使用 EngineRuntime 注入一個 Metrics 歸一化 Sidecar 來解決這個問題。該 Sidecar 的職責是:從推理引擎的原生 Metrics 端點采集指標,按照統(tǒng)一的命名規(guī)范進行轉換,然后通過標準的 Prometheus 端口對外暴露。對上層的監(jiān)控、告警和彈性伸縮系統(tǒng)而言,無論底層運行的是哪種推理引擎,看到的都是同一套指標名稱和語義。
這種設計同樣具備可擴展性。當需要支持一個新的推理引擎時,只需在 Metrics Sidecar 中增加該引擎的指標映射規(guī)則,封裝為對應的 ClusterEngineRuntimeProfile,上層的 Prometheus 配置、告警規(guī)則和 Autoscaler 策略完全不需要調整。
以下示例展示了一個典型的 EngineRuntime Profile,同時包含服務注冊和 Metrics 歸一化兩種能力:
name: metrics # 統(tǒng)一的 metrics 暴露端口當需要切換到 vLLM 時,只需創(chuàng)建一個新的 Profile(如 vllm-engine-runtime),將 ENGINE_TYPE 設為 vllm,服務注冊 Sidecar 和 Metrics Sidecar 內部會根據(jù)引擎類型選擇對應的注冊協(xié)議和指標映射規(guī)則。角色定義中只需替換 engineRuntimes 引用的 profileName:
image: registry.xxx/sglang:0.5.9這種架構帶來了幾個實際價值:
推理引擎可替換性——從 SGLang 切換到 vLLM 時,上層的監(jiān)控、告警、彈性伸縮策略完全不受影響,因為它們對接的始終是統(tǒng)一的指標接口。
服務注冊零依賴——不引入任何外部中間件,Worker 與 Router 直接通信完成注冊,架構簡潔、故障點少。
平臺與業(yè)務分離——平臺團隊維護 Profile(服務注冊和 Metrics 歸一化的實現(xiàn)),業(yè)務團隊只需關注推理服務本身的配置。
多級故障自愈
前面分析了大 EP 架構中故障域的三級級聯(lián)特征:實例內的通信組斷裂、跨角色的連接狀態(tài)失效、以及全局的重啟風暴。Kubernetes 原生的 restartPolicy: Always 只能重啟單個容器,完全無法應對這種多層級的故障傳播。RBG 針對性地設計了實例級重啟策略和防級聯(lián)保護。
下圖對比了無 RBG 保護時的故障級聯(lián)傳播與 RBG 自愈機制的處理流程:
![]()
自愈模型
RBG 在 RoleInstance(角色實例)級別實現(xiàn)了重啟策略,默認策略是 RecreateRoleInstanceOnPodRestart——當實例中任何一個 Pod 故障或容器重啟時,整個實例的所有 Pod 會被一起重建。
具體的自愈流程:
└───────────────────────────────────────────────────────┘防級聯(lián)保護是這個機制中最關鍵的設計。在分布式推理集群中,一個節(jié)點故障很容易引發(fā)連鎖反應——A 節(jié)點故障導致 B 節(jié)點通信超時,B 節(jié)點的健康檢查失敗觸發(fā)重啟,B 節(jié)點重啟又導致 C 節(jié)點異常……如果不加控制,整個集群可能在短時間內陷入重啟風暴。
RBG 通過雙層守衛(wèi)防止這種情況:內存中的 LRU 緩存提供零延遲的判斷(避免 informer 緩存延遲導致的重復觸發(fā)),API Server 上的持久化 Condition 則確保 Controller 自身重啟后也不會重復執(zhí)行。只有當實例完全恢復到 Ready 狀態(tài)后,守衛(wèi)才會被清除。
組件依賴感知
對于多組件的 RoleInstance(比如 Leader-Worker 模式),重建時還需要尊重組件間的啟動依賴:
重建(Scale-out)階段:按依賴順序逐步創(chuàng)建組件,Leader 就緒后才創(chuàng)建 Worker。
銷毀(Scale-in)階段:按依賴的逆序刪除,先停 Worker 再停 Leader。
環(huán)檢測:如果依賴關系中存在環(huán)(通過 DFS 三色算法檢測),回退到并行模式避免死鎖。
跨角色協(xié)調
單個角色的自愈解決了實例級別的問題,但跨角色的協(xié)調同樣重要。RBG 通過 CoordinatedPolicy CRD 來管理跨角色的一致性:
progression: OrderReady # 當前批次全部 Ready 后才繼續(xù)下一批這確保了在滾動更新或擴縮容時,Prefill 和 Decode 保持步調一致,不會出現(xiàn)"Prefill 已經全部更新到新版本,但 Decode 還在用舊版本"導致的協(xié)議不兼容問題。
原地升級
為什么推理服務需要原地升級
在傳統(tǒng)的 Kubernetes 滾動更新中,更新一個 Pod 意味著:刪除舊 Pod → 釋放 AI 芯片資源 → 重新調度新 Pod → 等待新 Pod 獲得 AI 芯片 → 拉取新鏡像 → 重新加載模型 → 服務就緒。對于大模型推理場景,這個過程的代價遠高于普通的 Web 服務:
首先是模型加載時間長。DeepSeek-V4 Flash 的模型權重超過數(shù)百 GB,從存儲加載到 AI 芯片顯存需要數(shù)分鐘。在這段時間內,該推理實例完全不可用。
其次是AI 芯片資源調度不確定。高性能計算資源是稀缺資源,刪除舊 Pod 后新 Pod 不一定能立即被調度到合適的節(jié)點——如果節(jié)點上的 AI 芯片 正好被其他工作負載占用,新 Pod 可能長時間處于 Pending 狀態(tài)。
最后是網絡身份變化。在使用 hostNetwork 的場景下,Pod 重建意味著 IP 可能改變,所有與之關聯(lián)的服務發(fā)現(xiàn)配置都需要更新,其他角色節(jié)點需要感知并重建連接。
理想的升級方式是:只替換容器鏡像,保持 Pod 的調度位置、IP 地址、掛載卷不變——這正是 RBG 原地升級的核心思路。
下圖對比了兩種升級方式的時間線差異:
![]()
三種更新策略
RBG 在每個角色的 rolloutStrategy 中提供了三種更新策略:
gracePeriodSeconds: 30 # 流量排空寬限期RecreatePod:傳統(tǒng)方式,刪除舊 Pod 后創(chuàng)建新 Pod。適用于需要變更掛載卷、網絡配置等非鏡像字段的場景。
InPlaceIfPossible:優(yōu)先嘗試原地升級,如果變更內容超出原地升級的能力范圍(比如修改了 Volume 或新增了容器),自動降級為 RecreatePod。這是推薦的生產策略——兼顧效率與安全。
InPlaceOnly:強制原地升級,如果變更無法原地執(zhí)行則直接報錯。適用于對升級停機時間有嚴格要求、且能確保只做鏡像變更的場景。
原地升級的執(zhí)行流程
當 Controller 檢測到角色模板發(fā)生變化時,會進入如下決策和執(zhí)行流程:
└─────────────────────────────────────────────────────────────┘這里有幾個設計細節(jié)值得關注:
寬限期(Grace Period)的意義在于優(yōu)雅停流。在推理場景中,一個請求(尤其是長文本的 Prefill 階段)可能需要數(shù)十秒才能完成。如果不等待當前請求結束就直接重啟容器,用戶會收到中斷錯誤。通過設置合理的 gracePeriodSeconds,Controller 先將 Pod 標記為 Not Ready(不再接收新請求),等待已有請求處理完畢后再執(zhí)行鏡像替換。
完成檢測不依賴"容器是否啟動"這樣的粗粒度信號,而是通過對比 ImageID 的變化來精確判斷——只有當 Kubelet 確實拉取并啟動了新鏡像后,ImageID 才會改變。這避免了因網絡抖動或鏡像拉取失敗導致的誤判。
防重復更新:Controller 在執(zhí)行原地升級后會通過 updateExpectations 追蹤該操作,在 API Server 確認變更之前不會推進 CurrentRevision。這防止了因 informer 緩存延遲導致同一個實例被重復升級。
原地升級與跨角色協(xié)調
原地升級與前面提到的 CoordinatedPolicy 天然兼容。協(xié)調層只控制"多少個實例可以同時更新",而不關心"用什么方式更新"。這意味著在跨角色協(xié)調場景下,Prefill 和 Decode 的原地升級也會受到 maxSkew 的約束——Controller 確保兩個角色的更新進度差異不超過配置的閾值,防止出現(xiàn)版本不兼容的狀態(tài)。
實際收益
在生產環(huán)境中,將推理框架從 SGLang 0.5.8 升級到 0.5.9 時:
傳統(tǒng) RecreatePod 方式下,每個 Prefill 實例的更新需要經歷"Pod 刪除 → 重新調度 → 模型加載 → 就緒"的完整周期,單實例更新耗時約 5-8 分鐘。整個集群的滾動更新需要數(shù)十分鐘,期間服務容量持續(xù)受損。
使用 InPlaceIfPossible 策略后,Pod 的調度位置和 AI 芯片綁定關系保持不變,省去了重新調度的等待時間。雖然模型仍需重新加載(因為推理進程重啟),但消除了資源競爭的不確定性,單實例更新耗時穩(wěn)定在 3-4 分鐘,整體升級時間和服務影響窗口顯著縮短。
效果與思考
目前,大 EP 推理服務的自動部署與自愈方案已在線上穩(wěn)定運行,主要改善體現(xiàn)在以下幾個方面:
![]()
同時也有一些局限性值得坦誠面對:
端口分配存在理論沖突風險:采用隨機策略而非全局注冊,在端口范圍較小或 Pod 密度極高時存在沖突概率(實際中可通過設置足夠大的范圍來規(guī)避)。
服務發(fā)現(xiàn)存在短暫不一致窗口:ConfigMap 的更新依賴 Controller 的 Reconcile 周期,更新期間可能出現(xiàn)短暫的不一致。
原地升級能力有限:目前僅支持容器鏡像變更,如需修改 Volume 掛載或網絡配置,仍需走 RecreatePod 路徑。不過 InPlaceIfPossible 策略會自動處理降級,無需人工判斷。
跨角色故障聯(lián)動依賴框架自身:例如 Decode 故障后 Router 自動摘除對應節(jié)點,目前需要推理框架自身支持,RBG 尚未提供開箱即用的流量摘除能力。
從更大的視角看,LLM 推理基礎設施正在從"單一框架 + 單一硬件"演進為"多框架 + 多硬件 + 多拓撲"的異構集群。RBG 提供了一個合理的抽象層——將"角色"作為基本編排單元,將"角色組"作為服務治理的原子粒度——這個抽象足夠通用,可以適配 PD 分離、MoE 分布式、Pipeline 并行等多種推理架構。
我們后續(xù)的工作將圍繞兩個方向:一是在 RBG 基礎上增強彈性伸縮能力,根據(jù)實時負載自動調整 Prefill 和 Decode 的比例;二是持續(xù)參與上游社區(qū)建設,將生產中遇到的問題和解決方案回饋給開源項目。
會議推薦
企業(yè)級 Agent 落地,繞不開 4 個真實的工程問題!如何在 Agent 安全性和可用性之間找到平衡點?Agent 需要什么樣的記憶系統(tǒng)才能真正理解上下文?如何通過算法壓榨實現(xiàn)智力增量與成本控制的極致平衡?多 Agent 協(xié)作,如何做到可觀測、可治理、可控制?6.26-27 AICon 上海站,國內頭部公司的 Agent 實踐,一次說透。
今日薦文
你也「在看」嗎?
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發(fā)布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.