虚拟 IP 和服务代理

Kubernetes 集群中的每个节点都运行一个 kube-proxy (除非你已部署自己的替代组件来代替 kube-proxy)。

kube-proxy 组件负责为 type 不是 ExternalName服务实现一个虚拟 IP 机制。kube-proxy 的每个实例都监视 Kubernetes 控制平面,以添加和删除服务和 EndpointSlice 对象。对于每个服务,kube-proxy 调用适当的 API(取决于 kube-proxy 模式)来配置节点以捕获到该服务的 clusterIPport 的流量,并将该流量重定向到该服务的一个端点(通常是一个 Pod,但也可能是任意用户提供的 IP 地址)。一个控制循环确保每个节点上的规则与 API 服务器指示的服务和 EndpointSlice 状态可靠地同步。

使用 iptables 模式的服务虚拟 IP 机制

一个不时出现的问题是,为什么 Kubernetes 依赖代理来将入站流量转发到后端。其他方法呢?例如,是否可以配置具有多个 A 值(或 IPv6 的 AAAA)的 DNS 记录,并依赖于轮询名称解析?

使用代理服务有几个原因

  • DNS 实现不遵守记录 TTL,并在它们应该过期后缓存名称查找结果,这已经有很长的历史了。
  • 有些应用程序只进行一次 DNS 查找并无限期地缓存结果。
  • 即使应用程序和库进行了正确的重新解析,DNS 记录上的低或零 TTL 也可能对 DNS 施加很高的负载,从而难以管理。

在本页的后面,你可以阅读有关各种 kube-proxy 实现如何工作的详细信息。总的来说,你应该注意到,在运行 kube-proxy 时,内核级别的规则可能会被修改(例如,可能会创建 iptables 规则),在某些情况下,这些规则不会被清理,直到你重新启动。因此,运行 kube-proxy 应该只由了解在计算机上拥有低级别、特权网络代理服务的后果的管理员完成。尽管 kube-proxy 可执行文件支持 cleanup 功能,但此功能不是官方功能,因此只能按原样使用。

本参考中的一些详细信息引用了一个示例:无状态图像处理工作负载的后端 Pod,运行三个副本。这些副本是可互换的——前端不在乎它们使用哪个后端。虽然组成后端集的实际 Pod 可能会发生变化,但前端客户端不应需要知道这一点,也不应需要自己跟踪后端集。

代理模式

kube-proxy 以不同的模式启动,这由其配置决定。

在 Linux 节点上,kube-proxy 的可用模式为

iptables
一种 kube-proxy 使用 iptables 配置数据包转发规则的模式。
ipvs
一种 kube-proxy 使用 ipvs 配置数据包转发规则的模式。
nftables
一种 kube-proxy 使用 nftables 配置数据包转发规则的模式。

在 Windows 上,kube-proxy 只有一种可用模式

kernelspace
一种 kube-proxy 在 Windows 内核中配置数据包转发规则的模式

iptables 代理模式

此代理模式仅在 Linux 节点上可用。

在这种模式下,kube-proxy 使用内核 netfilter 子系统的 iptables API 配置数据包转发规则。对于每个端点,它安装 iptables 规则,这些规则默认随机选择后端 Pod。

示例

例如,考虑本页前面描述的图像处理应用程序。创建后端服务时,Kubernetes 控制平面会分配一个虚拟 IP 地址,例如 10.0.0.1。对于此示例,假设服务端口为 1234。集群中的所有 kube-proxy 实例都会观察到新服务的创建。

当节点上的 kube-proxy 看到新服务时,它会安装一系列 iptables 规则,这些规则将从虚拟 IP 地址重定向到更多 iptables 规则,这些规则是按服务定义的。每个服务规则链接到每个后端端点的进一步规则,并且每个端点规则将流量(使用目标 NAT)重定向到后端。

当客户端连接到服务的虚拟 IP 地址时,iptables 规则会启动。选择一个后端(基于会话亲和性或随机),并且数据包被重定向到后端,而无需重写客户端 IP 地址。

当流量通过节点端口或负载均衡器进入时,也会执行相同的基本流程,尽管在这些情况下,客户端 IP 地址确实会被更改。

优化 iptables 模式性能

