虚拟 IP 和服务代理

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

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

使用 iptables 模式的 Service 虚拟 IP 机制

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

使用代理来实现 Service 有几个原因

  • DNS 实现长期以来都未能遵守记录 TTL,并且会在名称查找应过期后继续缓存结果。
  • 有些应用程序只执行一次 DNS 查找,并无限期地缓存结果。
  • 即使应用程序和库进行了正确的重新解析,DNS 记录的低 TTL 或零 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。

示例

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

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

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

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

优化 iptables 模式的性能

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

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

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

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

默认值 1s 在大多数集群中应该效果很好,但在非常大的集群中,可能需要将其设置为更大的值。特别是,如果 kube-proxysync_proxy_rules_duration_seconds 指标显示平均时间远大于 1 秒,那么提高 minSyncPeriod 可能会使更新更有效。

更新旧版 minSyncPeriod 配置

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

如果您之前覆盖了 minSyncPeriod,则应尝试删除该覆盖,让 kube-proxy 使用默认值(1s)或至少是升级前所用值的一个较小值。

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

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

IPVS 为将流量负载均衡到后端 Pod 提供了更多选项,这些选项是

  • rr (Round Robin):流量在后端服务器之间平均分配。

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

  • lc (Least Connection):将更多流量分配给连接数较少的服务器。

  • wlc (Weighted Least Connection):将更多流量路由到连接数相对其权重较少的服务器,即连接数除以权重。

  • lblc (Locality based Least Connection):发往同一 IP 地址的流量会被发送到同一后端服务器,前提是该服务器未过载且可用;否则,流量会被发送到连接数较少的服务器,并保留供将来分配。

  • lblcr (Locality Based Least Connection with Replication):发往同一 IP 地址的流量会被发送到连接数最少的服务器。如果所有后端服务器都过载,它会选择一个连接数较少的服务器并将其添加到目标集。如果目标集在指定时间内未更改,则会从集中移除负载最高的服务器,以避免高度复制。

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

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

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

  • nq (Never Queue):如果存在空闲服务器,则将流量发送到该服务器,而不是等待快速的服务器;如果所有服务器都忙,则该算法将回退到 sed 行为。

  • mh (Maglev Hashing):根据Google 的 Maglev hashing 算法分配入站作业。此调度程序有两个标志: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.33 [stable] (默认启用:true)

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

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

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

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

iptables 模式迁移到 nftables

希望从默认的 iptables 模式切换到 nftables 模式的用户应注意,在 nftables 模式下,某些功能的工作方式略有不同。

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

  • type: NodePort **Service on 127.0.0.1**:在 iptables 模式下,如果 --nodeport-addresses 范围包含 127.0.0.1(并且未传递选项 --iptables-localhost-nodeports false),则 type: NodePort 的 Service 甚至在“localhost”(127.0.0.1)上也可访问。在 nftables 模式(和 ipvs 模式)下,这将不起作用。如果您不确定是否依赖此功能,可以检查 kube-proxyiptables_localhost_nodeports_accepted_packets_total 指标;如果它非零,则意味着某个客户端已通过 localhost/loopback 连接到 type: NodePort Service。

  • NodePort 与防火墙的交互kube-proxyiptables 模式试图与过于激进的防火墙兼容;对于每个 type: NodePort Service,它会添加规则以接受该端口上的入站流量,以防流量被防火墙阻止。这种方法与基于 nftables 的防火墙不兼容,因此 kube-proxynftables 模式在此不做任何处理;如果您有本地防火墙,必须确保它已正确配置以允许 Kubernetes 流量通过(例如,允许 NodePort 范围上的入站流量)。

  • Conntrack bug 解决方法:Linux 内核 6.1 之前的版本存在一个 bug,可能导致到 Service IP 的长期 TCP 连接以“Connection reset by peer”错误关闭。kube-proxyiptables 模式为此 bug 安装了解决方法,但后来发现此解决方法会在某些集群中导致其他问题。nftables 模式默认不安装任何解决方法,但您可以检查 kube-proxyiptables_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.34 [稳定] (默认启用:true)

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

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

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

