Skip to content

研发视角:port、targetPort、NodePort 的关系与理解

本文面向研发团队(后端/前端/测试/运维开发),用大白话 + 图示 + 实例讲清楚 Service 里的三个端口到底什么意思、怎么配、常见坑有哪些。


1. 一句话总结

字段作用位置一句话解释
portService 自己身上Service 对外暴露的端口,集群内其他服务访问它时用
targetPort后端 Pod 的容器上容器真正监听的端口,流量最终要打到这个端口
nodePort集群每个节点上节点对外开放的端口,外部用户通过 <节点IP>:<NodePort> 访问

关系链:

外部用户/客户端


  NodePort        ← 节点上的端口(外部入口)


   Service:port   ← Service 的虚拟 IP + 端口(集群内入口)


  Pod:targetPort  ← 容器实际监听的端口(最终目的地)

2. 用快递物流来类比

想象你要寄一个快递到某公司的“客服部”:

  • port = 公司总机号码(比如 400-1234
    你不需要知道客服部每个人的分机号,只要打总机。

  • targetPort = 客服小张办公桌上的分机号(比如 8080
    总机会把电话转接给真正接听的人。

  • NodePort = 公司园区门口的一个公共收件窗口(比如 30080
    外部快递员不用进园区,把包裹放到门口窗口,园区内部再转送到总机。

Service 就是那个“总机系统”:外部/内部 caller 只需要记住 port,由 Service 根据 Endpoints 把流量转发到 Pod 的 targetPort


3. ClusterIP 场景:只有 port + targetPort

yaml
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  type: ClusterIP
  selector:
    app: user
  ports:
    - name: http
      port: 80        # 其他服务访问 user-service 时用的端口
      targetPort: 8080 # user 容器里 Spring Boot / Gin / Express 实际监听的端口

访问方式

集群内另一个服务调用:

bash
# 访问的是 Service 的 port(80),不是 Pod 的 8080
curl http://user-service:80
# 也可以写成
curl http://user-service.default.svc.cluster.local:80

流量路径

调用方 Pod


user-service:80   (ClusterIP: 10.96.123.10)

   │ kube-proxy 做 DNAT

user Pod IP:8080  (10.244.1.15:8080)

为什么要分开?

解耦 + 标准化

  • 业务代码里统一写 http://user-service:80,不用关心后端容器跑的是 8080、3000 还是 5000。
  • 容器端口改了,只要改 Service 的 targetPort,调用方不用改。
  • 多个服务都用 80 对外暴露,内部互不冲突(因为每个 Service 有独立 ClusterIP)。

4. NodePort 场景:port + targetPort + nodePort

yaml
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  type: NodePort
  selector:
    app: user
  ports:
    - name: http
      port: 80
      targetPort: 8080
      nodePort: 30080   # 不指定则系统从 30000-32767 自动分配

访问方式

bash
# 集群内部访问(仍可用 Service port)
curl http://user-service:80

# 集群外部访问(任一节点的 IP + NodePort)
curl http://<Node-IP>:30080

流量路径

外部客户端


<Node-IP>:30080   ← 节点网卡上的 NodePort


user-service:80   ← 还是 Service 的 port


user Pod:8080     ← 最终打到容器

研发需要注意

  • nodePort全局稀缺资源,范围默认 30000-32767
  • 不写 nodePort 时,Kubernetes 自动分配,每次重建可能变;如果要固定,请显式指定。
  • 生产环境一般不直接用 NodePort 暴露给公网,而是配合 LoadBalancer / Ingress。

5. LoadBalancer 场景:云厂商给你的外部 IP

yaml
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080
      # LoadBalancer 不需要你写 nodePort,但底层仍会创建 NodePort

访问方式

bash
# 通过云厂商分配的外部 IP 访问
curl http://<EXTERNAL-IP>:80

路径

用户


云 LB(如阿里云 SLB / AWS ELB)


<Node-IP>:<NodePort>


Service:80


Pod:8080

研发团队重点:对外接口文档写 http://<EXTERNAL-IP>:80 或域名,不要写 NodePort。


6. 常见配置组合速查

场景typeporttargetPortnodePort给谁用
内部微服务调用ClusterIP808080不写集群内 Pod
临时测试外部访问NodePort80808030080外部通过节点 IP
生产公网访问LoadBalancer80/4438080不写外部通过云 LB IP
七层域名路由ClusterIP/NodePort808080视 Ingress 而定Ingress Controller

7. 研发最容易踩的坑

7.1 只改容器端口,忘了改 targetPort

yaml
# 错误示例:容器监听 8080,但 targetPort 写的 80
ports:
  - port: 80
    targetPort: 80   # ❌ 应该是 8080

现象:Service 能解析,请求无响应或 Connection refused。
排查

bash
kubectl get endpoints <svc-name>
# 有 endpoints,但 telnet PodIP:targetPort 不通

7.2 调用方写死了 Pod IP

python
# 错误示例
requests.get("http://10.244.1.15:8080")

Pod IP 会变,应该通过 Service 域名访问:

python
requests.get("http://user-service:80")

7.3 以为 port 必须等于 targetPort

port 和 targetPort 可以不一样,而且通常建议不一样:

  • Service port: 80 对外友好、标准。
  • Pod targetPort: 8080 容器启动参数决定。

7.4 NodePort 和生产域名混用

直接让前端调用 http://192.168.1.10:30080 这种节点 IP + NodePort,一旦节点挂了、IP 变了、NodePort 变了就出问题。
正确做法:NodePort 只作为 Ingress Controller 或 LoadBalancer 的入口,业务通过域名/外部 IP 访问。

7.5 多端口服务命名不一致

yaml
ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: grpc
    port: 50051
    targetPort: 50051

建议:每个端口都命名,便于 Ingress、监控、日志区分。如果只有单端口,某些场景下 name 可省略,但建议养成命名习惯。


8. 给研发的速记口诀

text
Service 的 port 是门面,
Pod 的 targetPort 是真身,
NodePort 是园区大门,
LoadBalancer 是云端前台。

调用方永远记门面(port),
容器应用守真身(targetPort),
外部流量走大门(NodePort)或前台(LB),
千万别把 Pod IP 当门牌。

9. 实操验证小实验

9.1 部署一个 Nginx

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-demo
  template:
    metadata:
      labels:
        app: nginx-demo
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-demo
spec:
  type: NodePort
  selector:
    app: nginx-demo
  ports:
    - port: 8080        # Service 暴露的端口
      targetPort: 80    # Nginx 容器监听端口
      nodePort: 30080   # 节点上的端口

9.2 验证三种访问方式

bash
# 1. 集群内访问 Service port
kubectl run -it --rm debug --image=busybox --restart=Never -- wget -O- http://nginx-demo:8080

# 2. 查看 NodePort
kubectl get svc nginx-demo
# 输出示例:
# NAME         TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
# nginx-demo   NodePort   10.96.10.20    <none>        8080:30080/TCP   1m

# 3. 集群外访问 NodePort
curl http://<任意节点IP>:30080

9.3 观察 EndpointSlice

bash
kubectl get endpointslices -l kubernetes.io/service-name=nginx-demo -o yaml

可以看到 endpoints 里记录的是 Pod IP + targetPort(80),而不是 Service port(8080)或 NodePort(30080)。


10. 总结

  • port 是 Service 的对外端口,调用方使用。
  • targetPort 是容器的真实端口,流量最终到达这里。
  • nodePort 是节点对外暴露的端口,外部访问的入口。
  • 三者共同作用,实现了稳定入口 → 负载均衡 → 真实后端的流量转发。
  • 研发写代码时,只需关心 port;配置容器和 YAML 时,要正确对应 targetPort;外部对接时,使用 NodePort 或 LoadBalancer,不要写死 Pod IP。

本文档可与《Kubernetes Service 原理由浅入深》配套阅读。