主線
KAI K8s之路 03|Service 是什麼?Pod 會換,入口不能飄
Pod 可以被 Deployment 不斷替換,但呼叫方不能每天追新的 Pod IP。Service 把一組可替換 Pod 包成穩定入口,讓流量能透過 DNS、ClusterIP、selector 和 EndpointSlice 找到真正 ready 的後端。
Pod 會換,IP 會飄;Service 是把一組 Pod 變成穩定入口的網路抽象。
Debug Service 不要只盯 ClusterIP;順著 selector、EndpointSlice、Pod readiness 和 targetPort 一路查。
上一篇講 Deployment 時,我們先接受一件事:Pod 不該被手動照顧。
Pod 掛了可以重建,版本換了可以 rollout,node 出事也可能被重新排程。這些都很正常。
但這裡馬上出現下一個問題:
如果 Pod 一直會換,別的服務要怎麼連到它?
我不會讓 frontend 去記某個 backend Pod 的 IP。那樣等於叫客人每天追著不同員工跑。
這一章要記的句子很短:
Pod 會換,IP 會飄;Service 是把一組 Pod 變成穩定入口的網路抽象。
先不要追 Pod IP
Pod 有自己的 IP,這點很重要。
但 Pod IP 不是一個穩定承諾。
一個 Deployment 今天有三個 Pod,明天 rollout 之後可能是另外三個 Pod。名字會變,IP 會變,所在 node 也可能變。
如果另一個服務直接連 Pod IP,它其實是在跟「某一次落地的結果」綁死。
Kubernetes 不希望你這樣想。
它希望你把一組可以替換的 Pod 放在一個穩定入口後面,讓呼叫方只記得:
我要找 web 這個服務
而不是:
我要找 10.244.2.17 這個 Pod
這就是 Service 出現的位置。
用櫃台想 Service
延續上一章的排班例子。
Deployment 像排班表加主管:今天櫃台要有三個人,有人離開就補人,換流程時慢慢替換。
那 Service 是什麼?
我會把它想成「固定櫃台號碼」。
客人不需要知道今天是哪三個員工在後面做事,也不需要知道某個員工換到哪個座位。
客人只需要記得:
我要去 3 號櫃台。
Service 做的就是這件事。
它不等於員工,也不等於排班表。它是一個穩定的入口,後面接的是一組符合條件、當下可以服務的 backend Pods。
這個比喻也順手提醒一件事:
如果櫃台後面沒有人,櫃台號碼還在,但服務不會 magically 成功。
Service 有入口,不代表一定有健康後端。
Service 實際上描述了什麼
最常見的 Service 會長得像這樣:
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web
ports:
- name: http
port: 80
targetPort: 8080
我會這樣讀:
metadata.name: web:這個穩定入口叫webtype: ClusterIP:預設只在 cluster 內部提供一個穩定 IPselector: app=web:哪些 Pod 算是這個 Service 的後端port: 80:Service 對外呈現的 porttargetPort: 8080:真正轉去 Pod 裡 app 監聽的 port
這裡最容易被忽略的是 selector。
Service 不是靠 Deployment 名字找 Pod,也不是靠 Pod 名字找 Pod。
它主要靠 label selector 找 backend。
所以如果 Deployment 的 Pod template label 是:
labels:
app: api
但 Service selector 寫:
selector:
app: web
那這個 Service 很可能就沒有後端。
入口還在,但門後面是空的。
Service、DNS、EndpointSlice 怎麼接起來
正常的 ClusterIP Service 會得到一個 cluster 內部 IP,也會有 DNS 名字。
在同一個 namespace 裡,你通常可以用短名:
web
跨 namespace 時,會寫得更清楚:
web.default.svc.cluster.local
但 DNS 只是讓你找到 Service 入口。
真正的 backend list 不是藏在 DNS 裡,而是由 Kubernetes 根據 Service selector 維護成 EndpointSlice。
可以先用這條線記:
client -> Service DNS / ClusterIP -> EndpointSlice -> ready Pod IP:targetPort
更完整一點:
- 你建立 Service,寫好 selector
- Kubernetes 找到符合 label 的 Pods
- EndpointSlice 記錄這些 backend endpoints
- 每個 node 上的 service proxy / dataplane 依照這份 endpoint 資訊導流
- client 只需要連穩定的 Service 名字或 ClusterIP
這裡有一個重要細節:Service 不是健康檢查本身。
Pod 能不能進入可用 backend,通常跟 Pod readiness 有關。readiness 沒過,你會看到 Pod 還在,但流量不應該被正常送過去。
所以排查 Service 時,不要只問:
Service 有沒有 IP?
還要問:
EndpointSlice 裡有沒有 ready endpoint?
port 跟 targetPort 不要看混
這個坑非常常見。
Service 的 port 是 client 看到的 port。
Pod 裡 app 真的監聽的,是 targetPort。
例如:
ports:
- name: http
port: 80
targetPort: 8080
意思是:
client -> web:80 -> Pod:8080
如果 app 其實聽在 3000,但 Service 寫 targetPort: 8080,Service 入口可能存在,Endpoint 也可能存在,但請求就是打不到正確地方。
我自己的偏好是,production manifest 盡量用 named port 讓語意穩一點:
# Service
ports:
- name: http
port: 80
targetPort: http
# Pod template
ports:
- name: http
containerPort: 8080
這樣以後 Pod 內部 port 從 8080 改成 3000 時,只要維持 name: http 的語意,Service 不一定要跟著改數字。
Service type 先不用背太多,但要知道邊界
新手很容易把 Service 想成「對外暴露服務」。
這只對了一部分。
Service 預設的 ClusterIP 是 cluster 內部入口,不是 public internet 入口。
幾個常見 type 可以先這樣放:
ClusterIP:預設,給 cluster 內部用NodePort:在每個 node 上開一個固定範圍的 port,通常不是我會直接當正式入口的第一選擇LoadBalancer:請 cloud provider 或外部實作給你一個外部 load balancerExternalName:用 DNS CNAME 把 Service 名字指到外部 hostname
還有一個常見例外是 headless Service:
clusterIP: None
它不給你一般 ClusterIP,而是讓 DNS 回到後端 endpoint。這常出現在 StatefulSet 或需要 client 自己知道每個 backend 身份的場景。
這篇先不展開,因為主線要先抓住一般 Service 的直覺:
Service 不是「把東西公開到網路上」的同義詞;它先是一個穩定的服務入口抽象。
新手最容易誤會的幾件事
1. 以為 Service 會建立 Pod
Service 不會建立 Pod。
建立和維持 Pod 數量是 Deployment / ReplicaSet 那一層的事。
Service 只是看 label,找到符合條件的 backend,然後提供入口。
所以如果沒有 Pod,或者 selector 對不到 Pod,Service 不會幫你生出後端。
2. 以為 Service IP 通就代表 app 通
Service 有 ClusterIP,只代表入口物件存在。
要真的通,還要看:
- selector 有沒有選到 Pod
- EndpointSlice 裡有沒有 endpoint
- Pod readiness 有沒有過
targetPort有沒有對到 app 監聽的 port- app 自己有沒有正常回應
Service 是入口,不是保證成功的魔法門。
3. 把 selector 寫得太隨便
Selector 太窄,會選不到 Pod。
Selector 太寬,可能選到不該接流量的 Pod。
這種錯很麻煩,因為 YAML 看起來都合法,但流量行為會很怪。
我會把 Service selector 當成一條很重要的合約:
哪些 Pod 被允許站在這個入口後面。
4. 把 Service、Ingress、LoadBalancer 混在一起
Service 先解的是:cluster 裡面怎麼給一組 Pod 穩定入口。
Ingress / Gateway 更常是在解:外部 HTTP / HTTPS 流量怎麼進 cluster、怎麼做 host/path routing、TLS、流量策略。
LoadBalancer Service 可以拿到外部入口,但它不是 Ingress 的完整替代品。
先把層次分開,後面會輕鬆很多。
我會怎麼 inspect 一個 Service
如果有人說「這個 svc 不通」,我不會只看 kubectl get svc 就停。
我會順著這條線查:
kubectl get svc -n <ns>
kubectl describe svc <svc-name> -n <ns>
kubectl get endpointslice -n <ns> \
-l kubernetes.io/service-name=<svc-name>
kubectl get pods -n <ns> --show-labels
kubectl get pods -n <ns> -l app=web -o wide
kubectl describe pod <pod-name> -n <ns>
我會特別看:
- Service selector 是什麼
- Pod labels 是否真的 match
- EndpointSlice 裡有沒有 addresses
- endpoint condition 是否 ready / serving
- Service
port和 backendtargetPort是否對得上 - Pod 的 readiness probe 是否一直失敗
如果要從 cluster 裡驗證 DNS 和 HTTP,可以起一個暫時 client:
kubectl run tmp-curl -n <ns> --rm -it \
--image=curlimages/curl -- sh
nslookup web
curl -v http://web:80
如果 DNS 找得到,但 EndpointSlice 是空的,問題通常不在 DNS。
如果 EndpointSlice 有 ready endpoint,但 request timeout,才繼續往 targetPort、app listen address、NetworkPolicy、CNI / dataplane 那些方向查。
我對 Service 的記法
我不會把 Service 記成「Kubernetes 的 load balancer」。
這句太粗,容易把 ClusterIP、NodePort、LoadBalancer、Ingress 全部混在一起。
我比較喜歡這樣記:
Service 是一個穩定入口,背後是一份會隨 Pod 變動而更新的 backend 名單。
這樣你排查時就會自然問兩件事:
- 入口本身是否存在?
- 入口後面的 backend 名單是否正確且可用?
只要這兩件事分開,Service 就不難了。
這篇先記住三件事
- Pod IP 不是穩定介面;Service 才是一組 Pod 對外承諾的穩定入口
- Service 靠 selector 找 Pod,EndpointSlice 記錄當下可用的 backend endpoints
- Debug Service 要順著
Service -> selector -> EndpointSlice -> Pod readiness -> targetPort查,不要只盯 ClusterIP
下一篇會接著問:Service 給了 cluster 內部穩定入口,那外部 HTTP / HTTPS 流量要怎麼進來?