bigywater.tech
Kubernetes · 73

Service 会话保持:ClientIP 的超时

sessionAffinity: ClientIP 到底是怎么做的?超时是从第一次访问算还是最近一次?不同客户端会固定到同一个后端吗?后端被删了怎么办?这篇看 iptables 的 recent 规则并做定时实验。

30 / 30
ClientIP 会话保持下,同一客户端 30 个连接落在同一个后端的数量
12/12 与 8/12
超时 3 秒时,间隔 1 秒的访问仍粘住,间隔 4 秒的访问有 8 次换了后端
recent
iptables 实现会话保持用的内核模块,每个后端一张表

有无会话保持

实测 · 本机执行输出
  plain(None):[11, 10, 9](各后端收到的数量,从多到少)
  sticky(ClientIP):[30](全部落在同一个后端)
  另一个客户端 cli2 访问 sticky:[30];两个客户端分别固定到哪个后端:cli1→wgtmr cli2→44wps(各自独立,不一定相同)

同一个客户端 cli1 发 30 个新连接:plain Service(sessionAffinity: None)分布是 11/10/9;sticky Service(sessionAffinity: ClientIP,默认 timeoutSeconds: 10800)30 个全在一个后端。另一个客户端 cli2 访问 sticky 也全落在一个后端,但和 cli1 不是同一个——按来源 IP 各自固定。

iptables 里的 recent 模块

实测 · 本机执行输出
  sticky 的 SVC 链 KUBE-SVC-ORWA43K63G6M2SJE:
    KUBE-SVC-ORWA43K63G6M2SJE ! -s 10.244.0.0/16 -d 10.96.150.53/32 -p tcp -m tcp --dport 80 -j KUBE-MARK-MASQ
    KUBE-SVC-ORWA43K63G6M2SJE -m recent --rcheck --seconds 10800 --reap --name KUBE-SEP-BVBQE3KGEAB4HYQO --mask 255.255.255.255 --rsource -j KUBE-SEP-BVBQE3KGEAB4HYQO
    KUBE-SVC-ORWA43K63G6M2SJE -m recent --rcheck --seconds 10800 --reap --name KUBE-SEP-ELHBVIMXRY3OTTBH --mask 255.255.255.255 --rsource -j KUBE-SEP-ELHBVIMXRY3OTTBH
    KUBE-SVC-ORWA43K63G6M2SJE -m recent --rcheck --seconds 10800 --reap --name KUBE-SEP-YJCYVERRI7CLOUUO --mask 255.255.255.255 --rsource -j KUBE-SEP-YJCYVERRI7CLOUUO
    KUBE-SVC-ORWA43K63G6M2SJE -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-BVBQE3KGEAB4HYQO
    KUBE-SVC-ORWA43K63G6M2SJE -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-ELHBVIMXRY3OTTBH
    KUBE-SVC-ORWA43K63G6M2SJE -j KUBE-SEP-YJCYVERRI7CLOUUO
  其中一个 SEP 链 KUBE-SEP-BVBQE3KGEAB4HYQO:
    KUBE-SEP-BVBQE3KGEAB4HYQO -s 10.244.1.173/32 -j KUBE-MARK-MASQ
    KUBE-SEP-BVBQE3KGEAB4HYQO -p tcp -m recent --set --name KUBE-SEP-BVBQE3KGEAB4HYQO --mask 255.255.255.255 --rsource -m tcp -j DNAT --to-destination 10.244.1.173:80
  内核 recent 表(每个后端一张,记录客户端 IP):['KUBE-SEP-BVBQE3KGEAB4HYQO', 'KUBE-SEP-ELHBVIMXRY3OTTBH', 'KUBE-SEP-YJCYVERRI7CLOUUO']
  KUBE-SEP-BVBQE3KGEAB4HYQO 里记录的客户端:['src=10.244.1.174 ttl: 64 last_seen: 4320743250 oldest_pkt: 31 4320743028, 4320743029, 4320743031, 4320743032, ']

sticky 的 SVC 链前三条是 -m recent --rcheck --seconds 10800 --reap --name KUBE-SEP-… -j KUBE-SEP-…:每个后端一条,检查来源 IP 是否在该后端的 recent 表里,在就直接跳过去;都没命中才走后面的 statistic random 抽签。SEP 链里的 DNAT 规则带 -m recent --set,把客户端 IP 记进这个后端的表。内核的 /proc/net/xt_recent/ 下每个后端一张表,能看到 src=10.244.1.174 和它的访问时间戳。

超时与刷新

实测 · 本机执行输出
  timeoutSeconds 改成 3:{'clientIP': {'timeoutSeconds': 3}}
  规则里的 --seconds:['3']
  12 轮试验:间隔 1 秒(<3 秒)的两次访问落在同一个后端 12/12;间隔 4 秒(>3 秒)的两次访问落在不同后端 8/12(超时后重新抽签,3 个后端时换一个的概率约 2/3)
  每 2 秒访问一次(每次都在 3 秒超时内)共 7 次:1 个不同后端(每次访问都刷新计时器,所以一直粘在同一个)

把 timeoutSeconds 改成 3,规则里的 --seconds 随之变成 3。12 轮试验:间隔 1 秒(小于超时)的两次访问落在同一个后端 12/12;间隔 4 秒(大于超时)的两次访问有 8 次落在不同后端(超时后重新抽签,3 个后端时换一个的概率约 2/3)。每 2 秒访问一次共 7 次始终是同一个后端:每次访问都会刷新计时器,超时是从上一次访问算起,而不是第一次。

后端消失

实测 · 本机执行输出
  固定的后端 44wps(超时 10800 秒)被删除后立即重新访问 5 次:['qzvtf', 'qzvtf', 'qzvtf', 'qzvtf', 'qzvtf'](客户端被重新分配到存活的后端;之后又固定下来:True)

固定的后端 44wps 被删除后,客户端立即重新访问 5 次,全部落到 qzvtf:被重新分配到存活的后端,并固定在它上面。所以滚动更新期间,会话保持的客户端会被打散到新的 Pod。

分步演示

会话保持
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0),kube-proxy iptables 模式;后端是 3 副本 busybox httpd,返回各自主机名;客户端是两个 busybox Pod(cli1、cli2)里循环 wget(每次新连接);超时试验的间隔是 sleep 控制,精度约 ±0.3 秒;每种情形各一次(第三节 12 轮)
    • 没有验证:IPVS / nftables 模式下会话保持的实现(IPVS 用 persistent,nftables 用 set);通过 NodePort / LoadBalancer 进来时的源 IP(受 externalTrafficPolicy 和 SNAT 影响,见下一篇);timeoutSeconds 的取值范围 1~86400;客户端经过 NAT 后共享出口 IP 时会被当成同一个客户端;sessionAffinity 与 Headless Service 的关系
    京ICP备2026067502号-1京公网安备11010502063487号