虚拟 IP 和服务代理
kube-proxy
kube-proxytypeExternalName
clusterIPport

iptables 模式下 Service 的虚拟 IP 机制

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

使用代理转发方式实现 Service 的原因有以下几个

  • DNS 的实现不遵守记录的 TTL 约定的历史由来已久,在记录过期后可能仍有结果缓存。
  • 有些应用只做一次 DNS 查询,然后永久缓存结果。
  • 即使应用程序和库进行了适当的重新解析,TTL 取值较低或为零的 DNS 记录可能会给 DNS 带来很大的压力, 从而变得难以管理。
kube-proxykube-proxycleanup

本文中的一些细节会引用这样一个例子 运行了 3 个 Pod 副本的无状态图像处理后端工作负载。 这些副本是可互换的;前端不需要关心它们调用了哪个后端副本。 即使组成这一组后端程序的 Pod 实际上可能会发生变化, 前端客户端不应该也没必要知道,而且也不需要跟踪这一组后端的状态。

代理模式

kube-proxy 会根据不同配置以不同的模式启动。

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

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

iptables

此代理模式仅适用于 Linux 节点。

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

示例

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

当节点上的 kube-proxy 观察到新 Service 时,它会添加一系列 iptables 规则, 这些规则从虚拟 IP 地址重定向到更多 iptables 规则,每个 Service 都定义了这些规则。 每个 Service 规则链接到每个后端端点的更多规则, 并且每个端点规则将流量重定向(使用目标 NAT)到后端。

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

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

优化 iptables 模式性能

kube-proxy --config iptables
minSyncPeriod
minSyncPeriod0sminSyncPeriod: 0sminSyncPeriod
minSyncPeriodminSyncPeriod
1ssync_proxy_rules_duration_secondsminSyncPeriod
minSyncPeriod
minSyncPeriod
minSyncPeriod1s

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

syncPeriod
syncPeriodsyncPeriod
syncPeriod1h

IPVS 代理模式

此代理模式仅适用于 Linux 节点。

ipvs

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

IPVS 为将流量均衡到后端 Pod 提供了更多选择

rrwrrlc
wlclblc
lblcr
shdh
sed(C + 1) / UCUnqsed

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

虚拟 IP 和服务代理

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

nftables

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

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

nftablesiptables
nftables
iptablesnftables
iptablesnftablesnftables
127.0.0.1iptables--nodeport-addresses127.0.0.1--iptables-localhost-nodeports false127.0.0.1nftablesipvsiptables_localhost_nodeports_accepted_packets_total127.0.0.1
iptablesnftables
iptablesnftablesiptables_ct_state_invalid_dropped_packets_total--conntrack-tcp-be-liberalnftables
kernelspace

此代理模式仅适用于 Windows 节点。

nftablesiptables
kernelspace
kernelspace

作为基本操作的替代方案,托管服务后端 Pod 的节点可以直接应用数据包重写, 而不用将此工作交给运行客户端 Pod 的节点来执行。这称为 Direct Server Return(DSR)

--enable-dsrWinDSR

即使两个 Pod 在同一节点上运行,DSR 也可优化 Pod 的返回流量。

会话亲和性

在这些代理模型中,绑定到 Service IP:Port 的流量被代理到合适的后端, 客户端不需要知道任何关于 Kubernetes、Service 或 Pod 的信息。

.spec.sessionAffinityClientIPNone

会话粘性超时

.spec.sessionAffinityConfig.clientIP.timeoutSeconds

将 IP 地址分配给 Service

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

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

避免冲突

Kubernetes 的主要哲学之一是, 你不应需要在完全不是你的问题的情况下面对可能导致你的操作失败的情形。 对于 Service 资源的设计,也就是如果你选择的端口号可能与其他人的选择冲突, 就不应该让你自己选择 IP 地址。这是一种失败隔离。

service-cluster-ip-range

IP 地址分配追踪

为了确保每个 Service 都获得唯一的 IP 地址,内部分配器在创建每个 Service 之前更新 etcd 中的全局分配映射,这种更新操作具有原子性。 映射对象必须存在于数据库中,这样 Service 才能获得 IP 地址分配, 否则创建将失败,并显示无法分配 IP 地址。

在控制平面中,后台控制器负责创建该映射(从使用内存锁定的旧版本的 Kubernetes 迁移时需要这一映射)。 Kubernetes 还使用控制器来检查无效的分配(例如,因管理员干预而导致无效分配) 以及清理已分配但没有 Service 使用的 IP 地址。