在 iptables 模式下,kube-proxy 为每个服务创建一些 iptables 规则,并为每个端点 IP 地址创建一些 iptables 规则。在具有数万个 Pod 和服务的集群中,这意味着数万个 iptables 规则,并且当服务(或其 EndpointSlice)发生更改时,kube-proxy 可能需要很长时间才能更新内核中的规则。你可以通过 kube-proxy iptables 部分中的选项调整 kube-proxy 的同步行为,配置文件(你通过 kube-proxy --config <path> 指定)。

...
iptables:
  minSyncPeriod: 1s
  syncPeriod: 30s
...
minSyncPeriod

minSyncPeriod 参数设置尝试将 iptables 规则与内核重新同步之间的最短持续时间。如果它是 0s,则每当任何服务或端点发生更改时,kube-proxy 将始终立即同步规则。这在非常小的集群中效果很好,但当短时间内发生大量更改时,会导致大量冗余工作。例如,如果你的服务由 100 个 Pod 的Deployment 支持,并且你删除了 Deployment,那么如果 minSyncPeriod: 0s,kube-proxy 最终会从 iptables 规则中逐个删除服务的端点,总共进行 100 次更新。使用更大的 minSyncPeriod,多个 Pod 删除事件将聚合在一起,因此 kube-proxy 最终可能会进行 5 次更新,每次删除 20 个端点,这在 CPU 方面会更有效,并导致更快地同步完整的更改集。

minSyncPeriod 的值越大,可以聚合的工作越多,但缺点是每个单独的更改最终可能需要等待完整的 minSyncPeriod 才能被处理,这意味着 iptables 规则花费更多时间与当前的 API 服务器状态不同步。

1s 的默认值应该在大多数集群中运行良好,但在非常大的集群中,可能需要将其设置为更大的值。特别是,如果 kube-proxy 的 sync_proxy_rules_duration_seconds 指标指示的平均时间远大于 1 秒,则增加 minSyncPeriod 可能会使更新更有效率。

更新旧的 minSyncPeriod 配置

旧版本的 kube-proxy 在每次同步时都会更新所有 Service 的所有规则;这导致大型集群中出现性能问题(更新延迟),推荐的解决方案是设置较大的 minSyncPeriod。自 Kubernetes v1.28 起,kube-proxy 的 iptables 模式采用了更精简的方法,仅在 Service 或 EndpointSlice 实际发生更改时进行更新。

如果您之前覆盖了 minSyncPeriod,您应该尝试移除该覆盖,并让 kube-proxy 使用默认值(1s)或至少比升级前使用的值小的值。

如果您运行的 kube-proxy 版本不是 Kubernetes 1.32,请查看您实际运行版本的行为和相关建议。

syncPeriod

syncPeriod 参数控制一些与单个 Service 和 EndpointSlice 的更改没有直接关系的同步操作。特别是,它控制 kube-proxy 注意到外部组件干扰 kube-proxy 的 iptables 规则的速度。在大型集群中,kube-proxy 也只会在每个 syncPeriod 执行某些清理操作,以避免不必要的工作。

在大多数情况下,增加 syncPeriod 预计不会对性能产生太大影响,但过去,有时将其设置为非常大的值(例如,1h)是有用的。现在不推荐这样做,并且它可能会损害功能而不是提高性能。

IPVS 代理模式

此代理模式仅在 Linux 节点上可用。

ipvs 模式下,kube-proxy 使用内核 IPVS 和 iptables API 创建规则,以将来自 Service IP 的流量重定向到端点 IP。

IPVS 代理模式基于类似于 iptables 模式的 netfilter hook 函数,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 IPVS 模式下的 kube-proxy 比 iptables 模式下的 kube-proxy 以更低的延迟重定向流量,并且在同步代理规则时具有更好的性能。与 iptables 代理模式相比,IPVS 模式还支持更高的网络流量吞吐量。

