iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Kubernetes

初見 Kubernetes - 純網站後端開發踏入 K8s 世界的經驗分享系列 第 11 篇

Day 11 - Service:ClusterIP、LoadBalancer、NodePort

  • 分享至 

  • xImage
  •  

到目前為止,我們已經建立 Deployment 與 Pod replica,也知道 Pod 可以被刪除、重建並重新排程。

不過假設某個 API 要呼叫另一個服務,卻把對方某一顆 Pod 的位址直接填在程式設定裡,例如 http://10.244.1.7:8080。如果那顆 Pod 被刪除後重新建立,新 Pod 可能會換一個 IP,而程式仍呼叫舊 IP,就可能找不到服務。

Service 就是 Kubernetes 提供的穩定入口,常會透用 selector 找出目前符合條件的 Pod,讓呼叫端不必每次都因為變動的 Pod IP 而去頻繁的改相關設定或程式碼。 

Service 如何找到後端 Pod?

以下是一個簡單範例:

yaml
apiVersion: v1
kind: Service
metadata:
name: catalog-api
spec:
selector:
app.kubernetes.io/name: catalog-api
ports:
- name: http
port: 80
targetPort: http

這份 Service 有三個主要設定:

欄位 意義
selector 挑出帶有指定 Label 的後端 Pod。
port Service 對呼叫端提供的 Port。
targetPort 流量最後要送進 Pod container 的 Port;可填數字,也可指向 container port 的名稱。

對有設定 selector 的 Service,Kubernetes Control Plane 中的 EndpointSlice controller(端點切片控制器) 會依據符合條件的 Pod,建立或更新 EndpointSlice(端點切片)。

EndpointSlice 是 Kubernetes 用來記錄 Service 後端端點的物件,內容包含 Pod 位址、Port 與端點狀態。

當 Pod 數量較多時,同一個 Service 也可能對應多個 EndpointSlice。這個 controller 是 Kubernetes 的內建 Controller,會隨著 Service 或 Pod 狀態變化協調更新後端清單。實際流量通常會導向其中 Ready 的 endpoint。

Pod 被 Deployment 替換時,EndpointSlice 也會隨之更新。

所以從上述也可以了解到,Service 本身不參與建立、刪除 Pod 的相關事件。

https://ithelp.ithome.com.tw/upload/images/20260925/20124323J1qSC3LQ1r.jpg

實際封包要怎麼在 Node 上被轉送,會依叢集的網路實作而不同。

例如 kube-proxy 是常見的 Service 實作元件,某些 CNI 方案也能實作或接手處理分 Service 流量。不論底層細節如何,應用程式層的重點是:呼叫 Service,不要直接依賴某一顆 Pod IP。

三個最常用的 Service Type

Service Type 決定 Service 主要在哪個範圍可被存取。今天的主角是 ClusterIP、NodePort 與 LoadBalancer。

https://ithelp.ithome.com.tw/upload/images/20260925/20124323fX55D3TmC7.jpg

Type 主要用途 從哪裡可連線 初學時應注意
ClusterIP 叢集內服務互相呼叫 叢集內 Pod 預設類型,通常是叢集內的 Pod 與 Pod之間溝通最常用的入口。
NodePort 在每台 Node 開出同一個 Port 可到達任一 Node IP 的用戶端 需要處理 Node 網路、防火牆與 Port 範圍;通常不會被做為正式的對外入口。
LoadBalancer 請平台提供外部 Load Balancer 由平台配置的外部位址 是否真的拿到外部位址,取決於套件以及公、私有雲的相關配置。

ClusterIP:給叢集內部服務使用的預設入口

未指定 type 時,Service 預設就是 ClusterIP。Kubernetes 會配置一個叢集內可使用的虛擬 IP,讓其他 Pod 可以透過 Service name 連線。

同一個 Namespace 中,呼叫端通常可以直接使用:

text
http://catalog-api

叢集的 DNS Service(例如 CoreDNS)會把 Service name 解析成對應的叢集內的入口。與直接叫某一顆 Pod 不同的是:後端 Pod 即使被替換,catalog-api 這個 Service 名稱仍保持不變。

NodePort:每台 Node 都開出同一個 Port

NodePort 會讓每台 Node 的 Service proxy 配置同一個 Port,並在叢集設定允許的 Node IP 上接受流量。只要外部用戶端能連到其中一台可達的 Node,就能經由 NodeIP:NodePort 進入該 Service,再被轉送到符合 selector 的 Pod;預設行為仍會受到 kube-proxy mode 與 nodePortAddresses 設定影響。

它適合學習或某些直接暴露服務的情境,但要注意它暴露的是 Node 層級入口,不代表 Pod 一定就在你連到的那台 Node 上。也因為要處理 Node IP、網路路由與防火牆,正式環境通常會再交由專門的 Load Balancer、Ingress 或 Gateway 來管理對外流量。

LoadBalancer:Kubernetes 提出需求,平台負責配置

