iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Kubernetes

從零到 CKA:30 天掌握 Kubernetes 核心觀念與實作系列 第 12

Day 12|Kubernetes Scheduler 與資源管理 — Pod 到底會被排到哪個 Node?

  • 分享至 

  • xImage
  •  

前言

到目前為止,我們學了很多「怎麼建東西」—— Deployment、Service、ConfigMap、PV、StatefulSet……

但有一個問題我們一直沒問:

當你 kubectl apply 一個 Pod,Kubernetes 是怎麼決定它要跑在哪個 Node 上的?

你可能以為 Pod 是「隨機分配」到某個 Node,但其實不是

Kubernetes 背後有一個專門負責排程的元件 —— kube-scheduler,它會根據 Pod 的需求與各個 Node 的狀態,挑選適合這個 Pod 執行的 Node

如果 Scheduler 找不到符合條件的 Node,Pod 就會維持在 Pending 狀態,直到出現可以被排程的 Node

今天會學:

  1. Pod 為什麼會 Pending? — 從現象理解排程失敗的原因
  2. Scheduler 的工作流程 — Filtering → Scoring → Binding
  3. Requests 與 Limits — 理解 CPU、Memory 資源設定與排程的關係
  4. 實作驗證 — 動手觀察 Pod 的排程與資源限制行為
  5. 常見錯誤與注意事項 — Requests / Limits 設太高、太低或沒有設定會發生什麼事

以下操作皆在 master 節點 執行。


一、從現象講起:Pod 為什麼會 Pending?

先來動手製造一個「卡住」的 Pod,直接觀察 Pod 進入 Pending 狀態時會發生什麼事。

建立一個指定不存在 Node 標籤的 Pod:

vim pending-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pending-demo
spec:
  nodeSelector:
    gpu: "true"        # 指定要有 gpu=true 標籤的 Node(但我們的叢集沒有)
  containers:
  - name: nginx
    image: nginx
kubectl apply -f pending-demo.yaml

查看狀態:

kubectl get pods

https://ithelp.ithome.com.tw/upload/images/20260814/20181928IDy46NQq17.png

Pod 一直 Pending,不是 Running、不是 Error,就是卡著不動。

describe 看看發生什麼事:

kubectl describe pod pending-demo

在 Events 區塊會看到類似這樣的訊息:

https://ithelp.ithome.com.tw/upload/images/20260814/20181928DrFq4u6pRO.png

丟一個問題給你思考:

  • Node 明明是 Ready
  • 為什麼 Pod 還是停在 Pending

答案就是:

Scheduler 找不到符合 Pod 排程條件的 Node。

Node 顯示 Ready,只代表這個 Node 目前可以正常工作,並不代表它一定符合這個 Pod 的排程需求。

在這個例子中,我們透過 nodeSelector 要求 Node 必須具備gpu=true

但叢集中沒有任何 Node 符合這個條件,因此 Scheduler 在 Filtering 階段就會把所有 Node 排除掉,Pod 最後只能停在 Pending

造成 Pod Pending 的原因還有很多,例如:

  • Node 的 CPU 或 Memory 資源不足
  • Node 上有 Taint,但 Pod 沒有對應的 Toleration
  • Pod 設定了 Node Affinity,但沒有任何 Node 符合條件

這些都是 Scheduler 在排程 Pod 時會檢查的項目。


二、Scheduler 的工作流程

kube-scheduler 的排程邏輯可以簡化成三個階段

https://ithelp.ithome.com.tw/upload/images/20260814/201819288Sm3eDKK9s.png

Filtering(篩選)

第一步可以把它想成淘汰賽 —— Scheduler 會先把不符合 Pod 排程條件的 Node 全部排除。

常見的淘汰原因包括:

  • CPU 或 Memory 資源不足:Node 可供排程的資源不足以滿足 Pod 的 requests
  • NodeSelector 不匹配:Pod 指定了 nodeSelector,但 Node 沒有符合的標籤
  • Taint 無法容忍:Node 有 Taint,但 Pod 沒有對應的 Toleration
  • Port 衝突:Pod 使用的 hostPort 已經被該 Node 上其他 Pod 佔用
  • 儲存拓樸限制:Pod 需要使用的 Volume 只能掛載在特定 Node 或 Zone

如果 Filtering 結束後沒有任何 Node 符合條件,Pod 就會維持在 Pending 狀態。

Scoring(評分)

通過 Filtering 的 Node 會進入評分階段

Scheduler 會透過多種 Scoring Plugin 幫候選 Node 計算分數,例如:

  • 資源配置狀況:考量 Node 的 CPU、Memory 等資源使用情況,選擇較適合的 Node
  • BalancedAllocation:考量 CPU 與 Memory 的配置比例,避免資源使用過度失衡
  • ImageLocality:如果 Node 上已經存在 Pod 需要的 Container Image,可以獲得較高分數,減少下載 Image 的成本
  • Pod Affinity / Anti-Affinity:根據 Pod 之間的親和性與反親和性規則調整 Node 分數