会话亲和性

在这些代理模型中,发往 Service IP:Port 的流量被代理到适当的后端,而客户端对此一无所知 Kubernetes 或 Services 或 Pods。

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

会话粘性超时

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

分配 IP 地址给 Service

与实际路由到固定目标的 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.33 [stable] (默认启用:true)

控制平面用一个修改后的实现替换现有的 etcd 分配器,该实现使用 IPAddress 和 ServiceCIDR 对象而不是内部全局分配映射。与 Service 关联的每个集群 IP 地址随后引用一个 IPAddress 对象。

启用功能门还会用一个替代项替换后台控制器,该替代项处理 IPAddress 对象并支持从旧分配器模型迁移。Kubernetes 1.34 不支持从 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 范围。在启动期间,一个名为 kubernetes 的默认 ServiceCIDR 对象会从 kube-apiserver 的 --service-cluster-ip-range 命令行参数值创建。

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

Kubernetes 集群的发行版或管理员可能希望控制添加到集群的新 Service CIDR 不会与其他属于特定 IP 范围的网络发生重叠,或者只是简单地保留每个集群只有一个 ServiceCIDR 的现有行为。实现此目的的验证 Admission 策略示例是

---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "servicecidrs-default"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:   ["networking.k8s.io"]
      apiVersions: ["v1","v1beta1"]
      operations:  ["CREATE", "UPDATE"]
      resources:   ["servicecidrs"]
  matchConditions:
  - name: 'exclude-default-servicecidr'
    expression: "object.metadata.name != 'kubernetes'"
  variables:
  - name: allowed
    expression: "['10.96.0.0/16','2001:db8::/64']"
  validations:
  - expression: "object.spec.cidrs.all(i , variables.allowed.exists(j , cidr(j).containsCIDR(i)))"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "servicecidrs-binding"
spec:
  policyName: "servicecidrs-default"
  validationActions: [Deny,Audit]
---

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

功能状态: Kubernetes v1.26 [stable]

Kubernetes 使用以下公式 min(max(16, cidrSize / 16), 256)ClusterIP 范围划分为两个段,基于配置的 service-cluster-ip-range 的大小。该公式意味着结果永不小于 16 或大于 256,它们之间有一个渐进的步进函数

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

流量策略

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

内部流量策略

功能状态: Kubernetes v1.26 [stable]

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

外部流量策略

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

如果指定了 Cluster,则所有节点都是合格的负载均衡目标,*前提是*节点未被删除且 kube-proxy 健康。在此模式下:负载均衡器健康检查配置为将目标指向 Service 代理的就绪端口和路径。对于 kube-proxy,这评估为:${NODE_IP}:10256/healthzkube-proxy 将返回 HTTP 代码 200 或 503。kube-proxy 的负载均衡器健康检查端点返回 200 如果

  1. kube-proxy 健康,这意味着

    它能够进行网络编程并且在此过程中没有超时(超时定义为:2 × iptables.syncPeriod);并且

  2. 节点未被删除(Node 没有设置删除时间戳)。

kube-proxy 返回 503 并将节点标记为不合格,因为它支持终止节点的连接排水。从 Kubernetes 管理的负载均衡器的角度来看,当节点正在/删除时,会发生一些重要的事情。

正在删除时

  • kube-proxy 将开始使其就绪探测失败,并有效地将节点标记为不合格接收负载均衡器流量。负载均衡器健康检查失败会导致支持连接排水的负载均衡器允许现有连接终止,并阻止新连接建立。

已删除时

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

对于 Kubernetes 供应商来说,重要的是要注意,如果任何供应商将 kube-proxy 的就绪探测配置为活跃探测:那么 kube-proxy 将在节点删除时开始不断重启,直到节点完全删除。kube-proxy 暴露了 /livez 路径,与 /healthz 路径不同,它考虑节点的删除状态,只考虑其网络编程的进度。因此,对于任何想要为 kube-proxy 定义活跃探测的人来说,/livez 是推荐的路径。

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