LoadBalancer 不代表 Kubernetes 自己憑空產生一台負載平衡器。它是向叢集所整合的平台提出需求:請建立或配置一個外部 Load Balancer,並把流量導向這個 Service。

VIP:可被叢集外部看見的虛擬 IP

VIP(Virtual IP,虛擬 IP) 是用戶端用來連線的入口 IP,它不屬於某一顆固定 Pod 或單一網卡。
Load Balancer 可以把送到這個 IP 與 Port 的連線,依據不同規則轉送到對應的 Service。

使用者連線到 https://api.example.com 時,DNS 可將名稱解析到一個 VIP;使用者不需要知道後面有幾顆 Node、Pod 是否剛被重建,或實際流量最後進到哪一顆 Pod。這正是外部 Load Balancer 與 Service 要共同提供的穩定入口,整體概念上會有點像 nginx 的反向代理。

叢集完成配置後,會將可供連線的資訊寫入 Service 的 .status.loadBalancer.ingress;可以設定 ip,也可以設定 hostname。因此建議是讓 Client 端指向對應的 FQDN,而不是 VIP。

同一份 type: LoadBalancer YAML,在不同平台背後可能是完全不同的提供者:

環境 常見的提供者與結果
VMware Tanzu / vSphere 搭配 NSX ALB AKO 將 Service 需求同步給 NSX Advanced Load Balancer(Avi),由 Avi 建立 Virtual Service 與其 VIP。
AKS AKS 建立 Azure Load Balancer、配置公用或私用 IP,並設定後端集區與規則。
EKS EKS Auto Mode 可為 LoadBalancer Service 配置 AWS Network Load Balancer;自行管理的 EKS 叢集常使用 AWS Load Balancer Controller 建立與協調 NLB。
自建/裸機 Kubernetes 可安裝 MetalLB 這類 CNCF Sandbox 專案,從管理者設定的 IPAddressPool 配發 IP,並透過 L2(ARP/NDP)或 BGP 對外宣告該 VIP。

所以,type: LoadBalancer 是 Kubernetes 的通用介面,不是某一種固定產品或網路拓樸。Kubernetes 也提供 .spec.loadBalancerClass,讓叢集可明確指定由哪一種 Load Balancer 實作接手;具體註解、可用協定、健康檢查與是否能直接導向 Pod,都要查閱該平台或 Controller 的文件。

另外兩種:ExternalName 與 Headless Service

ExternalName 與前面三種不同,它不會建立 selector、EndpointSlice 或 Proxy,而是回傳一個外部 DNS 名稱的 CNAME。它適合建立名稱別名,但不適合拿來取代一般 Pod Service。

Headless Service 則是將 clusterIP 設為 None 的特殊設定,不是額外的 Service Type。它通常讓呼叫端直接取得後端 Pod 的 DNS 資訊,常見於需要自行處理節點身分或連線對象的 Stateful 工作負載。這兩種先知道存在即可。

Docker Compose 的 service name 與 container_name 有何不同?

Docker Compose 與 Kubernetes 都能讓服務用名稱互相呼叫,但背後模型不同:

Docker Compose Kubernetes
Compose service name 通常可作為同一 Compose network 內的 DNS 名稱。 Service name 是 Kubernetes API object 的名稱;常見情況會對應一群符合 selector 的 Pod,也可以對應手動 EndpointSlice 或 ExternalName 的 DNS alias。
container_name 是特定 container 的固定名稱。 Pod 名稱是個別執行個體名稱,會隨重建或擴縮改變。
設定 container_name 的 Compose service 不能擴成多個 container。 Service 本身就是為多個可替換 Pod 提供一個穩定入口。

因此,從 Docker Compose 遷移時,最接近「呼叫服務名稱」的概念是 Kubernetes Service。

實務案例:External IP 一直是 <pending>,不代表是 selector 壞掉

曾經遇過 LoadBalancer Service 的 External IP 一直停在 <pending>。後端 Pod 正常、selector 也正確,但平台無法分配對應 IP:指定的 IP 不在 IPAM Pool 範圍內,或是可用 IP 已耗盡。

後來發現應該往這些邏輯去排查才比較有效率:

  • Service、selector、EndpointSlice 是否正確,是 Kubernetes 物件與後端 Pod 的問題。
  • 外部 IP、VIP、平台 Load Balancer 是否可配置,是否為該叢集平台與 Controller 的問題。

遇到這類情況,先看 kubectl describe service 中的 Event,比起直接修改 selector 或重建 Pod 更容易找到根因。

今日結論

Service 讓呼叫端以穩定名稱與入口存取一群可替換的 Pod。ClusterIP 適合叢集內流量;NodePort 在 Node 層級開 Port;LoadBalancer 則要求平台配置外部入口。

參考資料


上一篇
Day 10 - Labels 與 Selectors
下一篇
Day 12 - ConfigMap 與 Secret:一般設定資料與敏感資料要如何分開
系列文
初見 Kubernetes - 純網站後端開發踏入 K8s 世界的經驗分享 共 16 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言