IPVS 为将流量平衡到后端 Pod 提供了更多选项;这些选项是

  • rr(轮询):流量在后端服务器之间均匀分配。

  • wrr(加权轮询):流量根据服务器的权重路由到后端服务器。权重较高的服务器会接收新的连接并获得比权重较低的服务器更多的请求。

  • lc(最少连接):更多流量被分配给活动连接较少的服务器。

  • wlc(加权最少连接):更多流量被路由到连接数相对于其权重较少的服务器,即连接数除以权重。

  • lblc(基于位置的最少连接):如果服务器没有过载并且可用,则将同一 IP 地址的流量发送到同一后端服务器;否则,将流量发送到连接较少的服务器,并将其保留以供将来分配。

  • lblcr(基于位置的带有复制的最少连接):将同一 IP 地址的流量发送到连接最少的服务器。如果所有后端服务器都过载,它会选择一个连接较少的服务器并将其添加到目标集。如果目标集在指定的时间内没有更改,则会从该集中删除负载最高的服务器,以避免高度复制。

  • sh(源哈希):流量通过查找基于源 IP 地址静态分配的哈希表发送到后端服务器。

  • dh(目标哈希):流量通过查找基于其目标地址静态分配的哈希表发送到后端服务器。

  • sed(最短预期延迟):流量转发到预期延迟最短的后端服务器。如果发送到服务器,则预期延迟为 (C + 1) / U,其中 C 是服务器上的连接数,U 是服务器的固定服务速率(权重)。

  • nq(从不排队):如果有空闲服务器,则将流量发送到空闲服务器,而不是等待快速服务器;如果所有服务器都很忙,则算法会回退到 sed 行为。

  • mh(Maglev 哈希):根据 Google 的 Maglev 哈希算法分配传入作业。此调度程序有两个标志:mh-fallback,如果选定的服务器不可用,则启用回退到其他服务器,以及 mh-port,它将源端口号添加到哈希计算中。使用 mh 时,kube-proxy 始终设置 mh-port 标志,并且不启用 mh-fallback 标志。在 proxy-mode=ipvs 中,mh 将作为源哈希 (sh) 工作,但带有端口。

这些调度算法通过 kube-proxy 配置中的 ipvs.scheduler 字段进行配置。

使用 IPVS 模式的 Service 虚拟 IP 地址机制

nftables 代理模式

特性状态: Kubernetes v1.31 [beta] (默认启用: true)

此代理模式仅在 Linux 节点上可用,并且需要内核 5.13 或更高版本。

在此模式下,kube-proxy 使用内核 netfilter 子系统的 nftables API 配置数据包转发规则。对于每个端点,它会安装 nftables 规则,默认情况下,这些规则会随机选择一个后端 Pod。

nftables API 是 iptables API 的后继者,旨在提供比 iptables 更好的性能和可扩展性。nftables 代理模式能够比 iptables 模式更快、更有效地处理服务端点的更改,并且还能够更有效地处理内核中的数据包(尽管这只有在具有数万个服务的集群中才会变得明显)。

截至 Kubernetes 1.32,nftables 模式仍然相对较新,可能与并非所有网络插件兼容;请查阅您的网络插件的文档。

iptables 模式迁移到 nftables

想要从默认的 iptables 模式切换到 nftables 模式的用户应该意识到,某些功能在 nftables 模式下的工作方式略有不同

  • NodePort 接口:在 iptables 模式下,默认情况下,NodePort 服务可以在所有本地 IP 地址上访问。这通常不是用户想要的,因此 nftables 模式默认为 --nodeport-addresses primary,这意味着 NodePort 服务仅在节点的 primary IPv4 和/或 IPv6 地址上可访问。您可以通过为该选项指定显式值来覆盖此设置:例如,--nodeport-addresses 0.0.0.0/0 以侦听所有(本地)IPv4 IP。

  • 127.0.0.1 上的 NodePort 服务:在 iptables 模式下,如果 --nodeport-addresses 范围包括 127.0.0.1(并且未传递选项 --iptables-localhost-nodeports false),则即使在 “localhost”(127.0.0.1)上也可以访问 NodePort 服务。在 nftables 模式(和 ipvs 模式)下,这不起作用。如果您不确定是否依赖此功能,可以检查 kube-proxy 的 iptables_localhost_nodeports_accepted_packets_total 指标;如果它不是 0,则表示某些客户端已通过 127.0.0.1 连接到 NodePort 服务。

  • NodePort 与防火墙的交互:kube-proxy 的 iptables 模式尝试与过于激进的防火墙兼容;对于每个 NodePort 服务,它都会添加规则以接受该端口上的入站流量,以防该流量被防火墙阻止。此方法不适用于基于 nftables 的防火墙,因此 kube-proxy 的 nftables 模式在此处不做任何操作;如果您有本地防火墙,则必须确保它已正确配置为允许 Kubernetes 流量通过(例如,允许整个 NodePort 范围内的入站流量)。

  • Conntrack 错误解决方法:6.1 之前的 Linux 内核存在一个错误,可能会导致到服务 IP 的长期 TCP 连接以错误 “Connection reset by peer” 关闭。kube-proxy 的 iptables 模式为此错误安装了一个解决方法,但后来发现该解决方法在某些集群中会导致其他问题。nftables 模式默认情况下不安装任何解决方法,但您可以检查 kube-proxy 的 iptables_ct_state_invalid_dropped_packets_total 指标,以查看您的集群是否依赖于该解决方法,如果是这样,您可以使用选项 --conntrack-tcp-be-liberal 运行 kube-proxy,以解决 nftables 模式下的问题。

