Skip to content

Kubernetes Service 全链路深入解析:port / targetPort / NodePort 与 iptables / ipvs 实现

本文从内核数据包流转视角,完整拆解 Service 的 porttargetPortnodePort 在全链路中各自扮演的角色,并分别用 iptables 模式ipvs 模式 还原实现细节。


目录

  1. 先建立全局视图
  2. 核心对象的数据结构
  3. ClusterIP 数据包全链路
  4. NodePort 数据包全链路
  5. iptables 模式实现细节
  6. ipvs 模式实现细节
  7. externalTrafficPolicy 对链路的影响
  8. 连接跟踪与返回包
  9. 关键命令与排障
  10. 总结对比

1. 先建立全局视图

假设有如下 Service:

yaml
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: default
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - name: http
      port: 80
      targetPort: 8080
      nodePort: 30080

后端有两个 Pod:

Pod A: 10.244.1.10:8080  (在 node-1 上)
Pod B: 10.244.2.10:8080  (在 node-2 上)

Service 被分配:

ClusterIP: 10.96.123.45
NodePort:  30080

三种访问方式对应的入口:

访问方式目标地址命中哪个端口概念
集群内访问10.96.123.45:80web.default:80port(Service 端口)
节点外访问<Node-1-IP>:30080nodePort(节点端口)
云 LB 访问<EXTERNAL-IP>:80外部 LB 转发到 NodePort / Service port

最终所有流量都要变成:

-> Pod-A:8080 或 Pod-B:8080

这里 8080 就是 targetPort


2. 核心对象的数据结构

2.1 Service 对象

yaml
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: NodePort
  clusterIP: 10.96.123.45
  selector:
    app: web
  ports:
    - name: http
      port: 80
      targetPort: 8080
      nodePort: 30080
      protocol: TCP

Service 本身只声明意图,不转发数据包。

2.2 EndpointSlice 对象

yaml
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: web-abc12
  labels:
    kubernetes.io/service-name: web
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 8080          # 这就是 targetPort
endpoints:
  - addresses: [10.244.1.10]
    conditions: { ready: true }
    nodeName: node-1
  - addresses: [10.244.2.10]
    conditions: { ready: true }
    nodeName: node-2

EndpointSlice 把 Service 的抽象映射到具体的 Pod IP + targetPort。

2.3 kube-proxy 的职责

kube-proxy 运行在每个节点上,它:

  1. Watch Service / EndpointSlice 变化。
  2. 在本地节点上配置转发规则(iptables 或 ipvs)。
  3. 数据包到达节点时,由内核根据规则直接转发,不经过 kube-proxy 进程

3. ClusterIP 数据包全链路

3.1 链路图

┌─────────────┐
│  Client Pod │
│ 10.244.1.5  │
└──────┬──────┘

       │ 目的地址:10.96.123.45:80


┌─────────────────────────┐
│ 本节点 kube-proxy 规则   │
│  (iptables / ipvs)      │
└──────┬──────────────────┘

       │ DNAT / 负载均衡
       │ 10.96.123.45:80 -> 10.244.1.10:8080 或 10.244.2.10:8080

┌─────────────────────────┐
│    CNI 网络(vxlan/    │
│    host-gw/calico 等)   │
└──────┬──────────────────┘


┌─────────────┐
│  Backend    │
│  Pod        │
│ 10.244.x.x:8080
└─────────────┘

3.2 端口转换关系

Service: 10.96.123.45:80        port

                ▼  DNAT
Pod:     10.244.1.10:8080       targetPort

核心:ClusterIP 不是一个真实网卡上的 IP,它只是 iptables/ipvs 规则中的一个匹配条件。


4. NodePort 数据包全链路

4.1 外部客户端访问 NodePort

外部客户端

    │ 目的地址:<Node-1-IP>:30080

┌─────────┐
│ node-1  │  网卡 eth0:30080
└────┬────┘

     │ kube-proxy 规则匹配 NodePort


┌─────────────────────────────┐
│  DNAT 到 ClusterIP:port     │
│  或 DNAT 直接到 Pod:targetPort │
└──────┬──────────────────────┘


┌─────────────────────────────┐
│  CNI 转发到目标 Pod          │
└─────────────────────────────┘

4.2 端口转换关系

外部:   <Node-1-IP>:30080      nodePort


