K8s服务内部负载均衡:集群内流量均衡实现
在Kubernetes集群中,Pod的IP地址随重建而变化,数量也可能随时伸缩。为了让集群内的应用能够稳定地互相访问,Kubernetes提供了Service这一抽象层。它用一个固定的虚拟IP(ClusterIP)和DNS名称代表一组Pod,并在请求到达时把流量分散到多个后端实例上,这就是K8s服务内部负载均衡的核心机制。
Service是集群内负载均衡的入口
当创建一个类型为ClusterIP的Service时,Kubernetes会为其分配一个仅集群内可达的虚拟IP,同时通过标签选择器(selector)关联一组Pod。访问方只需连接这个虚拟IP或对应的DNS名称,无需关心后端究竟有多少个Pod、运行在哪些节点上。
Service本身并不转发数据包,真正完成流量分发的是每个节点上的kube-proxy组件,以及可选的Service Mesh数据面。理解内部负载均衡的实现,需要沿着“Service → EndpointSlice → 节点转发规则”这条链路来看。
数据面:kube-proxy如何把请求分发到Pod
kube-proxy监听Service与EndpointSlice的变化,在节点上维护转发规则。不同实现模式下,分发行为的细节有所差异。
iptables模式
这是长期以来的默认模式。kube-proxy为每个Service生成一组iptables规则,当数据包命中ClusterIP时,规则会以概率方式在多个后端Pod中挑选一个,并将目标地址改写为Pod IP。由于选择过程基于随机概率,整体上流量会近似均匀地分布到各端点,但单条连接一旦建立就固定在同一后端上。
IPVS模式
IPVS是内核中的四层负载均衡模块,使用哈希表组织转发规则。相比iptables的线性规则链,它在Service数量较多时规则匹配的开销更小,并且原生支持多种调度算法,例如轮询、加权轮询、最少连接、源地址哈希等。kube-proxy可以通过参数指定默认调度算法,也可以通过Service的注解为单个服务指定算法。
nftables模式
较新的Kubernetes版本引入了基于nftables的代理模式,用nftables规则替代大规模iptables链,目标是改善大量Service场景下的规则更新效率与转发性能。该模式在逐步成熟的过程中,实际可用性取决于集群版本与kube-proxy配置。
调度策略与会话保持
在IPVS模式下,集群内均衡的行为更接近传统负载均衡器,可选策略包括:
- 轮询(rr):按顺序依次分发,适合后端处理能力相近的无状态服务。
- 加权轮询(wrr):按权重分配,适合实例规格不一致的场景。
- 最少连接(lc/wlc):优先选择当前连接数较少的后端,适合请求耗时差异较大的服务。
- 源地址哈希(sh):同一来源的请求尽量落到同一后端。
如果业务需要同一客户端持续访问同一Pod,可以使用Service的sessionAffinity字段。默认值为None,不做会话保持;设置为ClientIP时,同一客户端IP的请求会被固定到同一后端,保持时长可通过sessionAffinityConfig配置,未显式设置时使用集群默认值。
EndpointSlice与端点健康状态
Service的后端列表由EndpointSlice对象承载。相比早期的Endpoints资源,EndpointSlice把端点切分成多个较小的对象,在大规模集群中能减少单次变更带来的数据量,也便于kube-proxy增量更新转发规则。
只有通过就绪探针(readinessProbe)检查的Pod才会以Ready状态出现在EndpointSlice中,从而接收新流量。当Pod进入终止流程时,其端点会被标记为terminating,控制器会尽量让它不再接收新连接,但已经建立的连接通常不会被强行中断。这意味着就绪探针配置的合理性会直接影响内部负载均衡的实际效果:探针过于宽松会把流量引向尚未准备好的实例,过于严格则可能让后端数量不足。
Headless Service:把选择权交给客户端
把Service的clusterIP设置为None,就得到无头服务(Headless Service)。此时不会分配虚拟IP,DNS查询会直接返回所有就绪Pod的地址列表,由客户端自行决定连接哪一个。这种方式常见于有状态应用、需要客户端侧负载均衡或需要感知每个实例的场景。
无头服务模式下,均衡逻辑转移到了客户端或应用框架中,因此客户端是否具备合适的重试、连接池与选址策略,会显著影响最终的流量分布。
长连接场景下的均衡失真
四层负载均衡的工作单位是连接,而不是请求。当应用使用HTTP/2、gRPC或长轮询等长连接协议时,连接一旦建立就很少重建,流量便可能长期集中在少数几个Pod上,出现“连接数看着均衡、实际请求严重倾斜”的现象。
常见应对思路包括:
- 在客户端使用支持负载均衡的连接策略,例如gRPC的多地址解析与轮询策略,而不是长期复用单一连接。
- 为长连接设置合理的最大存活时间,让连接周期性重建。
- 在服务网格中启用七层负载均衡,按请求而不是按连接分发。
- 结合拓扑感知路由(Topology Aware Routing),让流量优先流向与调用方位于同一区域的端点,降低跨区域延迟。
实践建议
- 为业务Pod配置合理的就绪探针,确保只有真正可服务的实例进入端点列表。
- 根据集群规模和数据面特征选择合适的kube-proxy模式,Service数量较多时可关注IPVS或nftables。
- 无状态服务一般无需会话保持;有状态或依赖本地缓存的场景再评估sessionAffinity的代价。
- 长连接服务优先从客户端或七层代理层面解决均衡问题,不要只依赖四层转发规则。
- 排障时可以从Service、EndpointSlice、Pod就绪状态和节点转发规则几个层面依次确认,定位流量到底在哪一步没有按预期分发。
K8s服务内部负载均衡并不是单一组件完成的动作,而是API层抽象、端点管理与节点数据面协同的结果。理解这条链路,才能在实际部署中让集群内访问的流量真正均匀地落到每个可用的Pod上。