Skip to content

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。


目录

  1. 核心对象关系回顾
  2. 流量分发的真实执行者:kube-proxy
  3. iptables 模式下的分发机制
  4. ipvs 模式下的分发机制
  5. 分发前的后端筛选
  6. 会话保持(Session Affinity)
  7. 外部流量策略的影响
  8. 拓扑感知路由(Topology Aware Routing)
  9. EndpointSlice 拆分与部分后端不可用
  10. 实际案例分析
  11. 排障与验证命令
  12. 总结

第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 B

1.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 IP
  • port:容器实际监听的 targetPort
  • conditions.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 -v

3 个后端时的规则示例:

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 个概率命中 POD1111/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.0000000000

3 个后端时

KUBE-SEP-111  probability 0.3333333333
KUBE-SEP-222  probability 0.5000000000
KUBE-SEP-333  probability 1.0000000000

kube-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          0

ipvs 的 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:每个节点的规则包含全部后端 Pod
  • Local:每个节点只包含本节点上的后端 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: source

ipvs 模式

使用 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: 80

8.3 对 EndpointSlice 的影响

EndpointSlice 中的 zonenodeName 字段用于拓扑感知:

yaml
endpoints:
  - addresses:
      - 10.244.1.10
    nodeName: node-1
    zone: zone-a
  - addresses:
      - 10.244.2.10
    nodeName: node-2
    zone: zone-b

8.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 持续:

  1. Watch Pod 变化
  2. 根据 selector 匹配 Pod
  3. 根据 readinessProbe 状态更新 ready 字段
  4. 同步到 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:8080

iptables 模式规则

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/0

ipvs 模式规则

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          0

10.2 案例二:Pod 缩容后的分发

缩容前 4 个 Pod,缩容后 2 个 Pod:

iptables 模式

KUBE-SEP-A  probability 0.5000000000
KUBE-SEP-B  probability 1.0000000000

ipvs 模式

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.3 案例三:externalTrafficPolicy=Local 的 NodePort

Node-1: 2 个 Pod
Node-2: 1 个 Pod
Node-3: 0 个 Pod

Node-1 上的 NodePort 规则:

TCP  192.168.1.10:30080 rr
  -> 10.244.1.10:8080             Masq    1
  -> 10.244.1.11:8080             Masq    1

Node-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=web

11.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 mode

11.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 核心结论

  1. Service 不直接分发流量,真正分发的是 kube-proxy。
  2. kube-proxy 根据 EndpointSlice 中 ready 的 Pod IP:targetPort 生成规则。
  3. iptables 模式使用随机算法分发。
  4. ipvs 模式使用调度算法(默认 rr 轮询)分发。
  5. 分发前会经过多重筛选:ready 状态、externalTrafficPolicy、sessionAffinity、topology 等。
  6. 已建立的连接由 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