Scheduler 會綜合各項評分結果,最後從分數最高的候選 Node 中選出一個來執行 Pod。

Binding(綁定)

Scheduler 選定最適合的 Node 後,會透過 API Server 完成 Binding,把這個 Pod 指派到指定的 Node。

這個結果會反映在 Pod 的spec.nodeName欄位中。

接著,該 Node 上的 kubelet 會發現有新的 Pod 被指派給自己,開始進行後續工作,例如:

  • 準備 Pod 執行環境
  • 拉取 Container Image
  • 掛載需要的 Volume
  • 建立並啟動 Container

到這一步,Pod 才真正開始在選定的 Node 上執行。

💡 一句話總結

Scheduler 不是隨機分配 Pod,而是經過 Filtering(篩選)→ Scoring(評分)→ Binding(綁定) 的流程,最後將 Pod 排程到符合條件且評分較高的 Node 上。


三、Requests 與 Limits — Pod 的資源需求與限制

知道了 Scheduler 會先篩選 Node,那它是怎麼判斷「這台 Node 的資源夠不夠」的?

關鍵就在 Pod 設定的 RequestsLimits

其中:

  • Requests:代表 Pod 希望預留多少 CPU、Memory,Scheduler 會用它來判斷 Node 是否有足夠資源可以排程
  • Limits:代表 Container 最多可以使用多少資源,主要是在 Pod 執行後限制資源使用量

所以在排程階段,Scheduler 最主要看的其實是 requests

用比喻理解

想像你要訂飯店房間:

  • Requests(需求):你跟訂房系統說「我至少需要一間符合這些條件的房間」→ 訂房系統會根據你的需求判斷哪間房可以安排給你
  • Limits(上限):飯店規定「這間房最多只能使用到某個程度」→ 即使已經入住,也不能無限制使用資源

對應到 Kubernetes:

比較項目 Requests(請求) Limits(限制)
主要作用階段 排程階段 Pod 執行階段
意思 Pod 表示:「我需要保留這麼多資源」 Container 表示:「我最多可以使用這麼多資源」
Scheduler 會不會看? 會,Scheduler 主要根據 requests 判斷 Node 是否有足夠資源 通常不作為排程容量判斷的主要依據
CPU 超過時 可能被 Throttle(限速),不會因為單純超過 CPU Limit 而被終止
Memory 超過時 可能觸發 OOMKilled,Container 被終止
沒有設定時 如果沒有其他機制補上預設值,Scheduler 可能低估 Pod 的資源需求 Container 沒有設定對應上限,可能使用更多 Node 資源

YAML 怎麼寫?

apiVersion: v1
kind: Pod
metadata:
  name: resource-demo
spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      requests:          # Scheduler 排程依據
        cpu: "250m"      # 0.25 個 CPU 核心
        memory: "128Mi"  # 128 MiB 記憶體
      limits:            # 運行時的上限
        cpu: "500m"      # 最多 0.5 個 CPU 核心
        memory: "256Mi"  # 最多 256 MiB 記憶體

CPU 單位補充

  • 1 = 1 個 CPU 核心
  • 500m = 0.5 個 CPU 核心
  • 250m = 0.25 個 CPU 核心

其中 m 代表 millicpu1000m = 1 CPU

Memory 單位補充

  • Mi = MiB(Mebibyte,2^20 bytes)
  • Gi = GiB(Gibibyte,2^30 bytes)

Kubernetes 也支援 MG 等十進位單位,但它們和 MiGi 代表的容量不同。

例如:512Mi1Gi


四、實作驗證

Step 1:查看 Node 的可分配資源

先了解叢集有多少資源可用:

kubectl describe nodes | grep -A 5 "Allocatable"

或者更清楚地看單一 Node:

kubectl describe node <node-name>

在輸出中找到兩個關鍵區塊:

https://ithelp.ithome.com.tw/upload/images/20260814/20181928kCjuL7JBUN.png

  • Allocatable:這台 Node 可供 Pod 分配使用的資源總量
  • Allocated resources:目前已排程到這台 Node 上的 Pod 所設定的 requests / limits 加總

Allocatable 可以看出,這台 Node 目前大約可以提供 2 CPU、4Gi Memory 給 Pod 使用。

這裡顯示的是 Node 可分配給 Pod 的資源總量,至於目前已經有多少資源被 Pod 的 requests / limits 使用,則需要查看 kubectl describe node 中的 Allocated resources 區塊。

Step 2:部署一個有 Requests 的 Pod

vim resource-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: resource-demo
spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      requests:
        cpu: "250m"
        memory: "128Mi"
      limits:
        cpu: "500m"
        memory: "256Mi"
kubectl apply -f resource-demo.yaml

確認 Pod 正常運行:

kubectl get pod resource-demo -o wide

-o wide 會顯示 Pod 被排程到哪個 Node。

https://ithelp.ithome.com.tw/upload/images/20260814/20181928fKoQv1Ytfd.png

Step 3:製造一個排不上去的 Pod

現在試試部署一個「要求超大資源」的 Pod:

