Kubernetes 面試:如何讓 kube-scheduler 的 API 呼叫不阻塞?
題幹與適用場景
一個叢集啟用了會呼叫 API Server 的排程外掛。API 延遲升高時,排程週期被同步呼叫占住,待排程 Pod 越積越多。請設計 KEP-5229 所描述的非同步處理方案,說明優先級佇列、請求去重、失敗重試、取消、資源公平和回滾條件。
面試官考察點
- 能否區分排程週期串行語意與 API 副作用的非同步執行。
- 是否用有界優先級佇列和請求去重避免執行緒飢餓與重複寫入。
- 是否定義冪等鍵、逾時、取消、過期結果和不可重試錯誤。
- 是否考慮高優先級 Pod、公平性、背壓、指標與 feature gate 灰度。
回答前需要澄清的問題
- API 呼叫是唯讀查詢、冪等寫入,還是建立外部資源?副作用決定重試邊界。
- 排程外掛能否接受最終一致的外部狀態,還是綁定前必須得到確認?
- 失敗時 Pod 回到 unschedulable 佇列,還是只重試 API 操作?兩者不可混為一談。
- 叢集的 API QPS、並發排程數和優先級佇列預算是多少?
30 秒回答框架
我會把慢 API 操作從排程執行緒移到有界的優先級佇列,排程執行緒提交帶冪等鍵的工作後繼續處理其他 Pod。佇列按 Pod 優先級和等待時間服務,並對相同鍵的請求去重。完成事件只觸發安全的重新評估,過期結果不能直接綁定。錯誤按可重試與永久失敗分類,設定逾時、取消、退避和並發上限。用待處理數量、佇列等待、成功率和排程延遲做灰度門檻,異常時關閉 feature gate 回退同步路徑。
分步驟深入解答
1. 劃分同步邊界
排程週期負責選擇節點和維護排程上下文;非同步工作負責可延遲的 API 操作。若操作結果是綁定決策的硬前置,就不能簡單「丟到背景」,應先把狀態建模為 pending,確認前阻止綁定。只有不影響當前週期安全性的工作才可非同步化。
2. 設計有界優先級佇列
每個工作攜帶 Pod UID、操作類型、冪等鍵、截止時間和取消上下文。佇列必須有容量上限;滿載時向排程器回傳明確的 backpressure 訊號,而不是無界堆積。高優先級工作先服務,同時用 aging 或配額避免低優先級工作永久飢餓。
submit(key, priority, deadline, operation)
if same key is pending: coalesce(operation)
else if queue is full: return Backpressure
else enqueue(operation)3. 請求去重與冪等
去重只解決同一邏輯操作的並發合併,不等於 API Server 的冪等保證。寫入應使用穩定資源鍵、條件更新或伺服器冪等語意;重試前檢查上一次結果,避免重複建立外部資源。不同版本或不同目標的請求不能因字串相似而錯誤合併。
4. 完成、取消與過期結果
工作完成後發布事件,事件只負責讓相關 Pod 重新評估,不直接假設排程狀態仍然有效。Pod 被刪除、搶占或進入新排程上下文時取消舊工作。超過 deadline 的結果丟棄並記錄原因;API 呼叫成功但上下文已過期時也不能執行舊綁定。
5. 重試、背壓與公平
逾時、暫時網路錯誤和 429 可按指數退避重試;鑑權失敗、參數錯誤和衝突需要進入永久失敗或重新計算路徑。並發 worker 數、每個外掛配額和 API QPS 必須有上限。待排程 Pod 的重試要和 API 工作重試分開計數,否則一個慢操作會放大整個佇列。
6. 相容、灰度與回滾
保留外掛 API 和排程結果語意,用 feature gate 控制非同步路徑。先在低風險外掛、低並發叢集啟用,比較排程 P99、佇列等待、API 延遲、工作失敗、重複請求和 unschedulable 重試次數。若佇列積壓、綁定錯誤或 API 壓力超過基線,停止新工作並排空或取消佇列,再回退同步路徑。
高品質示範回答
我會先確認哪些 API 操作可以延遲,哪些是綁定前置條件;前者進入有界優先級佇列,後者保留明確的 pending 狀態。工作帶 Pod UID、操作類型、冪等鍵、deadline 和取消上下文,相同鍵的請求合併,佇列滿時施加背壓。worker 只執行冪等或可安全重試的操作,暫時錯誤退避,永久錯誤回到外掛的失敗處理。完成事件觸發重新評估,過期結果絕不直接綁定。發布使用 feature gate 和小範圍灰度,監測排程 P99、佇列深度、API QPS、重複率、失敗率和綁定正確性;異常時停止非同步提交、清理工作並回退同步路徑。
常見錯誤
- 把所有 API 呼叫都放背景 → 綁定前置條件可能被繞過 → 先畫出排程上下文和安全狀態機。
- 只有一個全域 FIFO → 高優先級 Pod 被低優先級工作阻塞 → 使用優先級、aging 和配額。
- 以請求字串去重 → 不同資源版本或副作用被錯誤合併 → 使用資源鍵和明確冪等語意。
- 無限重試 → API 故障變成佇列風暴 → 設 deadline、退避、最大嘗試次數和熔斷。
- 只看吞吐 → 排程延遲和錯誤被掩蓋 → 同時觀察佇列、API、重試和綁定正確性。
追問及應對
如果相同 Pod 同時提交讀和寫,可以合併嗎?
不能只憑 Pod UID 合併。讀請求可按資源版本合併,寫請求需按操作類型、目標資源和冪等鍵判斷,並保持必要先後關係。
佇列滿時應該丟棄哪個工作?
先依 deadline、優先級和可重建性制定策略;不可丟棄的綁定前置工作應產生 backpressure。任何丟棄都要讓 Pod 進入可解釋的重試或失敗狀態,不可靜默刪除。
API 呼叫成功但 Pod 已被搶占怎麼辦?
用取消上下文和物件版本檢查阻止舊上下文繼續寫入。成功結果只記錄為稽核資訊,新的排程上下文重新評估,不重用過期綁定意圖。