对于 Local Service:kube-proxy 将返回 200 如果

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

节点删除影响 kube-proxy 关于负载均衡器健康检查的返回代码。原因如下:删除节点可能会导致入站中断,如果所有端点同时在该节点上运行。

Kubernetes 项目建议云提供商集成代码将负载均衡器健康检查配置为指向 Service 代理的 healthz 端口。如果您正在使用或实现自己的虚拟 IP 实现来替代 kube-proxy,您应该设置一个类似的健康检查端口,其逻辑与 kube-proxy 实现匹配。

发往终止端点的流量

特性状态: Kubernetes v1.28 [stable]

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

这种终止端点的转发行为是为了允许 NodePortLoadBalancer Service 在使用 externalTrafficPolicy: Local 时优雅地排水连接。

随着部署进行滚动更新,负载均衡器后面的节点可能会从该部署的 N 个副本转换到 0 个副本。在某些情况下,外部负载均衡器可以在健康检查探测之间将流量发送到具有 0 个副本的节点。路由到终止端点可确保正在缩减 Pod 的节点能够优雅地接收并排水到这些终止 Pod 的流量。当 Pod 完成终止时,外部负载均衡器应已看到节点的健康检查失败,并已将节点完全从后端池中移除。

流量分布

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

Kubernetes Service 中的 spec.trafficDistribution 字段允许您表达将流量路由到 Service 端点的偏好。

PreferClose
这会优先将流量发送到与客户端同一区域的端点。EndpointSlice 控制器会使用 hints 更新 EndpointSlices 来传达此偏好,kube-proxy 然后会使用这些信息进行路由决策。如果客户端所在区域没有可用的端点,流量将为该客户端进行集群范围路由。
特性状态: Kubernetes v1.34 [beta] (默认启用:true)

在 Kubernetes 1.34 中,有两个附加值可用(除非 PreferSameTrafficDistribution 功能门被禁用)

PreferSameZone
这与 PreferClose 含义相同,但更明确。(最初的意图是 PreferClose 后来可能包含“偏好同一区域”之外的功能,但这已不再计划。将来,PreferSameZone 将成为此功能的推荐值,而 PreferClose 将被视为其已弃用的别名。)
PreferSameNode
这会优先将流量发送到与客户端同一节点的端点。与 PreferClose/PreferSameZone 一样,EndpointSlice 控制器会使用 hints 更新 EndpointSlices,表明某个切片应用于特定节点。如果客户端节点没有可用的端点,则 Service 代理将回退到“同一区域”行为,如果没有同一区域的端点,则回退到集群范围。

trafficDistribution 没有值的情况下,默认策略是将流量均匀分布到集群中的所有端点。

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

trafficDistribution 字段(带 PreferClose/PreferSameZone)以及使用 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

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

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

使用 trafficDistribution 的 Service 将尝试将流量路由到(健康的)拓扑内的端点,即使这意味着某些端点接收的流量远多于其他端点。如果您没有足够数量的端点与客户端在同一拓扑(“同一区域”、“同一节点”等)中,则端点可能会过载。如果入站流量未在拓扑中成比例分布,这种情况尤其可能发生。为缓解此问题,请考虑以下策略:

  • Pod 拓扑扩散约束:使用 Pod 拓扑扩散约束将 Pod 均匀分布到区域或节点。

  • 区域特定部署:如果您正在使用“同一区域”流量分发,但预计不同区域会看到不同的流量模式,则可以为每个区域创建单独的 Deployment。这种方法允许独立的应用程序独立扩展。此外,在 Kubernetes 项目本身之外的生态系统中,也有可用的工作负载管理插件可以帮助解决此问题。

下一步

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

您还可以

最后修改时间 2025 年 7 月 3 日上午 9:29 PST:WinDSR 到稳定更新 (406db73486)