Service: 10.96.123.45:80        port

              ▼  DNAT
Pod:     10.244.1.10:8080       targetPort

注意:NodePort 只是节点上的一个入口,最终还是要映射到 Service 的 port,再映射到 Pod 的 targetPort


5. iptables 模式实现细节

5.1 核心 Netfilter 钩子

Linux 内核处理数据包时,会经过 Netfilter 的几个钩子点:

PREROUTING -> [路由决策] -> FORWARD -> POSTROUTING


            INPUT(本机进程)


           OUTPUT

kube-proxy 主要操作 nat 表PREROUTINGOUTPUT 链。

5.2 iptables 规则链结构

kube-proxy 会创建如下链:

KUBE-SERVICES      # 匹配所有 Service(ClusterIP + port)

  ├── KUBE-SVC-XXXXXXXXXXXXXXXX   # 某个 Service 的链(按 ClusterIP:port 匹配)
  │     │
  │     ├── KUBE-SEP-XXXXXXXXXXXXXXXX   # 后端 Pod 1
  │     │     └── DNAT to Pod-1:targetPort
  │     │
  │     └── KUBE-SEP-YYYYYYYYYYYYYYYY   # 后端 Pod 2
  │           └── DNAT to Pod-2:targetPort

  └── KUBE-NODEPORTS    # 匹配 NodePort

        └── KUBE-SVC-XXXXXXXXXXXXXXXX   # 跳转到对应 Service 链

5.3 实际规则示例(模拟)

查看 nat 表:

bash
iptables -t nat -L -n -v

关键规则大致如下:

Chain PREROUTING (policy ACCEPT)
 target     prot opt in     out    source   destination
KUBE-SERVICES  all  --  *      *      0.0.0.0/0   0.0.0.0/0    /* kubernetes service portals */

Chain OUTPUT (policy ACCEPT)
 target     prot opt in     out    source   destination
KUBE-SERVICES  all  --  *      *      0.0.0.0/0   0.0.0.0/0    /* kubernetes service portals */

Chain KUBE-SERVICES (2 references)
 target     prot opt source    destination
KUBE-SVC-ABC123  tcp  --  0.0.0.0/0  10.96.123.45    /* default/web:http cluster IP */ tcp dpt:80
KUBE-NODEPORTS   all  --  0.0.0.0/0  0.0.0.0/0    /* kubernetes service nodeports */

Chain KUBE-SVC-ABC123 (1 references)
 target     prot opt source    destination
KUBE-SEP-POD111  all  --  0.0.0.0/0  0.0.0.0/0    statistic mode random probability 0.5000000000
KUBE-SEP-POD222  all  --  0.0.0.0/0  0.0.0.0/0

Chain KUBE-SEP-POD111 (1 references)
 target     prot opt source    destination
DNAT       tcp  --  0.0.0.0/0  0.0.0.0/0    tcp to:10.244.1.10:8080

Chain KUBE-SEP-POD222 (1 references)
 target     prot opt source    destination
DNAT       tcp  --  0.0.0.0/0  0.0.0.0/0    tcp to:10.244.2.10:8080

Chain KUBE-NODEPORTS (1 references)
 target     prot opt source    destination
KUBE-SVC-ABC123  tcp  --  0.0.0.0/0  0.0.0.0/0    /* default/web:http */ tcp dpt:30080

5.4 负载均衡算法:随机

iptables 模式下,kube-proxy 通过 statistic mode random probability 实现概率随机

  • 2 个后端:第一个规则概率 0.5,第二个规则 1.0(兜底)。
  • 3 个后端:概率依次是 0.333、0.5、1.0。

这意味着 iptables 模式是随机负载均衡,不是轮询。

5.5 数据包处理流程(ClusterIP)

1. 客户端发出 SYN 到 10.96.123.45:80
2. 进入本机 PREROUTING / OUTPUT 链
3. 命中 KUBE-SERVICES
4. 命中 KUBE-SVC-ABC123(匹配 ClusterIP:80)
5. 按概率选择 KUBE-SEP-POD111 或 POD222
6. 执行 DNAT:目的 IP:Port 改为 10.244.x.x:8080
7. 路由决策,通过 CNI 转发到目标 Pod
8. Pod 回复 SYN+ACK,源地址是 10.244.x.x:8080
9. 返回包经过本机时,conntrack 做反向 DNAT,将源地址改回 10.96.123.45:80
10. 客户端收到回复,以为是 ClusterIP 返回的

