系統設計面試:如何設計遵守 PDB 與拓撲分布的維護編排器?
題干與適用場景
叢集需要滾動升級節點,但工作負載設定了 PodDisruptionBudget 和 topology spread constraints。請設計維護編排器,說明候選節點選擇、驅逐並發、不可調度、容量預留、故障恢復和可觀測性。
Kubernetes 將 PDB 定義為限制自願中斷同時影響的 Pod 數量;維護工具應使用 Eviction API,讓 PDB 參與准入。拓撲分布約束則控制 Pod 在 zone、node 等故障域中的偏斜。高品質設計要把兩個約束放進同一控制迴圈,不能只檢查目前 Pod 數量後批量刪 Pod。
面試官考察點
- 是否區分自願中斷、硬體故障等非自願中斷及其保證邊界。
- 是否透過 Eviction API 遵守 PDB,而不是直接刪除 Pod。
- 是否按 topology spread 的
maxSkew、whenUnsatisfiable和標籤選擇器評估影響。 - 是否設計逐節點、逐故障域的並發和容量預留。
- 是否處理 PDB 阻塞、Pod 未就緒、重試、逾時和回滾。
- 是否能用事件、指標和稽核記錄解釋每次維護決策。
回答前需要釐清的問題
- 維護是節點 OS 升級、實例類型替換,還是緊急修復?緊急故障不一定受 PDB 保護。
- 工作負載的副本數、PDB、拓撲域和容量餘量是多少?
- 目標是最短總時長、最小使用者風險,還是嚴格保持每個 zone 的冗餘?
- 叢集是否有 Cluster Autoscaler、臨時節點池或跨 zone 容量限制?
- 允許多長時間等待 PDB,逾時後是暫停、降級還是人工批准?
30 秒回答框架
我會把維護編排器做成宣告式控制器:先讀取節點、Pod、PDB、拓撲約束和可調度容量,計算單次驅逐對健康副本和拓撲偏斜的影響,再透過 Eviction API 小批量執行。控制器以節點和故障域為並發邊界,等待替代 Pod Ready 後再繼續;PDB 阻塞或拓撲約束無法滿足時暫停並給出原因,不直接刪除 Pod。所有操作帶冪等鍵、逾時、回滾和指標,緊急非自願故障走獨立風險路徑。
分步驟深入解答
1. 定義狀態與約束輸入
維護任務包含目標節點、升級批次、最大並發、逾時和回滾策略。控制器快取節點標籤、Pod owner、Ready 狀態、PDB disruptionsAllowed、拓撲約束及可用容量;每次決策都重新讀取關鍵狀態,避免只依賴舊快照。
2. 計算候選節點和安全批次
先過濾已封鎖、承載系統關鍵 Pod 或沒有替代容量的節點,再按 zone、rack 和 workload 分組。單批次不能同時影響同一工作負載超過 PDB 允許值,也要模擬驅逐後新 Pod 的拓撲偏斜。優先選擇有充足餘量且不會讓某個故障域成為單點的節點。
3. 使用 Eviction API
對自願維護呼叫 Eviction API,讓 Kubernetes 根據 PDB 判斷是否允許。返回衝突或不可用時記錄具體 PDB、工作負載和重試時間。直接刪除 Pod 會繞過預算,應僅在明確的緊急破壞性流程中使用,並要求更高權限和人工確認。
讀取約束 -> 選擇一個節點 -> 建立 eviction 請求
-> PDB 允許?否:退避並重新評估
-> 是:等待替代 Pod Ready 與拓撲恢復
-> 成功:標記節點完成;失敗:暫停批次並回滾或升級4. 處理拓撲與容量回饋
驅逐不是終點。新 Pod 可能因 DoNotSchedule、缺少 topology key、親和性或容量不足而 Pending。控制器監控調度事件,必要時先擴容臨時節點池或改變批次順序;不能為完成升級而放寬工作負載約束。ScheduleAnyway 也要記錄最終偏斜和成本影響。
5. 設計並發、租約與冪等
用任務租約鎖定一個節點和控制器世代,避免多個維護器重複驅逐。預設每個 zone 一個小並發或每個工作負載一個令牌桶,只有替代 Pod Ready、PDB 預算恢復且拓撲偏斜回到閾值內才領取下一個令牌。重複執行同一 eviction 應識別為已處理,控制器重啟後從 API 狀態恢復。
6. 失敗、逾時和回滾
如果 Pod 長時間 Pending、PDB 長期為零、節點排空逾時或新節點不健康,暫停後續批次並解除不必要的 cordon。已升級的節點不能簡單「回滾」成舊版本時,要保留舊節點池或切換到已驗證映像。回滾目標是恢復服務冗餘,不是強行讓維護百分比達到 100%。
7. 可觀測性與演練
記錄每次選擇的節點、受影響工作負載、PDB 前後預算、拓撲計數、eviction 回應、Pending 原因和耗時。指標包括成功驅逐率、PDB 阻塞時長、拓撲最大偏斜、恢復 Ready 延遲、跨 zone 流量和任務重試。演練至少涵蓋單 zone 容量不足、PDB 為零、調度器故障、控制器重啟和節點突然消失。
高品質示範回答
我會把編排器建模為按節點和故障域推進的宣告式控制器。它讀取 Pod owner、Ready、PDB、topology spread、節點標籤和可調度容量,先模擬一次驅逐對健康副本與 maxSkew 的影響,再透過 Eviction API 發起小批次請求。每個 zone 設並發令牌,只有替代 Pod Ready、PDB 預算恢復且拓撲約束滿足,才繼續下一節點。
PDB 返回衝突、Pod 進入 Pending、容量不足或等待逾時都導致暫停並記錄原因;控制器可以擴容臨時節點池或調整順序,但不直接刪除 Pod,也不偷偷放寬約束。租約和世代號保證重試冪等,重啟後從 API 狀態恢復。指標涵蓋預算阻塞、最大拓撲偏斜、Ready 延遲、重試與跨 zone 成本,演練通過後再擴大批次。
常見錯誤
- 直接刪除 Pod 加快排空 → 繞過 PDB → 對自願維護使用 Eviction API。
- 只看
disruptionsAllowed→ 新 Pod 可能無法按拓撲約束調度 → 先模擬偏斜和容量。 - 一次驅逐多個 zone 的副本 → 失去故障域冗餘 → 以 zone 和 workload 設定並發令牌。
- PDB 阻塞就臨時修改預算 → 可能把維護風險轉給使用者 → 暫停、擴容或請求批准。
- 只等待 Pod 被刪除 → 服務可能長期沒有 Ready 替代 → 以 Ready、調度事件和逾時驅動狀態機。
- 沒有控制器租約 → 多個維護任務重複操作 → 用租約、世代號和冪等狀態恢復。
追問及應對
PDB 能保護節點突然宕機嗎?
不能完全保護。PDB 主要限制自願中斷;硬體故障和資源耗盡等非自願中斷可能直接減少副本。設計仍需多故障域、副本和容量冗餘。
為什麼不能用 kubectl delete pod?
直接刪除繞過 PDB 的驅逐准入。維護編排器應呼叫 Eviction API,讓 API Server 根據預算返回允許或衝突。
PDB 允許但新 Pod 一直 Pending 怎麼辦?
暫停批次,讀取調度事件和拓撲計數,擴容或更換候選節點;不要繼續驅逐造成更多冗餘缺口。
ScheduleAnyway 是否可以忽略偏斜?
不能忽略。它表示調度器會偏好降低偏斜的節點,但仍可能產生偏斜。控制器要記錄實際分布、成本和冗餘風險。
控制器重啟如何避免重複驅逐?
用任務租約、節點鎖、控制器世代號和持久狀態;恢復時以 API 中的 Pod、Eviction 和節點狀態為準,而不是重放記憶體佇列。
什麼時候允許緊急強制刪除?
僅在確認自願維護路徑無法處理的緊急風險、具備更高權限和人工批准時,並明確記錄可能違反 PDB 和損失冗餘的後果。