主题
Kubernetes Service 全链路深入解析:port / targetPort / NodePort 与 iptables / ipvs 实现
本文从内核数据包流转视角,完整拆解 Service 的
port、targetPort、nodePort在全链路中各自扮演的角色,并分别用 iptables 模式 和 ipvs 模式 还原实现细节。
目录
- 先建立全局视图
- 核心对象的数据结构
- ClusterIP 数据包全链路
- NodePort 数据包全链路
- iptables 模式实现细节
- ipvs 模式实现细节
- externalTrafficPolicy 对链路的影响
- 连接跟踪与返回包
- 关键命令与排障
- 总结对比
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:80 或 web.default:80 | port(Service 端口) |
| 节点外访问 | <Node-1-IP>:30080 | nodePort(节点端口) |
| 云 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: TCPService 本身只声明意图,不转发数据包。
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-2EndpointSlice 把 Service 的抽象映射到具体的 Pod IP + targetPort。
2.3 kube-proxy 的职责
kube-proxy 运行在每个节点上,它:
- Watch Service / EndpointSlice 变化。
- 在本地节点上配置转发规则(iptables 或 ipvs)。
- 数据包到达节点时,由内核根据规则直接转发,不经过 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(本机进程)
│
▼
OUTPUTkube-proxy 主要操作 nat 表 的 PREROUTING 和 OUTPUT 链。
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:300805.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:80806. 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 有三种转发模式:
| 模式 | 缩写 | 说明 |
|---|---|---|
| NAT | Masq | 修改源/目的 IP,Pod 看到的源 IP 是节点 IP |
| DR | Route | 直接路由,Pod 直接回复客户端,性能最好 |
| Tunnel | Tunnel | IPIP 隧道 |
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=web9.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 web9.3 查看 ipvs 规则
bash
# 查看虚拟服务和真实服务器
ipvsadm -Ln
# 查看连接状态
ipvsadm -Lnc
# 查看调度算法
ipvsadm -Ln --stats9.4 查看 conntrack
bash
# 查看连接跟踪表(需要 root)
conntrack -L | grep 10.96.123.45
# 查看某个后端的连接
conntrack -L -d 10.244.1.109.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 808010. 总结对比
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 -L | ipvsadm |
10.3 给研发和运维的关键认知
- ClusterIP 不是真实网卡 IP,它只是 iptables/ipvs 规则中的一个匹配目标。
- Service port 只存在于规则中,Pod 上没有任何进程监听 ClusterIP:port。
- targetPort 是容器真正监听的端口,EndpointSlice 里记录的就是它。
- nodePort 是节点物理端口,全局唯一,多个 Service 不能共用同一个 NodePort。
- kube-proxy 只负责配置规则,真正转发的是内核(iptables/ipvs)。
- 返回包依赖 conntrack,所以长连接在端点变化时可能异常。
- externalTrafficPolicy=Local 保留源 IP,但可能导致本节点无 Pod 时丢包。
建议配合《Kubernetes Service 原理由浅入深》和《port / targetPort / NodePort 关系解读》一起阅读。