5.6 NodePort 的处理

外部流量进入节点网卡的 30080:

1. 数据包目的地址:<Node-1-IP>:30080
2. PREROUTING -> KUBE-SERVICES -> KUBE-NODEPORTS
3. 命中 KUBE-NODEPORTS 中 tcp dpt:30080 的规则
4. 跳转到 KUBE-SVC-ABC123
5. 后续流程与 ClusterIP 相同:DNAT 到 Pod:8080

6. ipvs 模式实现细节

6.1 什么是 IPVS

IPVS(IP Virtual Server)是 Linux 内核中的四层负载均衡模块,工作在内核态,基于 Netfilter 钩子实现。

相比 iptables:

  • 规则数量与后端数量无关,只创建 virtual server + real server。
  • 支持多种调度算法:rr、lc、dh、sh、sed、nq。
  • 转发性能更高,更新更快。

6.2 IPVS 数据结构

Virtual Server (VS)     = Service 的 ClusterIP:port
  ├── Real Server 1     = Pod-1:targetPort
  ├── Real Server 2     = Pod-2:targetPort
  └── ...

6.3 实际规则示例

bash
ipvsadm -Ln

输出类似:

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.96.123.45:80 rr
  -> 10.244.1.10:8080             Masq    1      0          0
  -> 10.244.2.10:8080             Masq    1      0          0
TCP  192.168.1.10:30080 rr
  -> 10.244.1.10:8080             Masq    1      0          0
  -> 10.244.2.10:8080             Masq    1      0          0

解释:

  • 10.96.123.45:80 是 ClusterIP + Service port。
  • 192.168.1.10:30080 是节点 IP + NodePort。
  • 10.244.x.x:8080 是后端 Pod IP + targetPort。
  • rr 是轮询调度算法。
  • Masq 表示 MASQUERADE(SNAT)模式。

6.4 ipvs 的转发模式

IPVS 有三种转发模式:

模式缩写说明
NATMasq修改源/目的 IP,Pod 看到的源 IP 是节点 IP
DRRoute直接路由,Pod 直接回复客户端,性能最好
TunnelTunnelIPIP 隧道

Kubernetes 中 ipvs 默认使用 NAT/Masq 模式。

6.5 ipvs 数据包流程

1. 客户端访问 10.96.123.45:80
2. 数据包进入 ipvs 的 PREROUTING / LOCAL_IN 钩子
3. ipvs 在 virtual server 列表中匹配到 10.96.123.45:80
4. 根据调度算法(rr)选择一个 real server
5. 执行 DNAT + SNAT(Masq),将包发给 10.244.x.x:8080
6. Pod 回复,ipvs 做反向转换
7. 客户端收到回复

6.6 ipvs 与 iptables 的协作

ipvs 模式下,iptables 仍然存在,但只做辅助:

  • iptables 负责 NodePort 的初步匹配(将 NodePort 流量导入 ipvs)。
  • iptables 负责 SNAT / MASQUERADE 等 Netfilter 功能。
  • ipvs 负责真正的负载均衡和 DNAT。

7. externalTrafficPolicy 对链路的影响

7.1 externalTrafficPolicy=Cluster(默认)

外部客户端


<Node-IP>:30080


kube-proxy 负载均衡到任意 Pod
(可能在本节点,也可能跨节点转发)
  • 流量可能跨节点。
  • 会做一次 SNAT,Pod 看到的源 IP 是节点 IP,丢失真实客户端 IP
  • 负载均衡效果好。

7.2 externalTrafficPolicy=Local

外部客户端


<Node-IP>:30080


只转发到本节点上的 Pod
  • 流量不跨节点。
  • 保留真实客户端源 IP
  • 如果本节点没有 Pod,连接会被丢弃。
  • 负载均衡可能不均衡(依赖外部 LB 的调度)。

7.3 iptables 中的体现

externalTrafficPolicy=Local 时,kube-proxy 会为每个节点生成不同的规则:

  • 只把本节点上存在的 Pod 加入 NodePort 的 real server 列表。
  • iptables 中对应规则会检查源/目标,跳过非本节点后端。

7.4 ipvs 中的体现

