Pod、Node、Scheduler:Kubernetes 叢集的三個原子

· tech

#kubernetes#concept

上一篇講了 K8s 的靈魂:你宣告期望,reconcile loop 不斷把現實拉過去。但那句「我要 3 個」——3 個什麼?落在哪台機器上?誰決定放哪? 這篇把叢集最基本的三個原子講清楚:Pod、Node、Scheduler。

Pod:最小單位,而且不是「容器」

第一個反直覺的點:K8s 排程與伸縮的最小單位不是容器,是 Pod。 一個 Pod 包住一個(或少數幾個)容器,讓它們同生共死、共享網路與儲存:

Pod 最小部署單位・排程與伸縮的單位 容器:app 你的服務 容器:sidecar 選配(log / proxy) 共享:一個 IP(彼此用 localhost 通)· 共享 Volume
Pod 把「同生共死」的一或多個容器包成一個單位,共享網路(同一 IP)與儲存。多數 Pod 只有一個容器;sidecar 是選配

為什麼要多這層包裝?因為有些容器天生該綁在一起 —— 例如主服務 + 一個幫它收 log 或當代理的 sidecar,它們要共用網路、一起被排程、一起生死。但別過度用:多數 Pod 就是一個容器。 記住這句話就好:容器是「跑什麼」,Pod 是 K8s 真正搬動、複製、排程的單位。

Node:一台機器

Node 就是一台真正的機器(雲上多半是一台 VM)。上一篇提過叢集分兩半:

  • Control Plane(大腦):決定要跑什麼、排到哪、持續修正落差。
  • Worker Node(工人):真正跑你 Pod 的機器。每台上面有個 kubelet,負責照顧本機的 Pod、回報狀態給大腦。

所以「我要 3 個 Pod」的實際樣子,就是這 3 個 Pod 被分配到某幾台 Node 上跑。而決定「哪個 Pod 去哪台 Node」的,是第三個原子。

Scheduler:決定 Pod 落哪台

當 reconcile loop 要補一個新 Pod,這個 Pod 一開始是 Pending(還沒有 Node)。Scheduler 的工作,就是幫它挑一台落腳:

新 Pod Pending · 要 2 CPU Scheduler ① 過濾:裝得下、規則允許 ② 評分:挑最佳 Node A · 已滿 ✗ 剩 0.5 CPU → 裝不下 Node B · 挑中 ✓ 剩 4 CPU → 綁這台 Node C · 被 taint 擋 ✗ 有污點,Pod 沒 toleration
Scheduler 先「過濾」出裝得下、規則允許的 node,再「評分」挑最佳綁上去 —— reconcile loop 幫你補 Pod 時,那顆新 Pod 就是這樣找到落腳處的

過濾與評分背後的旋鈕——requests/limits、node 親和性、taint/toleration——是後面控制「誰跑在哪」的關鍵,這個系列之後會專門一篇講。這裡先知道:Pod 落在哪台不是隨機,是 Scheduler 依資源與規則算出來的。

關鍵心態:Pod 是短命的

最後一個一定要現在就建立的觀念:Pod 是用完即丟的。 它掛掉、被重排、滾動更新換新版時,舊的 Pod 直接消失,reconcile loop 開一個全新的 Pod 補上——新 Pod 有新的 IP。

這代表:你永遠不該記住某個 Pod 的 IP、也別把狀態存在 Pod 裡。 Pod 是牛,不是寵物——壞了就換一頭,不是抱著醫。這帶出兩個之後的主題:既然 Pod 的 IP 會變,別的服務怎麼穩定找到它?(→ Service,系列第 4 篇);Pod 沒了資料就沒了,那資料庫這種有狀態的怎麼辦?(→ Volume 與 StatefulSet,第 6 篇)。

反思

「Pod 不是容器」這個區分,想通就順了

我剛學 K8s 時卡最久的,就是「明明是跑容器,為什麼要多一個 Pod?」後來想通:Pod 是 K8s 的排程單位,容器是執行單位。 K8s 要搬動、複製、放置的是 Pod;至於 Pod 裡放一個還是幾個容器,是「這些東西要不要同生共死」的設計選擇。九成情況一個 Pod 一個容器,別為了炫技硬塞 sidecar。把「單位」這件事分清楚,後面 Deployment 管 Pod、Service 指向 Pod、排程排的是 Pod——全部一次順起來。

Pod 短命是特性,不是 bug

「Pod 隨時會消失、IP 會變」聽起來很不安,但這其實是 K8s 自我修復的前提,不是缺陷。正因為 Pod 被當成可拋棄的,壞了才能無痛換一個。這跟我在 SparkSpark on K8s 那幾篇反覆講的「executor 用完即刪」是同一種思維——把運算單位當成牛而非寵物。一旦接受這點,你就不會去做「記住某個 Pod」這種注定會痛的事,而會轉去用 K8s 給你的穩定抽象(Service、Volume)。

Scheduler 幫你想「放哪」,但你可以介入

預設 Scheduler 會自動挑一台裝得下的 node,多數時候你不用管。但當你有「這批 Pod 要跑在高記憶體機器」「別跟那個吵鄰居擠同一台」這種需求時,requests、affinity、taint 這些旋鈕就是你介入的方式——我在 Airflow + Spark on K8s 那篇用它把 Airflow 核心釘在穩定 node、把 Spark executor 丟去便宜的 spot node,就是這個道理。先讓 Scheduler 自動幫你放,真的有需求再出手調——這也是我對所有 K8s 進階功能的態度:預設夠用,別為了用而用。