題幹與適用場景
這道系統設計題面向平台工程師、DevOps、SRE 和負責 Kubernetes 執行環境的後端工程師。題目同時要求高可用、資源利用率和滾動擴縮。回答不能只貼一段 YAML,必須解釋故障域、候選 Pod 集合、skew 計算、硬軟約束和排程失敗後的運維動作。
面試官考察點
- 能否把「跨可用區」翻譯成正確的
topologyKey、標籤選擇器和maxSkew。 - 能否區分
DoNotSchedule的硬約束與ScheduleAnyway的軟偏好,並說明業務代價。 - 能否發現標籤不匹配、區域無節點、節點污點、滾動發布和縮容造成的隱藏風險。
- 能否用 Pending、skew、可用區容量和故障演練證明方案,而不是把 YAML 當成保證。
回答前需要釐清的問題
先確認「區域不可用」是整區故障、節點池暫時為空,還是只有資源不足;確認服務是否允許降副本、跨區流量和更高成本。詢問期望的最小副本數、單 Pod 資源請求、Pod 標籤是否由同一個 Deployment 管理、是否有多個版本同時滾動,以及是否必須至少覆蓋幾個域。若必須覆蓋的域數小於 minDomains,硬約束可能讓 Pod 長時間 Pending,這需要業務明確取捨。
30 秒回答框架
我會先用 topology.kubernetes.io/zone 作為區域故障域,再用 kubernetes.io/hostname 作為節點級補充約束,並確保 selector 與 Pod 自身標籤一致。maxSkew: 1 和 DoNotSchedule 適合副本數足夠且不能接受集中;容量緊張或只需要偏好時改用 ScheduleAnyway。如果要保證至少覆蓋 N 個區域,再評估 minDomains,同時準備 Pending 告警、跨區流量和降級策略。最後透過節點驅逐、區域縮容、滾動更新和擴容回放驗證實際分布。
分步深入解答
1. 先定義域和統計集合
topologyKey 指定域的節點標籤,例如區域或主機名;labelSelector 決定哪些已有 Pod 參與 skew 計算。selector 必須匹配該工作負載的 Pod,否則 Pod 可能不計入自己的分布,形成「ghost pod」,排程結果看似滿足卻與實際副本不符。域標籤也要在所有相關節點上穩定存在。
2. 從 maxSkew 推導硬約束
maxSkew 限制候選節點所在域與最少 Pod 域之間允許的差值。假設三個區域目前計數為 1、1、0,maxSkew: 1 時把新 Pod 放到空區域不會超過差值;若放到已有 1 個 Pod 的區域,差值變成 2,硬約束會拒絕。真實計算還受 selector、節點過濾和可用域集合影響,不能只按 Deployment 副本數心算。
3. 選擇 DoNotSchedule 或 ScheduleAnyway
DoNotSchedule 把 skew 當作過濾條件,無法滿足時 Pod 保持 Pending;它適合「集中到一個故障域就違反可用性目標」的服務。ScheduleAnyway 仍會優先降低 skew,但容量不足時允許落到較差域,適合先保住容量和可用性的服務。選擇應由副本不足的業務損失與跨區成本共同決定,並配套告警而不是靜默接受失衡。
4. 用第二個約束限制節點級集中
區域均衡不保證同一區域內不會把多個副本放到同一節點。可以增加 topologyKey: kubernetes.io/hostname 的約束,但要重新評估節點數量和資源請求。節點級 DoNotSchedule 在小叢集或大 Pod 下很容易阻塞滾動更新;此時可把節點級條件設為軟偏好,或先擴容節點池再發布。
5. 處理 minDomains、污點與空域
如果要求至少在若干域上分布,可使用 minDomains 配合 DoNotSchedule;它要求滿足的域數有明確前提,不能把「叢集目前沒有該域節點」誤當成已經覆蓋。節點親和、污點容忍和 nodeAffinityPolicy、nodeTaintsPolicy 會改變參與計算的節點集合。自動擴縮還可能把某個節點池縮到零,使排程器暫時看不到該域;需要與 autoscaler 的拓撲感知能力和節點標籤治理配合。
6. 為故障、發布和縮容建立驗證回路
在類生產叢集記錄每個區域、節點的匹配 Pod 數、Pending 原因和跨區請求比例。演練驅逐一個區域、讓一個區域無可排程容量、把副本從 2 調到 15、執行滾動更新和縮容;檢查服務可用副本、skew、發布耗時、流量成本和恢復時間。縮容後分布可能暫時失衡,不能只看一次 kubectl get pods,要驗證控制器後續是否重新排程或由 descheduler 修復。
高品質示範回答
我會把區域作為第一故障域、節點作為第二故障域,並讓 selector 精確匹配 Deployment 的標籤。區域約束可從 maxSkew: 1 開始:對關鍵 API 使用 DoNotSchedule,讓無法滿足時的 Pending 直接暴露容量問題;對允許短時集中或叢集經常容量緊張的服務使用 ScheduleAnyway,但用 skew 和可用副本告警失衡。若業務必須至少覆蓋三個區域,再評估 minDomains 與區域節點池是否始終存在。節點約束要根據節點數和 Pod 資源請求決定硬或軟,不能盲目疊加。最後用區域驅逐、零容量域、擴縮容和滾動更新演練,檢查 Pending 原因、skew、跨區流量與恢復時間。
常見錯誤
- 只寫
topology.kubernetes.io/zone→ 同一區域內仍可能集中到一個節點 → 按容量增加節點級約束並評估成本。 - 把
maxSkew: 1當成永遠均勻 → 標籤集合、可見域和縮容會改變統計 → 記錄實際匹配 Pod 與域集合。 - 所有服務都使用
DoNotSchedule→ 小叢集或單區容量不足會造成發布阻塞 → 按業務損失選擇硬或軟策略,並告警失衡。 - selector 不匹配 Pod 標籤 → 自身副本可能不參與計算,產生 ghost pod 行為 → 讓模板標籤與 selector 有契約測試。
- 忽略區域節點池縮到零 → 排程器暫時不知道該域,期望的覆蓋可能無法實現 → 讓 autoscaler 與拓撲標籤和域容量共同治理。
追問及應對
兩個副本、三個區域時能保證每區一個嗎?
不能同時滿足三域各一和只有兩個副本。應明確「至少覆蓋兩個域」或允許一個域沒有副本;硬約束只能避免超過設定 skew,不能憑空增加副本。若業務要求三域冗餘,副本下限必須至少為三,並驗證每個域都有可用容量。
為什麼 Pod 仍然 Pending?
檢查事件中的 spread constraint、節點標籤、selector、資源請求、污點容忍、親和規則和 minDomains。區分「沒有節點滿足過濾條件」和「軟約束評分較低」;必要時先擴容目標域,再降低硬約束或暫時調整發布策略。
ScheduleAnyway 會不會讓高可用目標失效?
會降低保證強度。它只提高滿足 spread 的節點得分,容量不足時仍可能集中。只有業務接受短時失衡且有可觀測告警、自動修復和容量計畫時使用;關鍵控制面或有明確故障域目標的服務保留硬約束。
滾動更新時舊版本和新版本如何一起統計?
selector 若同時匹配兩個版本,舊 Pod 會影響新 Pod 的 skew;若只匹配新版本,發布期間可能暫時失去整體分布約束。應把版本標籤策略、matchLabelKeys 的使用和最大不可用副本一起演練,確認升級高峰不會讓兩個版本擠在同一域。
縮容後失衡需要立即搬遷嗎?
拓撲約束主要影響新 Pod 排程,移除 Pod 不會自動保證剩餘 Pod 再次均勻。若失衡影響風險,使用 descheduler 或滾動重建,並先驗證遷移對容量、PDB、延遲和跨區流量的影響。