ipvs 模式下同样如此:

  • Cluster:所有节点上 NodePort VS 都有全部后端。
  • Local:每个节点上 NodePort VS 只包含本节点后端。

8. 连接跟踪与返回包

8.1 conntrack 的作用

无论是 iptables 还是 ipvs,都需要 conntrack(连接跟踪) 来处理返回包:

请求包:
  源:Client-IP:random-port
  目的:10.96.123.45:80

经过 DNAT 后:
  源:Client-IP:random-port
  目的:10.244.1.10:8080

Pod 回复:
  源:10.244.1.10:8080
  目的:Client-IP:random-port

返回包经过节点时,conntrack 做反向 DNAT:
  源:10.96.123.45:80
  目的:Client-IP:random-port

这样客户端一直认为自己在和 ClusterIP 通信。

8.2 长连接与端点变化

如果一个 Pod 在连接建立后被删除:

  • iptables/ipvs 会更新规则,移除这个后端。
  • 已经建立的连接仍然存在于 conntrack 表中,会继续发给旧 Pod。
  • 新连接才会被负载均衡到剩余后端。

这就是滚动更新时偶发 502/连接重置的原因之一。


9. 关键命令与排障

9.1 查看 Service 与后端

bash
kubectl get svc web -o wide
kubectl get endpoints web
kubectl get endpointslices -l kubernetes.io/service-name=web

9.2 查看 iptables 规则

bash
# 查看所有 Service 相关链
iptables -t nat -L KUBE-SERVICES -n -v

# 查看某个 Service 的链(需要知道链名,从上面输出找)
iptables -t nat -L KUBE-SVC-XXX -n -v

# 查看 NodePort 链
iptables -t nat -L KUBE-NODEPORTS -n -v

# 统计 DNAT 命中次数
iptables -t nat -L -n -v | grep web

9.3 查看 ipvs 规则

bash
# 查看虚拟服务和真实服务器
ipvsadm -Ln

# 查看连接状态
ipvsadm -Lnc

# 查看调度算法
ipvsadm -Ln --stats

9.4 查看 conntrack

bash
# 查看连接跟踪表(需要 root)
conntrack -L | grep 10.96.123.45

# 查看某个后端的连接
conntrack -L -d 10.244.1.10

9.5 追踪数据包

bash
# 在节点上抓包
tcpdump -i any -nn host 10.96.123.45

# 抓 NodePort
_tcpdump -i any -nn port 30080

# 抓后端 Pod
tcpdump -i any -nn host 10.244.1.10 and port 8080

10. 总结对比

10.1 port / targetPort / nodePort 在全链路中的位置

外部客户端

    │  nodePort: 30080(节点物理端口,外部入口)

┌─────────────┐
│   Node      │
└──────┬──────┘

       │  port: 80(Service ClusterIP 上的端口)

┌─────────────┐
│   Service   │ 10.96.123.45:80
└──────┬──────┘

       │  targetPort: 8080(Pod 容器真实端口)

┌─────────────┐
│     Pod     │ 10.244.1.10:8080
└─────────────┘

10.2 iptables vs ipvs 对比

特性iptables 模式ipvs 模式
负载均衡层iptables 规则链IPVS virtual server
调度算法随机(probability)rr / lc / dh / sh / sed / nq
后端数量大时规则链膨胀,更新慢结构稳定,更新快
性能较好更好
长连接重试较差较好
内核要求需要 ip_vs 模块
调试工具iptables -Lipvsadm

10.3 给研发和运维的关键认知

  1. ClusterIP 不是真实网卡 IP,它只是 iptables/ipvs 规则中的一个匹配目标。
  2. Service port 只存在于规则中,Pod 上没有任何进程监听 ClusterIP:port。
  3. targetPort 是容器真正监听的端口,EndpointSlice 里记录的就是它。
  4. nodePort 是节点物理端口,全局唯一,多个 Service 不能共用同一个 NodePort。
  5. kube-proxy 只负责配置规则,真正转发的是内核(iptables/ipvs)。
  6. 返回包依赖 conntrack,所以长连接在端点变化时可能异常。
  7. externalTrafficPolicy=Local 保留源 IP,但可能导致本节点无 Pod 时丢包。

建议配合《Kubernetes Service 原理由浅入深》和《port / targetPort / NodePort 关系解读》一起阅读。