kernelspace 代理模式

此代理模式仅在 Windows 节点上可用。

kube-proxy 在 Windows 虚拟过滤平台 (VFP)(Windows vSwitch 的扩展)中配置数据包过滤规则。这些规则处理节点级虚拟网络内的封装数据包,并重写数据包,以便目标 IP 地址(和第 2 层信息)对于将数据包路由到正确的目标是正确的。Windows VFP 类似于 Linux nftablesiptables 等工具。Windows VFP 扩展了最初为支持虚拟机网络而实现的Hyper-V Switch

当节点上的 Pod 将流量发送到虚拟 IP 地址,并且 kube-proxy 选择另一个节点上的 Pod 作为负载平衡目标时,kernelspace 代理模式会将该数据包重写为目标后端 Pod。Windows 主机网络服务 (HNS) 确保配置数据包重写规则,以便返回流量看起来来自虚拟 IP 地址而不是特定的后端 Pod。

kernelspace 模式的直接服务器返回

特性状态: Kubernetes v1.14 [alpha]

作为基本操作的替代方案,托管 Service 的后端 Pod 的节点可以直接应用数据包重写,而不是将此负担放在客户端 Pod 运行的节点上。这称为直接服务器返回

要使用此功能,您必须使用 --enable-dsr 命令行参数运行 kube-proxy **并** 启用 WinDSR 特性门控

即使两个 Pod 都在同一节点上运行,直接服务器返回也可以优化 Pod 返回流量的情况。

会话亲和性

在这些代理模型中,发送到 Service 的 IP:端口的流量会被代理到合适的后端,而客户端无需了解 Kubernetes、Services 或 Pods 的任何信息。

如果您想确保来自特定客户端的连接每次都传递到同一个 Pod,您可以为 Service 设置 .spec.sessionAffinityClientIP,以根据客户端的 IP 地址选择会话亲和性(默认为 None)。

会话粘性超时

您还可以通过为 Service 适当地设置 .spec.sessionAffinityConfig.clientIP.timeoutSeconds 来设置最大会话粘性时间。(默认值为 10800,即 3 小时)。

为 Service 分配 IP 地址

与实际路由到固定目标的 Pod IP 地址不同,Service IP 实际上并非由单个主机响应。相反,kube-proxy 使用数据包处理逻辑(例如 Linux iptables)来定义虚拟 IP 地址,这些地址会根据需要透明地重定向。

当客户端连接到 VIP 时,它们的流量会自动传输到合适的端点。Service 的环境变量和 DNS 实际上是根据 Service 的虚拟 IP 地址(和端口)填充的。

避免冲突

Kubernetes 的主要理念之一是,您不应该因为自己的过失而面临导致操作失败的情况。对于 Service 资源的设计,这意味着如果您的选择可能与他人的选择冲突,则不让您选择自己的 IP 地址。这是一个隔离失败。

为了允许您为您的 Service 选择 IP 地址,我们必须确保没有两个 Service 会冲突。Kubernetes 通过从为 API Server 配置的 service-cluster-ip-range CIDR 范围内为每个 Service 分配其自己的 IP 地址来实现这一点。

IP 地址分配跟踪

为了确保每个 Service 接收唯一的 IP 地址,内部分配器在创建每个 Service 之前,以原子方式更新 etcd 中的全局分配映射。映射对象必须存在于注册表中,Service 才能获取 IP 地址分配,否则创建将失败,并显示一条消息,指示无法分配 IP 地址。

