主题
Kubernetes Service 到 Endpoint 的流量分发机制详解
目标读者:Kubernetes 运维、SRE、后端研发、网络工程师
前置知识:Kubernetes Service、EndpointSlice、kube-proxy
文档版本:v1.0
一句话结论
Service 本身不直接分发流量。kube-proxy 根据 EndpointSlice 中记录的后端 Pod IP:targetPort,在节点上生成负载均衡规则(iptables 随机或 ipvs 调度算法),将访问 Service ClusterIP/NodePort 的流量分发到多个后端 Pod。
目录
- 核心对象关系回顾
- 流量分发的真实执行者:kube-proxy
- iptables 模式下的分发机制
- ipvs 模式下的分发机制
- 分发前的后端筛选
- 会话保持(Session Affinity)
- 外部流量策略的影响
- 拓扑感知路由(Topology Aware Routing)
- EndpointSlice 拆分与部分后端不可用
- 实际案例分析
- 排障与验证命令
- 总结
第1章 核心对象关系回顾
1.1 Service、EndpointSlice、Pod 的关系
┌─────────────────────────────────────┐
│ Service │
│ name: web-service │
│ ClusterIP: 10.96.123.45 │
│ port: 80 │
│ selector: app=web │
└──────────────┬──────────────────────┘
│ selector
▼
┌─────────────────────────────────────┐
│ EndpointSlice │
│ addressType: IPv4 │
│ ports: │
│ - port: 8080 (targetPort) │
│ endpoints: │
│ - 10.244.1.10:8080 (Pod A) │
│ - 10.244.1.11:8080 (Pod B) │
│ - 10.244.2.10:8080 (Pod C) │
└─────────────────────────────────────┘
│
▼
┌──────┴──────┐
▼ ▼
Pod A Pod B1.2 EndpointSlice 中的关键字段
yaml
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: web-service-abc12
labels:
kubernetes.io/service-name: web-service
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 8080 # targetPort
endpoints:
- addresses:
- 10.244.1.10
conditions:
ready: true # 只有 ready=true 才会接收流量
nodeName: node-1
zone: cn-north-1a
- addresses:
- 10.244.1.11
conditions:
ready: true
nodeName: node-1
zone: cn-north-1a
- addresses:
- 10.244.2.10
conditions:
ready: true
nodeName: node-2
zone: cn-north-1b关键字段:
addresses:Pod IPport:容器实际监听的 targetPortconditions.ready:只有true才会被加入转发后端nodeName/zone:用于拓扑感知路由
第2章 流量分发的真实执行者:kube-proxy
2.1 kube-proxy 的工作流程
kube-proxy Watch API Server
│
├─ Service 变化 → 获取 ClusterIP 和 port
├─ EndpointSlice 变化 → 获取后端 Pod IP:targetPort
│
▼
在本地节点生成规则
│
├─ iptables 模式:KUBE-SVC / KUBE-SEP 链
└─ ipvs 模式:virtual server + real server
│
▼
数据包到达节点时,内核按规则直接转发2.2 分发发生的时机
当 Pod 内应用发起请求时:
应用层: curl http://web-service:80
│
▼
DNS 解析: web-service → 10.96.123.45
│
▼
内核构造数据包: 目的 10.96.123.45:80
│
▼
命中 kube-proxy 规则 → 负载均衡选择后端 Pod
│
▼
DNAT 到 Pod IP:targetPort第3章 iptables 模式下的分发机制
3.1 核心规则链
PREROUTING / OUTPUT
│
▼
KUBE-SERVICES
│
▼
KUBE-SVC-XXXXXXXXXXXXXXXX (匹配 Service ClusterIP:port)
│
├─ KUBE-SEP-AAAAAAAAAAAA (Pod A:targetPort)
├─ KUBE-SEP-BBBBBBBBBBBB (Pod B:targetPort)
└─ KUBE-SEP-CCCCCCCCCCCC (Pod C:targetPort)3.2 随机负载均衡算法
iptables 模式下,kube-proxy 通过 statistic mode random probability 实现概率随机:
bash
iptables -t nat -L KUBE-SVC-XXX -n -v3 个后端时的规则示例:
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.3333333333
KUBE-SEP-POD222 all -- 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.5000000000
KUBE-SEP-POD333 all -- 0.0.0.0/0 0.0.0.0/0算法解释:
| 数据包 | 匹配规则 | 概率 |
|---|---|---|
| 第 1 个概率 | 命中 POD111 | 1/3 ≈ 0.333 |
| 未命中第 1 个,第 2 个概率 | 命中 POD222 | 在剩余 2/3 中占 1/2 = 1/3 |
| 最后兜底 | 命中 POD333 | 剩余 1/3 |
最终每个 Pod 获得 1/3 的流量。
3.3 为什么是随机而不是轮询
iptables 的 statistic 模块只支持随机概率,不支持轮询。这是因为:
- iptables 是无状态的
- 无法记住上一次选择了哪个后端
- 每个包独立做概率判断
优缺点:
| 优点 | 缺点 |
|---|---|
| 实现简单 | 不是严格的轮询 |
| 无状态,性能好 | 少量连接时可能不均衡 |
| 规则更新快 | 无法根据后端负载动态调整 |
3.4 新增/删除后端时规则如何变化
假设从 2 个后端扩容到 3 个:
2 个后端时:
KUBE-SEP-111 probability 0.5000000000
KUBE-SEP-222 probability 1.00000000003 个后端时:
KUBE-SEP-111 probability 0.3333333333
KUBE-SEP-222 probability 0.5000000000
KUBE-SEP-333 probability 1.0000000000kube-proxy 会重新计算所有概率值,确保总和为 1。
第4章 ipvs 模式下的分发机制
4.1 核心数据结构
Virtual Server (VS) = Service ClusterIP:port
├── Real Server 1 = Pod-1:targetPort (weight=1)
├── Real Server 2 = Pod-2:targetPort (weight=1)
└── Real Server 3 = Pod-3:targetPort (weight=1)4.2 支持的调度算法
| 算法 | 缩写 | 说明 |
|---|---|---|
| 轮询 | rr | 依次分配,默认 |
| 最少连接 | lc | 当前连接数最少优先 |
| 加权轮询 | wrr | 按权重轮询 |
| 加权最少连接 | wlc | 按权重和连接数 |
| 源地址哈希 | sh | 同一源 IP 固定到同一后端 |
| 目的地址哈希 | dh | 同一目的 IP 固定到同一后端 |
| 最短预期延迟 | sed | 计算响应时间 |
| 永不排队 | nq | 无队列等待 |
4.3 默认算法:rr(轮询)
bash
ipvsadm -Ln输出:
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.1.11:8080 Masq 1 0 0
-> 10.244.2.10:8080 Masq 1 0 0ipvs 的 rr 是真正的轮询,会记录状态,依次选择后端。
4.4 ipvs 与 iptables 的协作
ipvs 模式下,iptables 仍然存在:
- iptables 负责 NodePort 初步匹配
- iptables 负责 SNAT/MASQUERADE
- iptables 负责把 ClusterIP 流量导入 ipvs
PREROUTING / OUTPUT
│
▼
KUBE-SERVICES (iptables)
│
▼
ipvs virtual server (10.96.123.45:80)
│
▼
ipvs real server (Pod:targetPort)
│
▼
POSTROUTING (iptables SNAT)第5章 分发前的后端筛选
5.1 只有 Ready 的 Pod 才会接收流量
EndpointSlice 中 conditions.ready=true 的 Pod 才会被 kube-proxy 加入后端列表。
yaml
endpoints:
- addresses:
- 10.244.1.10
conditions:
ready: true # ✅ 接收流量
- addresses:
- 10.244.1.11
conditions:
ready: false # ❌ 不接收流量因此,生产环境务必配置 readinessProbe。
5.2 Terminating 状态的 Pod
当 Pod 进入 Terminating 状态:
- EndpointSlice 中
ready变为 false - 新连接不再分发到该 Pod
- 但已建立的连接(conntrack 中)仍然继续转发
5.3 externalTrafficPolicy=Local 的筛选
对于 NodePort/LoadBalancer:
Cluster:每个节点的规则包含全部后端 PodLocal:每个节点只包含本节点上的后端 Pod
Node-1 上的 NodePort 规则(Local 模式):
-> 10.244.1.10:8080 (本节点 Pod)
-> 10.244.1.11:8080 (本节点 Pod)
Node-2 上的 NodePort 规则(Local 模式):
-> 10.244.2.10:8080 (本节点 Pod)如果某节点上没有 Pod,访问该节点 NodePort 会被丢弃。
第6章 会话保持(Session Affinity)
6.1 默认行为
默认 sessionAffinity: None,同一客户端的不同连接可能被分发到不同 Pod。
6.2 ClientIP 会话保持
yaml
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 默认 3 小时开启后,同一客户端 IP 的流量始终转发到同一个后端 Pod。
6.3 实现机制
iptables 模式
使用 iptables 的 recent 模块:
bash
iptables -t nat -L KUBE-SVC-XXX -n -v可以看到类似规则:
recent: SET name: KUBE-SEP-XXX side: source mask: 255.255.255.255
recent: UPDATE seconds: 10800 name: KUBE-SEP-XXX side: sourceipvs 模式
使用 sh(源地址哈希)调度算法,或者通过 persistent connection 配置。
6.4 会话保持的局限性
- 对 NAT 后的多客户端可能集中到同一 Pod
- 长连接在 Pod 故障后需要重新建立
- 不适用于无状态负载均衡场景
第7章 外部流量策略的影响
7.1 externalTrafficPolicy=Cluster
外部客户端
│
▼
LB / NodePort
│
▼
任意节点
│
▼
kube-proxy 规则包含全部后端 Pod
│
▼
可能转发到本节点或其他节点 Pod特点:
- 负载均衡效果好
- 可能跨节点转发
- 源 IP 被 SNAT(丢失真实客户端 IP)
7.2 externalTrafficPolicy=Local
外部客户端
│
▼
LB / NodePort
│
▼
某个节点
│
▼
kube-proxy 规则只包含本节点后端 Pod
│
▼
只转发到本节点 Pod特点:
- 保留真实客户端源 IP
- 流量均衡依赖 Pod 分布
- 空节点会丢包
7.3 两种模式后端列表对比
| 节点 | Cluster 模式后端 | Local 模式后端 |
|---|---|---|
| Node-1(有 2 个 Pod) | 全部 Pod | 本节点 2 个 Pod |
| Node-2(有 1 个 Pod) | 全部 Pod | 本节点 1 个 Pod |
| Node-3(无 Pod) | 全部 Pod | 空(丢包) |
第8章 拓扑感知路由(Topology Aware Routing)
8.1 什么是拓扑感知路由
让流量优先在同一可用区(zone)或同一节点(node)内路由,减少跨区流量和延迟。
8.2 启用方式
yaml
apiVersion: v1
kind: Service
metadata:
name: web-service
annotations:
service.kubernetes.io/topology-mode: Auto
spec:
selector:
app: web
ports:
- port: 808.3 对 EndpointSlice 的影响
EndpointSlice 中的 zone 和 nodeName 字段用于拓扑感知:
yaml
endpoints:
- addresses:
- 10.244.1.10
nodeName: node-1
zone: zone-a
- addresses:
- 10.244.2.10
nodeName: node-2
zone: zone-b8.4 流量分发行为
- 同 zone 优先:客户端 Pod 在 zone-a 时,优先选择 zone-a 的后端
- 同 node 优先:客户端 Pod 在 node-1 时,优先选择 node-1 的后端
- 本地无可用后端时,才跨 zone/node
第9章 EndpointSlice 拆分与部分后端不可用
9.1 EndpointSlice 拆分
单个 EndpointSlice 最多包含 100 个 endpoints。当 Service 后端超过 100 个 Pod 时,会拆分为多个 EndpointSlice:
web-service-abc12 (100 endpoints)
web-service-def34 (100 endpoints)
web-service-ghi56 (50 endpoints)9.2 部分 EndpointSlice 不可用的影响
- 只要 EndpointSlice 中有 ready 的 endpoints,kube-proxy 就会把它们加入后端
- 不健康的 Pod 只是不被加入规则,流量自动分配到健康的 Pod
- 全部 Pod 不可用时,Service 无可用后端
9.3 Endpoint 控制器的工作
EndpointSlice Controller 持续:
- Watch Pod 变化
- 根据 selector 匹配 Pod
- 根据 readinessProbe 状态更新
ready字段 - 同步到 EndpointSlice
第10章 实际案例分析
10.1 案例一:3 个后端的 ClusterIP 分发
Service: web-service, ClusterIP: 10.96.123.45, port: 80
Pod A: 10.244.1.10:8080
Pod B: 10.244.1.11:8080
Pod C: 10.244.2.10:8080iptables 模式规则
Chain KUBE-SVC-WEB (1 references)
target prot opt source destination
KUBE-SEP-A all -- 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.3333333333
KUBE-SEP-B all -- 0.0.0.0/0 0.0.0.0/0 statistic mode random probability 0.5000000000
KUBE-SEP-C all -- 0.0.0.0/0 0.0.0.0/0ipvs 模式规则
TCP 10.96.123.45:80 rr
-> 10.244.1.10:8080 Masq 1 0 0
-> 10.244.1.11:8080 Masq 1 0 0
-> 10.244.2.10:8080 Masq 1 0 010.2 案例二:Pod 缩容后的分发
缩容前 4 个 Pod,缩容后 2 个 Pod:
iptables 模式:
KUBE-SEP-A probability 0.5000000000
KUBE-SEP-B probability 1.0000000000ipvs 模式:
TCP 10.96.123.45:80 rr
-> 10.244.1.10:8080 Masq 1 0 0
-> 10.244.1.11:8080 Masq 1 0 010.3 案例三:externalTrafficPolicy=Local 的 NodePort
Node-1: 2 个 Pod
Node-2: 1 个 Pod
Node-3: 0 个 PodNode-1 上的 NodePort 规则:
TCP 192.168.1.10:30080 rr
-> 10.244.1.10:8080 Masq 1
-> 10.244.1.11:8080 Masq 1Node-3 上的 NodePort 规则:
TCP 192.168.1.12:30080 rr
-> (空)因此,LB 必须配合健康检查,避免将流量发送到 Node-3。
第11章 排障与验证命令
11.1 查看 Service 与后端
bash
# Service 信息
kubectl get svc web-service -o wide
# Endpoints
kubectl get endpoints web-service
# EndpointSlice
kubectl get endpointslices -l kubernetes.io/service-name=web-service -o yaml
# Pod 标签
kubectl get pods -l app=web --show-labels
# Pod readiness
kubectl get pods -l app=web11.2 查看 kube-proxy 规则
bash
# iptables 模式
iptables -t nat -L KUBE-SERVICES -n -v | grep web-service
iptables -t nat -L KUBE-SVC-XXX -n -v
iptables -t nat -L KUBE-SEP-XXX -n -v
# ipvs 模式
ipvsadm -Ln
ipvsadm -Lnc
# 查看当前 kube-proxy 模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode11.3 流量分发验证
bash
# 从 Pod 内多次访问 Service,观察后端日志
kubectl exec -it <client-pod> -- sh
for i in $(seq 1 100); do curl -s http://web-service; done
# 查看各 Pod 访问日志
kubectl logs <pod-a>
kubectl logs <pod-b>
kubectl logs <pod-c>11.4 查看 conntrack
bash
# 查看 Service 的连接跟踪
conntrack -L | grep 10.96.123.45
# 查看某个后端的连接
conntrack -L -d 10.244.1.10第12章 总结
12.1 核心结论
- Service 不直接分发流量,真正分发的是 kube-proxy。
- kube-proxy 根据 EndpointSlice 中 ready 的 Pod IP:targetPort 生成规则。
- iptables 模式使用随机算法分发。
- ipvs 模式使用调度算法(默认 rr 轮询)分发。
- 分发前会经过多重筛选:ready 状态、externalTrafficPolicy、sessionAffinity、topology 等。
- 已建立的连接由 conntrack 维护,Pod 变化不影响老连接。
12.2 模式选择建议
| 场景 | 推荐模式 |
|---|---|
| 小规模、简单场景 | iptables |
| 大规模、大量后端 | ipvs |
| 需要真正轮询 | ipvs (rr) |
| 需要会话保持 | ipvs (sh) 或 iptables + sessionAffinity |
| 对延迟敏感 | ipvs |
12.3 关键认知
- EndpointSlice 只是元数据,流量分发规则由 kube-proxy 生成。
- 负载均衡发生在节点内核态,不经过 kube-proxy 进程。
- Pod 状态变化后,新连接立即按新规则分发,老连接由 conntrack 维持。
文档生成时间:2026-07-18