昨天我們體驗了三種 Service 類型,知道 Service 能提供穩定入口、自動負載均衡。
但你有沒有想過:當流量打到 Service 的 ClusterIP,到底是「誰」在背後把封包轉給正確的 Pod?
答案不是魔法,而是一套很具體的機制:Label Selector 決定「轉給誰」,kube-proxy 決定「怎麼轉」。
今天我們會從三個層面拆解 Service 的運作原理:
以下操作皆在 master 節點 執行,沿用 Day 4 的
php-apacheDeployment 與 Service。
Service 不是靠 Pod 名稱或 IP 來找後端的,而是靠 Label(標籤)。
kubectl get pods --show-labels
你會看到每個 Pod 都帶著 app=php-apache 這個標籤。
kubectl describe svc php-apache

在輸出中找到 Selector 欄位:
Selector: app=php-apache
這代表什麼? Kubernetes 會持續找出叢集中所有帶有 app=php-apache Label 的 Pod。只要 Pod 符合 Selector 條件且處於 Ready 狀態,Service 就可以將流量轉送到這些 Pod。
💡 Label Selector 的核心邏輯:
Service 不關心 Pod 叫什麼名字、IP 是多少,它只看 Label 有沒有 match。
這就是為什麼 Pod 重建後(名字和 IP 都變了),Service 依然能找到它 — 因為新 Pod 的 Label 沒變。
挑其中一個 Pod,把它的 Label 改掉:
kubectl label pod php-apache-6d768f465b-6drng app=removed --overwrite
用 kubectl get pods --show-labels 確認一下:

可以看到 php-apache-6d768f465b-6drng 的 Label 已經從 app=php-apache 變成 app=removed,因此不再符合 Service 的 Selector 條件,也就不會再接收到 Service 轉送的流量。
Service 透過 Selector 找到的 Pod 清單,會被記錄在 Endpoints(或更新的 EndpointSlice)裡,你可以用以下指令確認目前有幾個 Pod 在 Service 後面:
# 查看 EndpointSlice(推薦,1.33+)
kubectl get endpointslices -l kubernetes.io/service-name=php-apache
# 或傳統方式
kubectl get endpoints php-apache

簡單來說,Endpoints 就是一份動態的 Pod IP + Port 清單,每當 Pod 新增、刪除或變成 Not Ready,它都會自動更新。kube-proxy 就是靠讀取這份清單來決定流量要轉給誰。
💡 這個實驗證明了:Label 是 Service 和 Pod 之間唯一的連結。改掉 Label = 斷開連線。
Endpoints vs EndpointSlice
舊的 Endpoints 會將所有後端端點集中在同一個物件中。當 Pod 數量很多時,每次更新都需要處理整份資料。
EndpointSlice 則會將端點拆分成多個 Slice(預設每個 Slice 最多約 100 個端點),當後端發生變動時,只需更新受影響的 Slice,因此在效能與擴展性上更適合大型叢集。
Kubernetes 1.33 起已將 Endpoints API 標記為 deprecated,建議優先使用 EndpointSlice。
現在我們知道 Service 靠 Label 找到 Pod,結果存在 Endpoints 裡。但 實際把封包從 ClusterIP 轉到 Pod IP 的是誰?
答案是 kube-proxy。
kube-proxy 是跑在每一個 Node 上的元件,它會:
kubectl get pods -n kube-system -l k8s-app=kube-proxy
每個 Node 上都會有一個 kube-proxy Pod(以 DaemonSet 方式部署)。
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
你會看到 mode 欄位,常見的值是 "" (空字串 = 預設 iptables)、"nftables" 或 "ipvs"。
kube-proxy 支援三種轉發模式,它們都能完成工作,但方式和效能各有不同。
iptables 是 Linux 核心內建的封包過濾框架。在這個模式下,kube-proxy 會把每個 Service 的轉發規則寫成 iptables 規則鏈。
讓我們來看看目前系統上的 iptables 規則。這裡用 -t nat 指定查看 NAT 表(因為 DNAT 轉發發生在這裡),-S 則是以指令格式列出所有規則:
sudo iptables -t nat -S KUBE-SERVICES
你會看到每個 Service 都對應一串規則,透過 DNAT(Destination NAT)將 ClusterIP 的流量轉發到後端 Pod IP。

