寫這篇想要分享的重點:
深入剖析 Kubernetes 調度器(Scheduler)的背後運作機制,並針對惡夢般的Pending狀態提供一套搭配真實 Terminal 畫面的系統化除錯;同時掌握透過nodeName以及 Labels / Selectors 進行手動調度與分配的作法。這篇想要講什麼:
- Scheduler 如何運作?從 Filtering 到 Scoring 的選定過程。
- debug惡夢
Pending:透過 Terminal 畫面與kubectl describe快速找到資源不足、標籤不符或nodeName綁定錯誤的bug。- 手動指定節點:
nodeName強制綁定 vs. 透過 Labels 與 NodeSelector 進行彈性調度。- Labels、Selectors 與 Annotations 的概念釐清與進階篩選。
為何要寫這篇:
前面幾天我們學會了如何寫 Pod 的 YAML。但在真實的多節點叢集中,當你一口氣部署了數十個 Pod,K8s 究竟是怎麼決定要把哪一個 Pod 放到哪一台機器的?當 Pod 遲遲無法啟動、卡Pending時,光看 YAML 往往找不出問題,必須靠SOP才能抓出兇手。這篇將帶你揭開節點調度的真相!
Scheduler: 調度器 / 排程器
Filtering / Predicates: 過濾階段 / 預選條件
Scoring / Priorities: 評分階段 / 優選條件
NodeSelector: 節點選擇器
Annotations: 註釋 / 附加標註資訊
當我們建立 Pod 時,若未指定特定的 Worker Node,調度器(Scheduler)便會介入尋找最合適的執行目標。
過濾階段(Filtering):檢查所有節點是否符合 Pod 的基本條件(例如資源是否充足),不合格的節點會直接被刷掉。
評分階段(Scoring):針對通過過濾的節點進行評分,選出最優質的節點來調度。
Pending 該怎麼辦?當 Pod 遲遲無法啟動、卡在 Pending 狀態時,執行 kubectl explain pod.spec 這類查詢指令是看不出原因的。這時必須透過除錯步驟來找出問題原因:
使用 kubectl describe pod <pod-name> 指令,將畫面直接拉到最底部的 Events 區段,這是鎖定問題的關鍵:
kubectl describe pod frontend-app
Terminal 畫面與錯誤範例:
Name: frontend-app
Namespace: default
Priority: 0
Node: <none>
Labels: app=frontend
Annotations: <none>
Status: Pending
IP:
IPs: <none>
Containers:
app:
Image: nginx:latest
...
Conditions:
Type Status
PodScheduled False
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 12s default-scheduler 0/3 nodes are available: 3 Insufficient cpu.
從上面的 Terminal 輸出可以清楚看出原因:
0/3 nodes are available: 3 Insufficient cpu:代表叢集中的 3 台節點資源已經空空,沒有足夠的 CPU 容納這個 Pod。
其他常見訊息還包含 node(s) didn't match Pod's node affinity / selector(找不到符合標籤的節點)或 node(s) had untolerated taint(被節點污點擋下)。
nodeName如果一個 Pod 從頭到尾都沒有被 Scheduler 處理,很可能是因為它在 YAML 中被手動指定了錯誤的 nodeName,導致調度器直接略過不處理。
Terminal 範例(檢查 YAML):
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
nodeName: non-existent-node # ⚠️ 寫錯或指派了一台不存在的機器名稱
containers:
- name: nginx
image: nginx
當你執行 kubectl describe pod nginx 時,會發現 Node: 欄位直接被寫錯指到不存在的機器,導致 Pod 永遠無法被調度:
Name: nginx
Namespace: default
Node: non-existent-node/
Status: Pending
...
Events:
<none> # 因為有指定 nodeName,Scheduler 會直接忽略它,甚至連 FailedScheduling 事件都不會產生!
在 K8s 中,你無法直接將執行中的 Pod 從一個節點移動到另一個節點,因為運行中的行程是直接由各節點的 kubelet 來管控的。若要更換節點或修改如 nodeName 等無法動態調整的欄位,只能將 Pod 刪除,並在正確的環境中重新建立。
Terminal 範例(使用 --force 強制替換重建):
$ kubectl replace --force -f nginx.yaml
pod "nginx" deleted
pod/nginx replaced
透過 --force 參數,系統會幫你強制執行刪除並用新的 YAML 內容重新長出一顆 Pod,讓排程重新啟動。
Labels 與 Selectors 可以讓我們輕鬆對各種 K8s 物件進行分組與篩選。
--selector 過濾特定標籤。# 查詢目前命名空間下的所有 Kubernetes 物件 (包含 Pods, Services, Deployments 等)
kubectl get all \
# 使用 --selector (可簡寫為 -l) 進行多條件複合篩選
--selector \
env=prod, \ # 條件一:環境必須是生產環境 (Production)
bu=finance, \ # 條件二:業務單位必須是財務部門 (Finance)
tier=frontend # 條件三:架構層級必須是前端 (Frontend)
# 註:多個條件以逗號分隔代表「AND」邏輯,必須同時符合以上三個條件的物件才會被顯示
ReplicaSet 與 Services 中的 Labels:如果覺得可能有多個 Pod 擁有相同的 Label 但功能不同,可以指定多個 Labels 來確保正確對應。
Annotations:與用來分組和選擇的 Labels 不同,Annotations 是用來記錄像是工具版本、建置資訊、聯絡電話或email等供參考與整合用途的詳細資料。
除了依靠調度器自動選擇外,我們也可以透過手動方式將 Pod 指到特定的節點上。
# 為特定的 K8s Worker 節點新增自定義標籤,標記該節點具備 SSD 硬碟
kubectl label nodes <your-node-name> disktype=ssd
# 指令拆解:
# - kubectl label nodes:指定要對節點(Node)進行標籤操作的指令
# - <your-node-name>:你想要貼標籤的目標節點名稱(可透過 kubectl get nodes 查詢)
# - disktype=ssd:以 Key=Value 形式定義的標籤內容,供後續 Pod 的 nodeSelector 進行比對
接著在 Pod 的 YAML 規格中加入 nodeSelector,調度器便會利用這些鍵值對來找出正確的節點。不過需要注意的是,nodeSelector 只能進行簡單的 Key-Value 比對,無法直接做到像 "OR" 邏輯或範圍判斷的多條件選擇。
本篇總結:
說明了 Scheduler 從過濾到評分的運作流程,透過Terminal 畫面與 kubectl describe 系統化確認 Pending 狀態(包含 Insufficient 資源與 nodeName 綁定錯誤)的指令技巧, 一步一步除錯,並掌握透過 Labels、Selectors 與 NodeSelector 進行手動與條件式調度的應用。
下一篇預告:
掌握了基礎的節點分配與選擇器後,明天 Day 07:【調度】進階節點親和性與污點容忍機制 將帶大家解鎖更進階的 Node Affinity(支援 In、NotIn、Exists 等複雜條件)以及 Taints & Tolerations(污點與容忍機制),教你如何像管理 VIP 專區一樣,把核心服務綁定在特定的硬體規格上,千萬別錯過!
kubectl describe pod 檢查底部 Events 畫面,透過 Insufficient 或配對錯誤找出資源或標籤問題。nodeName 且機器不存在,Scheduler 會直接略過,導致 Events 空白,必須特別注意。kubectl replace --force)來觸發重新排程。