虚拟 IP 和服务代理

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

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

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

有一个经常被问到的问题是:为什么 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(或其 EndpointSlice)发生变化时,kube-proxy 更新内核中的规则可能需要很长时间。你可以通过 kube-proxy 配置文件iptables 部分的选项(通过 kube-proxy --config <path> 指定)来调整 kube-proxy 的同步行为。

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

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

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

默认值 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),或者至少在升级前使用比你之前更小的值。

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

syncPeriod

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

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

IPVS 代理模式

特性状态: Kubernetes v1.35 [已弃用]

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

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

IPVS 代理模式基于类似于 iptables 模式的 netfilter 钩子函数,但使用哈希表作为底层数据结构,并在内核空间中工作。

说明

ipvs 代理模式是一个实验,旨在提供比 iptables 模式具有更好规则同步性能和更高网络流量吞吐量的 Linux kube-proxy 后端。虽然它成功实现了这些目标,但内核 IPVS API 被证明与 Kubernetes Services API 不匹配,并且 ipvs 后端从未能够正确实现所有 Kubernetes Service 功能的边缘情况。

nftables 代理模式(如下所述)本质上是 iptablesipvs 模式的替代品,性能比两者都好,并被推荐作为 ipvs 的替代品。如果你部署的 Linux 系统太旧而无法运行 nftables 代理模式,你也应该考虑尝试 iptables 模式而不是 ipvs,因为自 ipvs 模式首次引入以来,iptables 模式的性能已大大提高。

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 - Maglev 哈希): 基于 Google 的 Maglev 哈希算法分配传入的任务。此调度程序有两个标志:mh-fallback,如果所选服务器不可用,则启用回退到不同服务器;mh-port,将源端口号添加到哈希计算中。使用 mh 时,kube-proxy 总是设置 mh-port 标志,而不启用 mh-fallback 标志。在 proxy-mode=ipvs 中,mh 将作为源哈希 (sh) 工作,但带有端口。

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

说明

要以 IPVS 模式运行 kube-proxy,你必须在启动 kube-proxy 之前使节点上的 IPVS 可用。

当 kube-proxy 在 IPVS 代理模式下启动时,它会验证 IPVS 内核模块是否可用。如果未检测到 IPVS 内核模块,kube-proxy 将报错退出。

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

nftables 代理模式

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

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

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

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

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

iptables 模式迁移到 nftables

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

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

  • type: NodePort Service 在 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-proxy 的 iptables_localhost_nodeports_accepted_packets_total 指标;如果它非 0,则意味着某些客户端已通过 localhost/loopback 连接到 type: NodePort Service。

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

  • Conntrack 错误修复:6.1 之前的 Linux 内核存在一个错误,可能导致通往 Service 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 模式的直接服务器返回 (DSR)

特性状态: Kubernetes v1.34 [稳定](默认启用)

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

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

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

会话亲和性

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

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

会话粘性超时时间

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

说明

在 Windows 上,不支持设置 Service 的最大会话粘性时间。

为 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.33 [稳定](默认启用)

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

启用该功能门控还会将后台控制器替换为一个处理 IPAddress 对象并支持从旧分配器模型迁移的替代控制器。Kubernetes 1.36 不支持从 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/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 的现有行为。实现此目的的验证准入策略示例为

---
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 [稳定]

Kubernetes 通过使用以下公式 min(max(16, cidrSize / 16), 256),根据配置的 service-cluster-ip-range 的大小将 ClusterIP 范围划分为两个频段。该公式意味着结果永远不小于 16 或大于 256,且在它们之间具有分级步进函数

Kubernetes 倾向于通过从高频段中进行选择,从而将动态 IP 地址分配给 Service,这意味着如果你想为 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. 节点没有被删除(Node 没有设置删除时间戳)。

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

正在删除时

  • kube-proxy 将开始无法通过其就绪探测,并本质上将节点标记为不适合负载均衡器流量。负载均衡器健康检查失败会导致支持连接耗尽的负载均衡器允许现有连接终止,并阻止建立新连接。

删除后

  • Kubernetes 云控制器管理器中的服务控制器从引用的符合条件的目标集中删除该节点。从负载均衡器的后端目标集中删除任何实例都会立即终止所有连接。这也是为什么在节点删除时 kube-proxy 首先未能通过健康检查的原因。

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

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

对于 Local 服务:如果满足以下条件,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 服务时,能够优雅地耗尽连接。

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

流量分布控制

Kubernetes Service 中的 spec.trafficDistribution 字段允许你表达关于流量应该如何路由到 Service 端点的偏好。

PreferSameZone
这优先将流量发送到与客户端位于同一区域的端点。EndpointSlice 控制器使用 hints 更新 EndpointSlices 以传达此偏好,kube-proxy 随后将其用于路由决策。如果客户端的区域没有任何可用端点,则流量将为该客户端在整个集群范围内路由。
PreferSameNode
这优先将流量发送到与客户端位于同一节点的端点。与 PreferSameZone 一样,EndpointSlice 控制器使用 hints 更新 EndpointSlices,指示应将切片用于特定节点。如果客户端的节点没有任何可用端点,则服务代理将回退到“相同区域”行为,或者如果没有相同区域端点,则在整个集群范围内路由。
PreferClose (已弃用)
这是 PreferSameZone 的一个旧别名,在语义上不太清晰。

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

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

具有 PreferSameZonetrafficDistribution 字段,以及使用 service.kubernetes.io/topology-mode: Auto 注解的旧“拓扑感知路由”功能,都旨在优先处理相同区域的流量。然而,它们的方法有一个关键区别

  • service.kubernetes.io/topology-mode: Auto 尝试根据可分配的 CPU 资源在各区域间按比例分配流量。这种启发式方法包含保护措施(例如针对少量端点的回退行为),牺牲了一些可预测性以换取可能更好的负载均衡。

  • trafficDistribution: PreferSameZone 旨在更简单且可预测:“如果该区域有端点,它们将接收该区域的所有流量;如果该区域没有端点,流量将分配到其他区域”。这种方法提供了更多的可预测性,但这意味着你有责任避免端点过载

如果 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 年 10 月 19 日,下午 8:06 PST:将 IPVS 代理模式标记为在 kubernetes 1.35 中弃用 (6672edc949)