使用 Kubernetes API 跟踪IP 地址分配

MultiCIDRServiceAllocatornetworking.k8s.io/v1alpha1

启用该特性门控还会用替代实现将后台控制器替换,来处理 IPAddress 对象并支持从旧的分配器模型迁移。 Kubernetes 1.32 不支持从 IPAddress 对象迁移到内部分配映射。

MultiCIDRServiceAllocator

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

以下是用户查询 IP 地址的简短示例

NAME         TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   2001:db8:1:2::1   <none>        443/TCP   3d1h
NAME              PARENTREF
2001:db8:1:2::1   services/default/kubernetes
2001:db8:1:2::a   services/kube-system/kube-dns
--service-cluster-ip-rangekubernetes
NAME         CIDRS         AGE
kubernetes   10.96.0.0/28  17m

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

servicecidr.networking.k8s.io/newcidr1 created
NAME             CIDRS         AGE
kubernetes       10.96.0.0/28  17m
newservicecidr   10.96.0.0/24  7m

Service 虚拟 IP 地址的地址段

service-cluster-ip-rangemin(max(16, cidrSize / 16), 256)ClusterIP
type: ClusterIP

流量策略

.spec.internalTrafficPolicy.spec.externalTrafficPolicy

内部流量策略

.spec.internalTrafficPolicyClusterLocalClusterLocalLocal

外部流量策略

.spec.externalTrafficPolicyClusterLocalClusterLocalLocal
Cluster${NODE_IP}:10256/healthz
iptables.syncPeriod

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

当节点被删除时

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

当节点被删除后

  • Kubernetes 云控制器管理器中的服务控制器会将节点从所引用的候选目标集中移除。 从负载均衡器的后端目标集中移除任何实例会立即终止所有连接。 这也是 kube-proxy 在节点删除过程中首先使健康检查失败的原因。
/livez/healthz/livez/livez
proxy_livez_totalproxy_healthz_total
Local
  1. kube-proxy 是健康/就绪的,并且
  2. 在相关节点上有一个本地端点。

对于负载均衡器健康检查而言,节点删除不会对 kube-proxy 的返回代码产生影响。原因是如果所有端点同时在上述节点上运行,则删除节点最终可能会导致入站流量中断。

Kubernetes 项目建议云提供商集成代码配置负载均衡器健康检查,以针对服务代理的 healthz 端口。 如果你正在使用或实现自己的虚拟 IP 实现,供人们使用它替代 kube-proxy,你应该设置一个类似的健康检查端口, 其逻辑应与 kube-proxy 实现相匹配。

流向正终止的端点的流量

ProxyTerminatingEndpointsLocal
NodePortLoadBalancerexternalTrafficPolicy: Local

当一个 Deployment 被滚动更新时,处于负载均衡器后端的节点可能会将该 Deployment 的 N 个副本缩减到 0 个副本。在某些情况下,外部负载均衡器可能在两次执行健康检查探针之间将流量发送到具有 0 个副本的节点。 将流量路由到处于终止过程中的端点可确保正在缩减 Pod 的节点能够正常接收流量, 并逐渐降低指向那些处于终止过程中的 Pod 的流量。 到 Pod 完成终止时,外部负载均衡器应该已经发现节点的健康检查失败并从后端池中完全移除该节点。

流量分发

spec.trafficDistributionspec.trafficDistribution
PreferClosehints
trafficDistribution
service.kubernetes.io/topology-mode: Auto
trafficDistributionPreferCloseservice.kubernetes.io/topology-mode: Auto
service.kubernetes.io/topology-modeAutotrafficDistributiontrafficDistribution

与流量策略的交互

trafficDistributionexternalTrafficPolicyinternalTrafficPolicytrafficDistribution
externalTrafficPolicyinternalTrafficPolicyLocaltrafficDistribution: PreferClose
trafficDistributionexternalTrafficPolicyinternalTrafficPolicyClustertrafficDistribution: PreferClose

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

PreferClose
  • 特定于具体实现的行为 各个数据平面实现处理此字段的方式可能会稍有不同。 如果你使用的是 kube-proxy 以外的实现,请参阅该实现的特定文档以了解该实现是如何处理此字段的。

接下来

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

也可以

阅读剩余 0%
本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。