K8s · 網路與服務 · 第 17 課 · · 9min read

ClusterIP Service 入門:Pod 之間怎麼互相找到?

Pod IP 會變、不能寫死。Service 給你一個固定的虛擬 IP(ClusterIP),永遠指向那群 Pod。這篇用 busybox curl nginx-svc 實測叢集內部通訊。

#Kubernetes #Service #ClusterIP #網路
章節目錄 · 13

Pod 跑起來了,外面怎麼連進去?

到 Day 5 Deployment 為止,我們的 Pod 管得好好的:擴縮容、滾動更新、自我修復都會了。但是有個現實問題沒解決:

Pod 跑起來了,但 Pod 有三個常見的痛點

  • Pod IP 會變 — 你刪一個 Pod 重建,IP 從 10.42.0.15 變成 10.42.0.20。前端寫死 IP 連線就斷了

  • 流量該分給誰 — 三個 Pod 副本,使用者該連哪一個?三個人都連同一個就浪費了擴容

  • Pod IP 是叢集內部虛擬 IP — 你瀏覽器打不開
  • K8s 的解法是 Service。Service 給你一個永遠不變的 IP + 自動負載均衡。Pod 怎麼換、怎麼重建都不影響。

    ClusterIP:Service 的預設類型

    Service 有三種類型,這篇先講最常用的 ClusterIP

    • 給 Service 一個叢集內部的虛擬 IP
    • IP 永遠不變(只要 Service 存在)
    • 自動把流量負載均衡到後面的 Pod
    • 自動 DNS 解析(用 Service 名字就能連)
    ⚠️ ClusterIP 只在叢集內可見。外面的瀏覽器連不上 — 那是 NodePort 的工作。

    Service 怎麼找到後面的 Pod?

    答案還是 label + selector

    # service-clusterip.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-svc
    spec:
      type: ClusterIP        # 預設值,可省略
      selector:
        app: nginx           # ← 找所有 app=nginx 的 Pod
      ports:
      - port: 80             # Service 監聽的 port
        targetPort: 80       # 轉發到 Pod 的 port
    💡 黃金法則重申:Deployment selector / Pod template labels / Service selector 三者必須一致。寫錯一個 Service 就找不到 Pod。

    port vs targetPort 怎麼分?

    • port: 80 — Service 自己監聽的 port(別人連 Service 用這個)
    • targetPort: 80 — 轉發到 Pod 容器的哪個 port(最終接收請求的地方)
    兩個通常一樣。但你也可以讓 Service 開 8080、Pod 開 80:
    ports:
    
    • port: 8080 # 別人連 nginx-svc:8080
    targetPort: 80 # 實際送到 Pod 的 80

    對照 docker run -p 8080:80:左邊 8080 = port,右邊 80 = targetPort。

    部署 + 驗證

    kubectl apply -f service-clusterip.yaml
    

    # 看 Service
    kubectl get svc
    # nginx-svc ClusterIP 10.43.0.150 <none> 80/TCP

    # 10.43.0.150 就是 ClusterIP,永遠不變

    Endpoints:Service 背後的 Pod IP 列表

    K8s 在背後維護一個 Endpoints 物件,記錄 Service 對應到哪些 Pod IP:

    kubectl get endpoints nginx-svc
    # ENDPOINTS: 10.42.0.15:80, 10.42.1.8:80, 10.42.2.12:80

    這三個就是 nginx Deployment 的三個 Pod IP。

    Pod 重建/擴容時,Endpoints 自動更新 — 你完全不用手動改。

    從另一個 Pod 連進去測試

    # 開一個臨時 Pod 測試
    kubectl run test-curl --image=curlimages/curl --rm -it --restart=Never -- sh
    

    # 進到 shell 後
    curl http://nginx-svc
    # 看到 nginx 歡迎頁

    # 完整 DNS 名字也行
    curl http://nginx-svc.default.svc.cluster.local

    # 多打幾次,K8s 自動負載均衡到不同 Pod

    DNS 名字格式:用 Service 名字就能連

    K8s 內建 CoreDNS,每個 Service 自動註冊一筆 DNS 紀錄:

    完整:<svc-name>.<namespace>.svc.cluster.local
    同 namespace:<svc-name>
    跨 namespace:<svc-name>.<namespace>

    實際例子:

    • nginx-svc ← 在 default namespace 內,這樣就行
    • nginx-svc.default ← 跨 namespace 連
    • nginx-svc.default.svc.cluster.local ← 完整 FQDN

    自動更新驗證:刪 Pod 看 Endpoints

    kubectl get endpoints nginx-svc
    # 記下三個 IP
    

    kubectl delete pod nginx-deploy-xxx-aaa
    # Deployment 自動補新 Pod(IP 變了)

    # 等幾秒
    kubectl get endpoints nginx-svc
    # 三個 IP 中有一個變了 ← Service 自動偵測 + 更新

    Service 幫你解決的問題:Pod 隨便掛、隨便重建、IP 怎麼變都沒關係,Service 地址永遠不變、流量永遠到健康的 Pod。

    連不上 Service 怎麼辦?常見排錯

    固定排錯流程:

    # 1. 看 endpoints 有沒有 IP
    kubectl get endpoints nginx-svc
    # 空的 → selector 沒對上
    # 有 IP → 走第 2 步
    

    # 2. 確認 selector 跟 Pod label 一致
    kubectl describe svc nginx-svc | grep Selector
    # Selector: app=nginx
    kubectl get pods --show-labels
    # 確認 Pod 的 LABELS 有 app=nginx

    # 3. 確認 targetPort 跟容器實際監聽的 port 一致

    90% 的 Service 連不上是 selector 對不上targetPort 寫錯

    對照 Docker Compose

    功能Docker ComposeK8s ClusterIP
    服務名稱 DNShttp://api:8080http://nginx-svc:80
    跨 Node做不到
    自動負載均衡簡單輪詢kube-proxy 真的負載均衡
    健康檢查踢 Pod做不到
    概念一樣,但 K8s 多了跨節點 + 健康檢查 + 真負載均衡。

    重點整理

    • ClusterIP = 叢集內部的穩定 IP,永遠不變
    • selector + label 找後面的 Pod
    • port 是 Service 監聽的、targetPort 是 Pod 容器監聽的
    • 在同 namespace 直接連
    • Endpoints 自動更新(Pod 變動跟著動)
    • 連不上 → 先看 Endpoints 是否為空 → selector → targetPort

    下一步

    ClusterIP 解決了叢集內部的連線。但外面的使用者怎麼連?這就要用 NodePort、LoadBalancer 和三種 Service 的差異