在控制平面中,后台控制器负责创建该映射(需要支持从使用内存锁定的旧版本 Kubernetes 迁移)。Kubernetes 还使用控制器来检查无效分配(例如:由于管理员干预)并清理不再被任何 Service 使用的已分配 IP 地址。

使用 Kubernetes API 进行 IP 地址分配跟踪

功能状态: Kubernetes v1.31 [beta] (默认禁用: false)

如果您启用 MultiCIDRServiceAllocator 功能门networking.k8s.io/v1alpha1 API 组,控制平面将使用修订后的实现替换现有的 etcd 分配器,该实现使用 IPAddress 和 ServiceCIDR 对象而不是内部全局分配映射。然后,与 Service 关联的每个集群 IP 地址都引用一个 IPAddress 对象。

启用功能门还会用一个处理 IPAddress 对象并支持从旧分配器模型迁移的替代方案替换后台控制器。Kubernetes 1.32 不支持从 IPAddress 对象迁移到内部分配映射。

修订后的分配器的主要优点之一是,它消除了可用于 Service 集群 IP 地址的 IP 地址范围的大小限制。启用 MultiCIDRServiceAllocator 后,IPv4 没有限制,而对于 IPv6,您可以使用 /64 或更小的 IP 地址网络掩码(而不是使用传统实现时的 /108)。

通过 API 提供 IP 地址分配意味着作为集群管理员,您可以允许用户检查分配给其 Service 的 IP 地址。Kubernetes 扩展,例如 Gateway API,可以使用 IPAddress API 来扩展 Kubernetes 的固有网络功能。

这是一个用户查询 IP 地址的简短示例

kubectl get services
NAME         TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   2001:db8:1:2::1   <none>        443/TCP   3d1h
kubectl get ipaddresses
NAME              PARENTREF
2001:db8:1:2::1   services/default/kubernetes
2001:db8:1:2::a   services/kube-system/kube-dns

Kubernetes 还允许用户使用 ServiceCIDR 对象动态定义 Service 的可用 IP 范围。在引导期间,会从 kube-apiserver 的 --service-cluster-ip-range 命令行参数的值创建一个名为 kubernetes 的默认 ServiceCIDR 对象。

kubectl get servicecidrs
NAME         CIDRS         AGE
kubernetes   10.96.0.0/28  17m

用户可以创建或删除新的 ServiceCIDR 对象来管理 Service 的可用 IP 范围

cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1beta1
kind: ServiceCIDR
metadata:
  name: newservicecidr
spec:
  cidrs:
  - 10.96.0.0/24
EOF
servicecidr.networking.k8s.io/newcidr1 created
kubectl get servicecidrs
NAME             CIDRS         AGE
kubernetes       10.96.0.0/28  17m
newservicecidr   10.96.0.0/24  7m

Service 虚拟 IP 地址的 IP 地址范围

功能状态: Kubernetes v1.26 [稳定版]

Kubernetes 根据配置的 service-cluster-ip-range 的大小,使用以下公式 min(max(16, cidrSize / 16), 256)ClusterIP 范围分为两个波段。该公式可以概括为*永远不小于 16 或大于 256,并且它们之间具有渐变步进函数*。

Kubernetes 更倾向于通过从上波段中选择来为 Service 分配动态 IP 地址,这意味着如果您想为 type: ClusterIP Service 分配特定的 IP 地址,则应从**下**波段手动分配 IP 地址。这种方法降低了分配冲突的风险。

流量策略

您可以设置 .spec.internalTrafficPolicy.spec.externalTrafficPolicy 字段来控制 Kubernetes 如何将流量路由到健康的(“就绪”)后端。

内部流量策略

功能状态: Kubernetes v1.26 [稳定版]

您可以设置 .spec.internalTrafficPolicy 字段来控制如何路由来自内部来源的流量。有效值为 ClusterLocal。将该字段设置为 Cluster 以将内部流量路由到所有就绪的端点,并设置为 Local 以仅路由到就绪的节点本地端点。如果流量策略为 Local 且没有节点本地端点,则 kube-proxy 会丢弃流量。

外部流量策略