vim greedy-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: greedy-pod
spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      requests:
        cpu: "100"        # 要求 100 個 CPU 核心!
        memory: "128Gi"   # 要求 128 GiB 記憶體!
kubectl apply -f greedy-pod.yaml

查看狀態:

kubectl get pod greedy-pod

https://ithelp.ithome.com.tw/upload/images/20260814/20181928N6pLajPEih.png

果然,Pod 進入了 Pending 狀態!

接著用 describe 看看 Scheduler 為什麼無法完成排程:

kubectl describe pod greedy-pod

會在 Events 中看到:

https://ithelp.ithome.com.tw/upload/images/20260814/201819284tVLd310U8.png

意思是:目前沒有任何 Node 同時具備足夠的 CPU 與 Memory 來滿足這個 Pod 的 requests

因此 Scheduler 在 Filtering 階段就會把所有不符合資源需求的 Node 排除,Pod 最後只能維持在 Pending 狀態。

Step 4:觀察資源使用狀況

如果你的叢集有安裝 Metrics Server,可以用:

kubectl top nodes

https://ithelp.ithome.com.tw/upload/images/20260814/20181928geTAj5lFXM.png

這裡顯示的是 Node 目前 CPU 與 Memory 的實際使用量,不是 Pod 宣告的 requests

⚠️ Scheduler 看的是 Requests,不是實際使用量!

即使一台 Node 的實際 CPU 使用率只有 10%,如果上面已經排程的 Pod,其 CPU requests 加總已經接近或達到 Node 的可分配容量,Scheduler 就可能無法再把新的 Pod 排進去。

這就像飯店的房間已經被預訂了,即使房客還沒入住,新客人也不能再訂同一間房。


五、常見錯誤與注意事項

錯誤 1:完全不設定 Requests 和 Limits

如果沒有設定 requests

  • Scheduler 可能低估這個 Pod 實際需要的資源
  • Pod 可能被排到資源已經相當吃緊的 Node
  • 多個 Pod 同時競爭資源時,可能造成效能不穩定

如果沒有設定 limits

  • Container 可能使用超出預期的 CPU 或 Memory
  • 當應用程式失控時,可能影響同一台 Node 上的其他 Pod

在生產環境中,建議至少合理設定 Requests,讓 Scheduler 能正確判斷資源需求;是否設定 Limits,則應依應用程式特性與資源管理策略決定。

錯誤 2:Requests 設太高(資源利用率降低)

如果應用程式實際只需要約 100m CPU,但 requests 卻設定成 2000m

  • Scheduler 會把這個 Pod 視為需要 2 CPU
  • 這部分資源會被當成已預留的排程容量
  • 即使 Node 的實際 CPU 使用率很低,其他 Pod 也可能因為可用 Requests 容量不足而進入 Pending

所以 requests 不宜隨意設得過高,應根據應用程式實際需求與監控數據進行調整。

錯誤 3:Limits 設太低

如果 limits 設得太低,CPU 和 Memory 會有不同的結果:

  • CPU Limit 太低:Container 可能會被 Throttling(限速),通常表現為應用程式變慢,但不會因為單純超過 CPU Limit 而被終止
  • Memory Limit 太低:當 Container 使用的記憶體超過限制時,可能會被 OOMKilled,導致 Container 被終止並重新啟動

可以用以下指令排查是否曾經發生 OOMKilled

kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[0].lastState}'

如果看到類似:

reason: OOMKilled 

就代表 Container 曾經因為超過 Memory Limit 而被 OOM Kill,需要重新評估記憶體需求與 limits 設定。


小結

今天我們學會了 Kubernetes 的排程與資源管理機制:

重點 說明
Scheduler 三階段 Filtering(篩選)→ Scoring(評分)→ Binding(綁定)
Requests Pod 宣告的資源需求,是 Scheduler 判斷 Node 是否有足夠資源的重要依據
Limits Container 的資源使用上限,CPU 超過可能被 Throttling,Memory 超過可能被 OOMKilled
Pod Pending 可能是 Scheduler 在 Filtering 階段找不到符合條件的 Node
資源觀察 kubectl top 看實際使用量,kubectl describe node 可查看 Requests / Limits 的配置情況

現在我們知道 Scheduler 會根據各種條件篩選 Node,但如果想要更精確地控制 Pod 該跑在哪裡呢?

例如:

  • 「這個 Pod 只能跑在有 GPU 的 Node」
  • 「這個 Pod 優先跑在 SSD Node」
  • 「這個 Pod 不要跟另一個 Pod 排在同一台 Node」

明天我們來學 Taints & Tolerations 與 Node Affinity,看看 Kubernetes 如何用更細緻的規則控制 Pod 的排程位置!


參考資源


上一篇
Day 11|StatefulSet — 讓有狀態應用穩定運行
下一篇
Day 13|Node Affinity 與 Taints & Tolerations — 精準控制 Pod 的排程位置
系列文
從零到 CKA:30 天掌握 Kubernetes 核心觀念與實作15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言