先记住这个答案
设置 sessionAffinity: ClientIP 后,kube-proxy 会为来自同一源 IP 的新连接分到同一个后端 Pod,并在 timeoutSeconds(默认10800秒)内保持。它只识别 IP,不解析 HTTP,因此带 Cookie 的应用会话仍需自身处理;多个客户端共用出口 IP 会导致流量倾斜。该功能对 ClusterIP、NodePort 有效,对 headless 不适用。
- kube-proxy 层实现,基于源 IP 哈希粘滞
- 不解析 HTTP,Cookie 需应用自己处理
- 后端变动或 NAT 共源 IP 会造成会话偏移
kube-proxy 如何在数据面完成客户端粘滞
当 Service 定义中设置 sessionAffinity: ClientIP 时,kube-proxy 会启动虚拟 IP 转发逻辑,为每个源 IP 计算一个后端索引。iptables 模式利用内核连接跟踪和自定义链路记录选择结果;IPVS 模式则启用基于源 IP 的持久性调度。规则中会保存源 IP、目标后端和过期时间,同源新连接在超时内直接复用同一选择,实现粘滞效果。
默认超时 timeoutSeconds 为 10800 秒,可通过 sessionAffinityConfig.clientIP.timeoutSeconds 调整,最小值为 1。
购物车服务在 NAT 后端的错误假设
假设某微服务商城使用三个 Pod 副本,Service 以 NodePort 暴露并设置 sessionAffinity: ClientIP,期望每个用户的购物车数据固定在同一个 Pod 上。压测从一台跳板机发起,该跳板机源 IP 固定,模拟 500 个在线用户,预期所有请求都会打到一个 Pod,其余两个几乎零负载,热点 Pod 可能会因 CPU 超限频繁重启,导致服务整体抖动。
根因是所有客户端经过跳板机出口 NAT,源 IP 被收敛为同一值,kube-proxy 按 IP 哈希自然全部映射到同一后端,毫无负载均衡可言。此场景下应在前端接入支持 Cookie 粘滞的 Ingress 或外部负载均衡器,将路由粒度提升到 HTTP 会话级别,并让后端使用独立的会话存储(如 Redis),才能既保留用户状态又避免单点热点。
适用边界与失效条件
sessionAffinity 只对虚拟 IP 服务生效,包括 ClusterIP 和 NodePort;Headless Service 没有 cluster IP,DNS 直接返回所有端点,因此该字段无意义。对于 NodePort 或 LoadBalancer 类型的 Service,若设置 externalTrafficPolicy: Cluster,节点转发时会做 SNAT,后端看到的源 IP 变为节点 IP,同样会导致粘滞错乱;此时应搭配 externalTrafficPolicy: Local 以保留真实源 IP。
当 Pod 集合变化(滚动发布、崩溃重启、扩缩容),哈希映射会重新计算,已建立的 TCP 连接仍依靠 conntrack 保持,但新连接可能被分到新 Pod,导致应用层会话中断。此外,客户端重启或更换网络导致源 IP 变化也会丢失粘滞。因此生产中通常不单独依赖此特性,而是结合应用层复制和外部一致性哈希实现高可用有状态服务。
容易答错的地方
- 设置后会话永不迁移
- 纠正:sessionAffinity 的映射会在后端列表变化时重新计算。例如 Deployment 滚动更新导致 Pod IP 变化,新增或删除副本后,新连接可能分布到不同 Pod;超时默认 3 小时,到期也会触发重选。
- 能维持登录态的 Cookie 会话
- 纠正:它只按源 IP 四层识别,完全不解析 HTTP,不读取 Cookie、Session ID。应用自身或七层代理(如 Ingress)才具备 cookie 保持能力,Kubernetes Service 层面无法做到。
面试官还会怎么问?
ClientIP 与 Cookie 粘滞的可靠性差距在哪?
ClientIP 依赖网络层,存在 NAT 时海量用户共享 IP,粘滞粒度粗且易倾斜;Cookie 由服务端生成,隔离到会话,但需要代理层次支持。例如 Ingress 的 affinity 基于 Cookie,Service 的 affinity 基于 IP,二者不能混同。
timeoutSeconds 设多大合适?
默认 10800 秒适合一般 Web 场景,但若后端经常扩缩容,时间过长会放大失效可能性。建议参考应用会话生命周期,通常 300-3600 秒,并配合 readiness 探针避免把流量发给正在终止的 Pod。
如何验证 sessionAffinity 是否真正按 IP 粘滞?
在 Pod 里记录访问的来源 IP 和请求路径,从外部使用固定 IP 连续请求 Service 的 NodePort 或 LoadBalancer 地址(避免走后端 Pod 直接访问 ClusterIP),观察日志是否落在同一 Pod。注意集群网络可能做 SNAT,需确保使用 externalTrafficPolicy: Local 保留源 IP,或查看 kube-proxy 的 conntrack 表确认 NAT 情况。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。