您可以设置 .spec.externalTrafficPolicy 字段来控制如何路由来自外部来源的流量。有效值为 ClusterLocal。将该字段设置为 Cluster 以将外部流量路由到所有就绪的端点,并设置为 Local 以仅路由到就绪的节点本地端点。如果流量策略为 Local 且没有节点本地端点,则 kube-proxy 不会转发任何相关 Service 的流量。

如果指定了 Cluster,则所有节点都有资格作为负载均衡目标*只要*该节点未被删除且 kube-proxy 是健康的。在此模式下:负载均衡器运行状况检查被配置为以服务代理的就绪端口和路径为目标。对于 kube-proxy,这可以评估为:${NODE_IP}:10256/healthz。如果 kube-proxy 返回 HTTP 代码 200 或 503。如果满足以下条件,则 kube-proxy 的负载均衡器运行状况检查端点返回 200:

  1. kube-proxy 是健康的,这意味着
    • 它能够继续执行网络编程并且在执行此操作时不会超时(超时定义为:**2 × iptables.syncPeriod**);并且
  2. 该节点未被删除(未为该节点设置删除时间戳)。

kube-proxy 在节点正在删除时返回 503 并将节点标记为不符合条件的原因是,kube-proxy 支持终止节点的连接耗尽。当节点*正在*被删除/ *已被*删除时,从 Kubernetes 管理的负载均衡器的角度来看,会发生一些重要的事情。

正在删除时

  • kube-proxy 将开始使其就绪探测失败,并基本上将节点标记为不符合负载均衡器流量的条件。负载均衡器运行状况检查失败会导致支持连接耗尽的负载均衡器允许现有连接终止,并阻止建立新连接。

删除后

  • Kubernetes 云控制器管理器中的服务控制器会从引用的合格目标集中删除该节点。从负载均衡器的后端目标集中删除任何实例会立即终止所有连接。这也是 kube-proxy 首先在节点删除时使运行状况检查失败的原因。

对于 Kubernetes 供应商来说,需要注意的是,如果任何供应商将 kube-proxy 就绪探测配置为活跃度探测:那么当节点正在删除时,kube-proxy 将开始持续重启,直到它被完全删除。kube-proxy 公开了一个 /livez 路径,与 /healthz 路径相反,该路径**不**考虑节点的删除状态,而只考虑其网络编程的进度。因此,/livez 是任何希望为 kube-proxy 定义 livenessProbe 的人的推荐路径。

部署 kube-proxy 的用户可以通过评估指标来检查就绪/活跃状态:proxy_livez_total / proxy_healthz_total。这两个指标都发布两个序列,一个带有 200 标签,另一个带有 503 标签。

对于 Local Service:如果满足以下条件,kube-proxy 将返回 200:

  1. kube-proxy 是健康的/就绪的,并且
  2. 在相关节点上具有本地端点。

节点删除**不**会影响 kube-proxy 的返回代码,这涉及到负载均衡器运行状况检查。原因是:如果所有端点同时在所述节点上运行,则删除节点可能会导致入口中断。

Kubernetes 项目建议云提供商集成代码配置以服务代理的 healthz 端口为目标的负载均衡器运行状况检查。如果您正在使用或实现自己的虚拟 IP 实现,人们可以使用它来代替 kube-proxy,则应设置一个类似的运行状况检查端口,其逻辑与 kube-proxy 实现匹配。

到终止端点的流量

功能状态: Kubernetes v1.28 [稳定版]

如果在 kube-proxy 中启用了 ProxyTerminatingEndpoints 功能门并且流量策略为 Local,则该节点的 kube-proxy 会使用更复杂的算法来选择 Service 的端点。启用该功能后,kube-proxy 会检查该节点是否具有本地端点以及是否所有本地端点都被标记为正在终止。如果有本地端点并且**所有**端点都在终止,那么 kube-proxy 会将流量转发到这些终止端点。否则,kube-proxy 将始终优先将流量转发到未终止的端点。

存在这种用于终止端点的转发行为,以便在使用 externalTrafficPolicy: Local 时,NodePortLoadBalancer Service 可以正常耗尽连接。

当部署进行滚动更新时,支持负载均衡器的节点可能会从该部署的 N 个副本转换为 0 个副本。在某些情况下,外部负载均衡器可以在两次运行状况检查探测之间将流量发送到具有 0 个副本的节点。将流量路由到正在终止的端点可确保正在缩减 Pod 的节点可以正常接收流量并将其耗尽到这些正在终止的 Pod。当 Pod 完成终止时,外部负载均衡器应该已经看到节点的运行状况检查失败,并已将该节点从后端池中完全删除。

