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= 容器
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 對外的LoadBalancer:雲端的玩法
NodePort 的問題:使用者要記 IP 和 port。192.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永遠是,沒人接
三種 Service 比較表
| 類型 | 對外可見? | 怎麼用 | 適用場景 |
|---|---|---|---|
| ClusterIP | ❌ 只叢集內 | mysql-svc:3306 | 內部 service-to-service |
| NodePort | ✅ 叢集內 + Node IP | | 開發測試、地端 demo |
| LoadBalancer | ✅ 公網 IP | | 雲端 production |
LoadBalancer
└─ 包含 NodePort
└─ 包含 ClusterIP
LoadBalancer 建好時,K8s 會自動幫你開一個 NodePort,再讓雲商的 LB 把流量導到 NodePort。NodePort 也內建一個 ClusterIP 供叢集內使用。所以建一個 LoadBalancer Service,三種能力都有。
怎麼選?
| 情境 | 用什麼 |
|---|---|
| 後端 API 給其他 Pod 用 | ClusterIP(預設) |
| 本機 k3d/minikube 開發 | NodePort |
| 公司內網 demo | NodePort |
| 雲端 production(單一服務) | LoadBalancer |
| 雲端 production(多個服務) | Ingress(下一篇) |
每開一個 LoadBalancer 在 GCP / AWS 都要花錢(一個月幾十美金)。公司有 30 個微服務 = 30 個 LoadBalancer = 一個月幾百美金。
Ingress 讓你只開一個 LoadBalancer,依路徑(/api / /admin)導到不同 Service,省錢又彈性。
對照 Docker
| Docker | Kubernetes |
|---|---|
docker run -p 8080:80 | NodePort:30080:80 |
docker run 沒開 port | ClusterIP:只能 container 之間連 |
| 雲端負載平衡器手動接 | LoadBalancer:K8s 自動建 |
-p,但有個關鍵差:Docker 的 port 只在那台機器上開;NodePort 每個 Node 都開。
排錯:NodePort 連不到
現象 1:curl 沒回應
# 確認 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 又怎麼連?