iptables 的運作方式:
.gif?table=block&id=3113b2b2-a07b-8079-9db5-ef8fadfc7afc&spaceId=6c530871-3a02-4b92-931e-aac641a36a83&expirationTimestamp=1786096800000&signature=hTzOlnMFeNKF9emBrpaMa70qGxJr8NH3MuLrBhD1ncc&downloadName=iptable+%28online-video-cutter.com%29.gif)
Client → ClusterIP:80
↓ (iptables DNAT)
Pod-1:80 (機率 33%)
Pod-2:80 (機率 33%)
Pod-3:80 (機率 34%)
⚠️ iptables 模式的限制
- iptables 規則主要依序進行匹配,當 Service 與 Endpoint 數量增加時,規則數量也會隨之增加。
- 大量 Service 與後端 Pod 可能產生數千甚至上萬條 iptables 規則。
- 當 Service 或後端端點發生變化時,kube-proxy 需要同步更新相關規則。
- 在大規模叢集中,大量規則的同步與更新可能增加處理時間,並影響網路效能。
nftables 是 Linux 核心中較新的封包過濾與封包處理框架,也被視為 iptables 的後繼技術。kube-proxy 在 Kubernetes 1.29 引入 nftables 模式(Alpha),並於 Kubernetes 1.33 達到 GA(General Availability,正式穩定版)。
nftables 的優勢:
nftables 不需要額外的核心模組,直接編輯 kube-proxy 的 ConfigMap:
kubectl edit configmap kube-proxy -n kube-system
把 mode 改成 "nftables",然後重啟 kube-proxy:
kubectl rollout restart daemonset kube-proxy -n kube-system
驗證切換成功:
kubectl logs -n kube-system -l k8s-app=kube-proxy | grep "Using nftables"
nftables 的定位
nftables 是 kube-proxy 較新的代理模式,主要用來改善 iptables 在大規模叢集中的效能與規則更新效率。
相較於 iptables,nftables 可以利用 map 等資料結構進行更有效率的查找,並支援較具增量性的規則更新,因此在 Service 與 Endpoint 數量較多時具有更好的擴展性。
目前 iptables 仍是 kube-proxy 的預設模式,但 Kubernetes 已將 nftables 視為未來建議採用的方向,並建議原本使用 IPVS 的環境評估遷移至 nftables。
IPVS(IP Virtual Server)是 Linux 核心中專門做負載均衡的模組,效能遠優於 iptables。
.gif?table=block&id=3113b2b2-a07b-800d-94de-df095d12b0a4&spaceId=6c530871-3a02-4b92-931e-aac641a36a83&expirationTimestamp=1786096800000&signature=gqW0hQNwimjN0yQObtX-rN12qxi6WG9tLxzfZyie6ak&downloadName=ipvs+%28online-video-cutter.com%29.gif)
IPVS 的優勢:
先確認 Node 上有載入必要的核心模組:
sudo modprobe ip_vs
sudo modprobe ip_vs_rr
sudo modprobe ip_vs_wrr
sudo modprobe ip_vs_sh
lsmod | grep ip_vs
然後編輯 kube-proxy 的 ConfigMap:
kubectl edit configmap kube-proxy -n kube-system
把 mode 改成 "ipvs",然後重啟 kube-proxy:
kubectl rollout restart daemonset kube-proxy -n kube-system
驗證切換成功:
kubectl logs -n kube-system -l k8s-app=kube-proxy | grep "Using ipvs"
如果看到 Using ipvs Proxier 就代表成功了。
什麼時候會使用 IPVS?
- 當叢集中有大量 Service 與 Endpoint,iptables 規則數量過多、效能開始受到影響時,可考慮使用 IPVS。
- IPVS 支援多種負載均衡演算法,例如 Round Robin、Least Connection 等。
- IPVS 過去常用於大型 Kubernetes 叢集,但隨著 nftables 模式成熟,新建環境已不建議優先選擇 IPVS,可優先評估 nftables。
| 比較項目 | iptables | nftables | IPVS |
|---|---|---|---|
| 查找方式 | 規則匹配,規模增大時成本較高 | Map / Set 查找,接近 O(1) | Hash-based 查找 |
| 負載均衡方式 | 隨機選擇後端 | 隨機選擇後端 | 支援 RR、LC、SH 等多種 scheduler |
| 規則更新 | 仍需處理與整體規模相關的規則更新 | 增量更新,只處理變動的 Service / Endpoint | 增量更新 |
| 額外核心模組 | 不需要 | 不需要 | 需載入 ip_vs 等核心模組 |
| 適合情境 | 相容性優先、一般規模叢集 | 新建中大型叢集,較推薦 | 既有大型叢集或特定需求 |
| 目前狀態 | 預設模式 | Kubernetes 1.33 GA,需手動啟用 | 可用,但新環境不建議優先採用 |
把今天學到的串起來,一個請求從 Client 到 Pod 的完整路徑:
Client
│
▼
Service(ClusterIP)
│
│ ① Service 的 Selector 定義要選取哪些 Pod
│ → 控制平面更新 EndpointSlice
│
│ ② kube-proxy 監看 Service / EndpointSlice
│ → 設定流量轉送規則
│
▼
kube-proxy(iptables / nftables / IPVS)
│
│ 根據轉送規則選擇一個 Endpoint
│ 並將流量導向對應的 Pod
│
▼
Pod(實際處理請求)
今天我們拆解了 Service 背後的完整機制:
| 元件 | 角色 | 說明 |
|---|---|---|
| Label Selector | 定義後端 Pod | Service 透過 Label Selector 指定哪些 Pod 屬於自己的後端。 |
| EndpointSlice | 記錄後端端點 | 控制平面根據 Selector 結果,動態維護可供 Service 使用的後端 IP 與 Port。 |
| kube-proxy | 設定流量轉送規則 | 執行於各個 Node,監看 Service 與 EndpointSlice,並建立對應的流量轉送規則。 |
| iptables / nftables / IPVS | 實作封包轉送 | kube-proxy 可透過不同的 Linux 核心機制實作 Service 流量轉送,其中 nftables 是目前較新的方案。 |
今天我們先把 Service 的運作方式拆清楚,了解它如何透過 Selector、EndpointSlice 與 kube-proxy,將流量穩定導向後端 Pod。
不過,Service 解決的是「如何存取一組 Pod」的問題。當系統裡開始出現多個 Service,而且這些服務都需要對外提供 HTTP/HTTPS 存取時,就會遇到下一個問題:這些外部流量要怎麼統一管理與分流?
明天我們會接著介紹 Ingress,看看它如何透過單一入口與路由規則,將不同網域或路徑的流量導向對應的 Service!