流量分配

特性状态: Kubernetes v1.31 [beta] (默认启用: true)

Kubernetes Service 中的 spec.trafficDistribution 字段允许你表达如何将流量路由到 Service 端点的偏好。诸如 kube-proxy 之类的实现会将 spec.trafficDistribution 字段作为指导。给定偏好相关的行为在不同的实现之间可能略有不同。

PreferClose 与 kube-proxy
对于 kube-proxy,这意味着优先将流量发送到与客户端位于同一区域内的端点。EndpointSlice 控制器会使用 hints 更新 EndpointSlices 以传递此偏好,kube-proxy 随后会使用此偏好进行路由决策。如果客户端所在的区域没有任何可用的端点,则该客户端的流量将在集群范围内路由。

在没有为 trafficDistribution 设置任何值的情况下,kube-proxy 的默认路由策略是将流量分发到集群中的任何端点。

service.kubernetes.io/topology-mode: Auto 的比较

带有 PreferClosetrafficDistribution 字段和 service.kubernetes.io/topology-mode: Auto 注解都旨在优先处理同一区域的流量。然而,它们的方法存在关键差异

  • service.kubernetes.io/topology-mode: Auto:尝试基于可分配的 CPU 资源在各个区域之间按比例分配流量。此启发式算法包括安全措施(例如,针对少量端点的回退行为),并且可能导致该功能在某些情况下因负载均衡原因而被禁用。这种方法牺牲了一定的可预测性,以换取潜在的负载均衡。

  • trafficDistribution: PreferClose:此方法的目标是稍微简单且更具可预测性:“如果区域中有端点,它们将接收该区域的所有流量;如果区域中没有端点,流量将分发到其他区域”。虽然这种方法可能提供更高的可预测性,但这确实意味着你需要控制管理潜在的过载

如果 service.kubernetes.io/topology-mode 注解设置为 Auto,它将优先于 trafficDistribution。(该注解将来可能会被弃用,而倾向于使用 trafficDistribution 字段)。

与流量策略的交互

trafficDistribution 字段相比,流量策略字段(externalTrafficPolicyinternalTrafficPolicy)旨在提供更严格的流量局部性要求。以下是 trafficDistribution 与它们的交互方式

  • 流量策略的优先级:对于给定的 Service,如果流量策略(externalTrafficPolicyinternalTrafficPolicy)设置为 Local,则它将优先于相应流量类型(分别为外部或内部)的 trafficDistribution: PreferClose

  • trafficDistribution 的影响:对于给定的 Service,如果流量策略(externalTrafficPolicyinternalTrafficPolicy)设置为 Cluster(默认值),或者如果未设置这些字段,则 trafficDistribution: PreferClose 将指导相应流量类型(分别为外部或内部)的路由行为。这意味着将尝试将流量路由到与客户端位于同一区域的端点。

使用流量分发控制的注意事项

  • 端点过载的可能性增加: PreferClose 启发式算法将尝试将流量路由到最近的健康端点,而不是将流量均匀地分布在所有端点上。如果某个区域内的端点数量不足,则它们可能会过载。如果传入流量未在各个区域之间按比例分配,则尤其如此。为了缓解这种情况,请考虑以下策略

    • Pod 拓扑分布约束:使用 Pod 拓扑分布约束,使你的 Pod 在各个区域之间更均匀地分布。

    • 特定于区域的部署:如果你预计会看到倾斜的流量模式,请为每个区域创建一个单独的 Deployment。此方法允许单独的工作负载独立扩展。生态系统中还有来自 Kubernetes 项目之外的工作负载管理插件,可以在这里提供帮助。

  • 特定于实现的的行为: 每个数据平面实现可能对该字段的处理方式略有不同。如果你使用的是 kube-proxy 之外的实现,请参考该实现特定的文档,以了解如何处理此字段。

下一步

要了解有关 Service 的更多信息,请阅读 使用 Service 连接应用程序

你还可以

上次修改时间为 2024 年 10 月 7 日下午 2:00 PST:补充对 IPVS 的描述 (9dbbfe258c)