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

NodePort 是什麼?K8s 三種 Service(ClusterIP / NodePort / LoadBalancer)怎麼選?

ClusterIP 只在叢集內看得到,外面連不進來。NodePort 把每個 Node 的 port 開出來,外網就能連。這篇講清楚三種 Service 的差別與適用場景。

#Kubernetes #NodePort #LoadBalancer #Service
章節目錄 · 11

ClusterIP 的天花板

上一篇 ClusterIP 把多個 Pod 變成一個固定的虛擬 IP,叢集內所有 Pod 都能用 mysql-svc 連到 MySQL。

但問題來了:你的瀏覽器在叢集外面

ClusterIP 只在叢集內部可見。你打 curl 10.96.0.5 從筆電完全沒反應 — 因為那是 K8s 內部的虛擬網段。

外面要連進來,需要把 Service 從叢集內「往外開一道門」。這就是 NodePort

NodePort 是什麼?

NodePort 在每個 Node 上都開一個指定的 port(範圍 30000-32767),任何打到 Node IP:NodePort 的流量會被導到對應的 Pod。

外部使用者
    ↓ curl <Node IP>:30080
┌─────────────────────────┐
│ Node 1 :30080 ─┐         │
│ Node 2 :30080 ─┼─→ Pod   │   ← 任何 Node 都行
│ Node 3 :30080 ─┘         │
└─────────────────────────┘

重點:不是只有 Pod 所在的 Node 開 port,是每個 Node 都開。你打哪個 Node IP 都會通。

三個 Port 在做什麼?

NodePort YAML 看起來會有三個 port,新手最容易混。

apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - nodePort: 30080      # ① Node 上對外的 port
      port: 80             # ② Service 在叢集內的 port
      targetPort: 80       # ③ Pod 容器裡的 port

流量路線(從外到內):

外面 curl <Node IP>:30080  ← ① nodePort
        ↓
Service ClusterIP:80       ← ② port(叢集內依然有 ClusterIP)
        ↓
Pod 容器:80                ← ③ targetPort

口訣

  • nodePort = 對外
  • port = 對內(給叢集內其他 Pod 用,跟 ClusterIP 一樣)
  • targetPort = 容器
NodePort 沒有取代 ClusterIP — 它是「在 ClusterIP 上面再加一層對外的入口」。叢集內依然可以 curl nginx-nodeport:80,外面也可以 curl :30080

從外面驗證

部署完後:

$ kubectl get svc nginx-nodeport
NAME             TYPE       CLUSTER-IP      PORT(S)
nginx-nodeport   NodePort   10.96.50.10     80:30080/TCP

$ kubectl get nodes -o wide
NAME INTERNAL-IP
k3d-cluster1 192.168.97.2

任何一台 Node 連 30080:

$ curl 192.168.97.2:30080
<h1>Welcome to nginx!</h1>

重點觀察

  • PORT(S) 顯示 80:30080 — 80 是叢集內的 port,30080 是 Node 對外的

  • 多 Node 叢集,每個 Node 都會聽 30080,連哪台都行

  • 流量到 Node 後,K8s 會幫你轉發到對應 Pod(即使 Pod 不在這台 Node 上)
  • LoadBalancer:雲端的玩法

    NodePort 的問題:使用者要記 IP 和 port192.168.97.2:30080 不能上 production。

    正式環境的做法是 LoadBalancer

    spec:
      type: LoadBalancer
      selector:
        app: nginx
      ports:
        - port: 80
          targetPort: 80
    $ kubectl get svc
    NAME    TYPE           EXTERNAL-IP       PORT(S)
    nginx   LoadBalancer   34.123.45.67      80:31234/TCP

    EXTERNAL-IP 是雲商給你的公網 IP。直接 curl 34.123.45.67 就能連。

    關鍵:LoadBalancer 只在雲環境(GKE / EKS / AKS)有效。你要 K8s 跟雲商對接:

    • GKE → 自動建立 Google Cloud Load Balancer
    • EKS → 自動建立 AWS ELB
    • 地端 / 自家機房 → EXTERNAL-IP 永遠是 ,沒人接
    地端要用 LoadBalancer 得自己裝 MetalLB 模擬。

    三種 Service 比較表

    類型對外可見?怎麼用適用場景
    ClusterIP❌ 只叢集內mysql-svc:3306內部 service-to-service
    NodePort✅ 叢集內 + Node IP:30080開發測試、地端 demo
    LoadBalancer✅ 公網 IP:80雲端 production
    遞進關係(包含關係):
    LoadBalancer
      └─ 包含 NodePort
           └─ 包含 ClusterIP

    LoadBalancer 建好時,K8s 會自動幫你開一個 NodePort,再讓雲商的 LB 把流量導到 NodePort。NodePort 也內建一個 ClusterIP 供叢集內使用。所以建一個 LoadBalancer Service,三種能力都有。

    怎麼選?

    情境用什麼
    後端 API 給其他 Pod 用ClusterIP(預設)
    本機 k3d/minikube 開發NodePort
    公司內網 demoNodePort
    雲端 production(單一服務)LoadBalancer
    雲端 production(多個服務)Ingress(下一篇)
    為什麼 production 不直接用 LoadBalancer?

    每開一個 LoadBalancer 在 GCP / AWS 都要花錢(一個月幾十美金)。公司有 30 個微服務 = 30 個 LoadBalancer = 一個月幾百美金。

    Ingress 讓你只開一個 LoadBalancer,依路徑(/api / /admin)導到不同 Service,省錢又彈性。

    對照 Docker

    DockerKubernetes
    docker run -p 8080:80NodePort:30080:80
    docker run 沒開 portClusterIP:只能 container 之間連
    雲端負載平衡器手動接LoadBalancer:K8s 自動建
    NodePort 最像 Docker 的 -p,但有個關鍵差:Docker 的 port 只在那台機器上開;NodePort 每個 Node 都開

    排錯:NodePort 連不到

    現象 1curl :30080 沒回應

    # 確認 Service type
    $ kubectl get svc
    TYPE        # ClusterIP? → 你忘了改 NodePort
    

    # 確認 nodePort 真的開了
    $ kubectl describe svc nginx-nodeport | grep NodePort
    NodePort: <unset> 30080/TCP

    # 確認 Pod 跑起來
    $ kubectl get endpoints nginx-nodeport
    ENDPOINTS # 有 IP 才代表 Pod 接得上

    現象 2:Endpoints 是空的

    通常是 selector 跟 Pod 的 labels 對不上。回去 Labels 那篇 檢查。

    現象 3:本機 curl 有回應,外面沒有

    防火牆。雲端 VM 的 security group / 公司網路防火牆要開 30000-32767 範圍。

    重點整理

    • NodePort 在每個 Node 上開一個 port(30000-32767),讓外面連得到
    • 三個 port:nodePort(對外)/ port(叢集內)/ targetPort(容器)
    • LoadBalancer = 雲商公網 IP,地端要 MetalLB
    • 包含關係:LoadBalancer ⊃ NodePort ⊃ ClusterIP
    • Production 多服務用 Ingress,只開一個 LoadBalancer 省錢

    下一步

    NodePort 解決了「外面進來」的問題。但如果你有 10 個 Pod 跑在叢集裡,每個 Pod 之間怎麼互相找到對方

    K8s 內部是怎麼把 mysql-svc 解析成 IP 的?跨 namespace 又怎麼連?

    下一篇:K8s DNS 與 Namespace:用名字找服務