虚拟 IP 和服务代理
Kubernetes 集群中的每个节点都运行一个 kube-proxy(除非你部署了自己替代 kube-proxy
的组件)。
kube-proxy
组件负责为 Service(type
不是 ExternalName
)实现虚拟 IP 机制。每个 kube-proxy 实例都监视 Kubernetes 控制平面,监听 Service 和 EndpointSlice 对象的创建和删除。对于每个 Service,kube-proxy 调用相应的 API(取决于 kube-proxy 模式)来配置节点,以捕获发往 Service 的 clusterIP
和 port
的流量,并将流量重定向到 Service 的一个端点(通常是一个 Pod,但也可能是用户提供的任意 IP 地址)。一个控制循环确保每个节点上的规则与 API 服务器所示的 Service 和 EndpointSlice 状态可靠地同步。
服务的虚拟 IP 机制,使用 iptables 模式
一个时不时出现的问题是,为什么 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。
示例
例如,考虑本页前面描述的图像处理应用程序。当后端 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 地址。
当流量通过 NodePort 或负载均衡器进入时,也执行相同的基本流程,尽管在这些情况下客户端 IP 地址会被更改。
优化 iptables 模式性能
在 iptables 模式下,kube-proxy 为每个 Service 创建一些 iptables 规则,并为每个端点 IP 地址创建一些 iptables 规则。在有数万个 Pod 和 Service 的集群中,这意味着数万条 iptables 规则,当 Service(或其 EndpointSlice)发生变化时,kube-proxy 可能需要很长时间才能更新内核中的规则。你可以通过 kube-proxy 配置文件(通过 kube-proxy --config <path>
指定)的 iptables
部分中的选项调整 kube-proxy 的同步行为
...
iptables:
minSyncPeriod: 1s
syncPeriod: 30s
...
minSyncPeriod
minSyncPeriod
参数设置尝试将 iptables 规则与内核重新同步的最小持续时间。如果设置为 0s
,那么 kube-proxy 将在每次 Service 或 EndpointSlice 发生变化时立即同步规则。这在非常小的集群中工作良好,但在短时间内有很多变化时会导致大量冗余工作。例如,如果你的 Service 由一个包含 100 个 Pod 的 Deployment 提供支持,并且你删除了该 Deployment,那么当 minSyncPeriod: 0s
时,kube-proxy 将逐个从 iptables 规则中移除 Service 的端点,总共进行 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.33 版本,请查阅你实际运行的版本对应的行为和相关建议。
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 哈希算法分配传入作业,此调度器有两个标志: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 会以错误退出。
服务的虚拟 IP 地址机制,使用 IPVS 模式
nftables
代理模式
Kubernetes v1.33 [稳定]
(默认启用:true)此代理模式仅在 Linux 节点上可用,并且需要内核版本 5.13 或更高。
在此模式下,kube-proxy 使用内核 netfilter 子系统的 nftables API 配置报文转发规则。对于每个端点,它会安装 nftables 规则,默认情况下会随机选择一个后端 Pod。
nftables API 是 iptables API 的继承者,旨在提供比 iptables 更好的性能和可伸缩性。nftables
代理模式能够比 iptables
模式更快、更有效地处理服务端点变更,并且还能在内核中更有效地处理报文(但这仅在拥有数万个服务的集群中才会显著)。
截至 Kubernetes 1.33,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
选项),则 NodePort 服务即使在“localhost”(127.0.0.1
)上也能访问。在nftables
模式(和ipvs
模式)下,这将不起作用。如果你不确定是否依赖此功能,可以检查 kube-proxy 的iptables_localhost_nodeports_accepted_packets_total
指标;如果它非零,则表示有客户端通过127.0.0.1
连接到 NodePort 服务。NodePort 与防火墙的交互:kube-proxy 的
iptables
模式尝试与过于激进的防火墙兼容;对于每个 NodePort 服务,它会添加规则以接受该端口上的入站流量,以防流量原本会被防火墙阻止。这种方法对基于 nftables 的防火墙不起作用,因此 kube-proxy 的nftables
模式在此不做任何处理;如果你有本地防火墙,必须确保其配置正确以允许 Kubernetes 流量通过(例如,通过允许整个 NodePort 范围的入站流量)。Conntrack bug 修复:Linux 内核 6.1 之前版本存在一个 bug,可能导致到 Service IP 的长连接 TCP 连接以“对端连接重置”错误关闭。kube-proxy 的
iptables
模式安装了一个针对此 bug 的修复,但后来发现此修复在某些集群中导致了其他问题。nftables
模式默认不安装任何修复,但你可以检查 kube-proxy 的iptables_ct_state_invalid_dropped_packets_total
指标,查看你的集群是否依赖该修复,如果是,你可以在nftables
模式下运行 kube-proxy 并使用--conntrack-tcp-be-liberal
选项来解决此问题。
kernelspace
代理模式
此代理模式仅在 Windows 节点上可用。
kube-proxy 在 Windows 虚拟过滤平台(Virtual Filtering Platform,VFP)中配置报文过滤规则,VFP 是 Windows vSwitch 的一个扩展。这些规则处理节点级虚拟网络中的封装报文,并重写报文,以便目标 IP 地址(和二层信息)正确,从而将报文路由到正确的目标。Windows VFP 类似于 Linux 的 nftables
或 iptables
等工具。Windows VFP 扩展了最初为支持虚拟机网络而实现的 Hyper-V Switch。
当节点上的 Pod 向虚拟 IP 地址发送流量时,如果 kube-proxy 选择另一个节点上的 Pod 作为负载均衡目标,kernelspace
代理模式会将报文重写为以目标后端 Pod 为目的地。Windows 主机网络服务(Host Networking Service,HNS)确保报文重写规则配置正确,以便返回流量看起来来自虚拟 IP 地址而不是特定的后端 Pod。
kernelspace
模式的直接服务器返回
Kubernetes v1.14 [alpha]
作为基本操作的替代方案,托管 Service 后端 Pod 的节点可以直接应用报文重写,而不是将此负担放在客户端 Pod 运行的节点上。这称为直接服务器返回(direct server return)。
要使用此功能,你必须使用 --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 上,不支持为 Services 设置最大会话粘滞时间。为 Service 分配 IP 地址
与实际路由到固定目标的 Pod IP 地址不同,Service IP 实际上并非由单个主机应答。相反,kube-proxy 使用数据包处理逻辑(例如 Linux iptables)来定义虚拟 IP 地址,这些地址在需要时会被透明地重定向。
当客户端连接到 VIP 时,其流量会自动传输到适当的端点。Services 的环境变量和 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 [稳定]
(默认启用:true)控制平面将现有的 etcd 分配器替换为使用 IPAddress 和 ServiceCIDR 对象而非内部全局分配映射的修订实现。与 Service 关联的每个 Cluster IP 地址随后引用一个 IPAddress 对象。
启用特性门控(feature gate)还会将后台控制器替换为处理 IPAddress 对象的替代实现,并支持从旧的分配器模型迁移。Kubernetes 1.33 不支持从 IPAddress 对象迁移到内部分配映射。
修订后的分配器的主要优势之一是,它消除了用于 Services 的 Cluster IP 地址的 IP 地址范围大小限制。启用 MultiCIDRServiceAllocator
后,对于 IPv4 没有限制;对于 IPv6,可以使用 /64 或更小的 IP 地址子网掩码(而旧实现使用的是 /108)。
通过 API 提供 IP 地址分配,意味着你作为集群管理员可以允许用户检查分配给其 Services 的 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 对象动态定义 Services 的可用 IP 范围。在引导过程中,会从 kube-apiserver 的 --service-cluster-ip-range
命令行参数的值创建一个名为 kubernetes
的默认 ServiceCIDR 对象。
kubectl get servicecidrs
NAME CIDRS AGE
kubernetes 10.96.0.0/28 17m
用户可以创建或删除新的 ServiceCIDR 对象来管理 Services 的可用 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 的现有行为。实现此目的的验证准入策略(Validation Admission Policy)示例是
---
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 根据配置的 service-cluster-ip-range
的大小,使用以下公式 min(max(16, cidrSize / 16), 256)
将 ClusterIP
范围划分为两个频段。该公式可以概括为:*不小于 16 或不大于 256,并在其间采用分级的阶梯函数*。
Kubernetes 倾向于通过从高频段中选择来为 Services 分配动态 IP 地址,这意味着如果你想为 type: ClusterIP
的 Service 分配特定的 IP 地址,你应该手动从低频段中分配一个 IP 地址。这种方法降低了分配冲突的风险。
流量策略
你可以设置 .spec.internalTrafficPolicy
和 .spec.externalTrafficPolicy
字段来控制 Kubernetes 如何将流量路由到健康的(“就绪”)后端。
内部流量策略
Kubernetes v1.26 [稳定]
你可以设置 .spec.internalTrafficPolicy
字段来控制来自内部来源的流量如何路由。有效值是 Cluster
和 Local
。将此字段设置为 Cluster
将内部流量路由到所有就绪端点,设置为 Local
则仅路由到本地节点上的就绪端点。如果流量策略是 Local
且没有本地节点端点,kube-proxy 将丢弃流量。
外部流量策略
你可以设置 .spec.externalTrafficPolicy
字段来控制来自外部来源的流量如何路由。有效值是 Cluster
和 Local
。将此字段设置为 Cluster
将外部流量路由到所有就绪端点,设置为 Local
则仅路由到本地节点上的就绪端点。如果流量策略是 Local
且没有本地节点端点,kube-proxy 不会转发相关 Service 的任何流量。
如果指定了 Cluster
,则所有节点都有资格成为负载均衡目标,前提是该节点未被删除且 kube-proxy 健康。在此模式下:负载均衡器健康检查被配置为针对服务代理的就绪性端口和路径。对于 kube-proxy,这会被评估为:${NODE_IP}:10256/healthz
。kube-proxy 将返回 HTTP 状态码 200 或 503。kube-proxy 的负载均衡器健康检查端点在满足以下条件时返回 200:
- kube-proxy 健康,这意味着
- 它能够正常进行网络编程并且在此过程中没有超时(超时时间定义为:2 ×
iptables.syncPeriod
);并且
- 它能够正常进行网络编程并且在此过程中没有超时(超时时间定义为:2 ×
- 节点未被删除(未为该节点设置删除时间戳)。
kube-proxy 在节点被删除时返回 503 并将节点标记为不合格的原因是,kube-proxy 支持对正在终止的节点进行连接耗尽。从 Kubernetes 管理的负载均衡器的角度来看,当节点*正在被*或*已经被*删除时,会发生几件重要的事情。
删除过程中
- kube-proxy 将开始使其就绪性探针失败,并基本上将该节点标记为不适合负载均衡器流量。负载均衡器健康检查失败会导致支持连接耗尽的负载均衡器允许现有连接终止,并阻止新连接建立。
删除时
- Kubernetes 云控制器管理器中的 Service 控制器将节点从引用的合格目标集合中移除。从负载均衡器的后端目标集合中移除任何实例会立即终止所有连接。这也是 kube-proxy 在节点删除期间首先使其健康检查失败的原因。
对于 Kubernetes 厂商来说,重要的是要注意,如果任何厂商将 kube-proxy 就绪性探针配置为活跃性探针(liveness probe):那么当节点正在删除时,kube-proxy 将开始持续重启,直到节点完全删除。kube-proxy 暴露了一个 /livez
路径,与 /healthz
不同,它不考虑节点的删除状态,而只考虑其网络编程的进度。因此,对于希望为 kube-proxy 定义活跃性探针的任何人来说,/livez
是推荐的路径。
部署 kube-proxy 的用户可以通过评估指标:proxy_livez_total
/ proxy_healthz_total
来检查就绪性/活跃性状态。这两个指标都发布两个系列,一个带有 200 标签,一个带有 503 标签。
对于 Local
Services:kube-proxy 在满足以下条件时返回 200:
- kube-proxy 健康/就绪,并且
- 在相关节点上有一个本地端点。
节点删除对 kube-proxy 关于负载均衡器健康检查的返回码没有影响。原因在于:如果所有端点同时运行在正在删除的节点上,删除这些节点最终可能导致入口(ingress)中断。
Kubernetes 项目建议云提供商集成代码将负载均衡器健康检查配置为针对服务代理的 healthz 端口。如果你正在使用或实现自己的虚拟 IP 实现(人们可以使用它替代 kube-proxy),你应该设置一个类似的健康检查端口,其逻辑与 kube-proxy 的实现相匹配。
流向正在终止的端点流量
Kubernetes v1.28 [稳定]
如果 kube-proxy 中启用了 ProxyTerminatingEndpoints
特性门控 且流量策略为 Local
,则该节点的 kube-proxy 会使用更复杂的算法来选择 Service 的端点。启用此特性后,kube-proxy 会检查该节点是否有本地端点以及所有本地端点是否被标记为正在终止。如果存在本地端点且所有端点都在终止,则 kube-proxy 会将流量转发到这些正在终止的端点。否则,kube-proxy 总是优先将流量转发到未终止的端点。
这种针对正在终止端点的转发行为的存在,是为了允许 NodePort
和 LoadBalancer
Services 在使用 externalTrafficPolicy: Local
时平滑地耗尽连接。
部署进行滚动更新时,支持负载均衡器的节点可能会从该部署的 N 个副本过渡到 0 个副本。在某些情况下,外部负载均衡器可以在健康检查探测之间向具有 0 个副本的节点发送流量。将流量路由到正在终止的端点可以确保正在缩减 Pod 的节点能够平滑地接收流量并将其导向那些正在终止的 Pod。等到 Pod 完成终止时,外部负载均衡器应该已经看到节点的健康检查失败,并已将该节点从后端池中完全移除。
流量分布
Kubernetes v1.33 [稳定]
(默认启用:true)Kubernetes Service 中的 spec.trafficDistribution
字段允许你表达流量应如何路由到 Service 端点的偏好。
优先选择靠近的
- 这会优先将流量发送到与客户端位于同一区域的端点。EndpointSlice 控制器会用
hints
更新 EndpointSlice 以传达此偏好,然后 kube-proxy 会使用这些提示进行路由决策。如果客户端所在区域没有任何可用端点,则流量将为该客户端路由到集群范围内的端点。
Kubernetes v1.33 [Alpha]
(默认未启用)当 PreferSameTrafficDistribution
特性门控 启用时,还有两个附加值可用:
优先选择同一区域
- 这与
PreferClose
含义相同,但更明确。(最初的意图是PreferClose
稍后可能包含“优先选择同一区域”以外的功能,但此计划已取消。将来,PreferSameZone
将是此功能推荐使用的值,而PreferClose
将被视为其已废弃的别名。) 优先选择同一节点
- 这会优先将流量发送到与客户端位于同一节点上的端点。与
PreferClose
/PreferSameZone
一样,EndpointSlice 控制器会用hints
更新 EndpointSlice,表明某个切片应用于特定节点。如果客户端所在节点没有任何可用端点,则服务代理将回退到“同一区域”行为,或者如果同一区域也没有端点,则回退到集群范围内的行为。
如果未设置 trafficDistribution
的任何值,默认策略是将流量均匀分布到集群中的所有端点。
与 service.kubernetes.io/topology-mode: Auto
的比较
带有 PreferClose
/PreferSameZone
的 trafficDistribution
字段,以及使用 service.kubernetes.io/topology-mode: Auto
注解的旧的“拓扑感知路由”特性,都旨在优先处理同一区域的流量。然而,它们的方法存在关键差异:
service.kubernetes.io/topology-mode: Auto
尝试根据可分配的 CPU 资源按比例跨区域分配流量。这种启发式方法包含保护措施(例如针对少量端点的回退行为),牺牲了一定的可预测性,以换取潜在更好的负载均衡。trafficDistribution: PreferClose
旨在更简单、更可预测:“如果区域中有端点,它们将接收该区域的所有流量;如果区域中没有端点,流量将分布到其他区域”。这种方法提供了更高的可预测性,但也意味着你需要负责避免端点过载。
如果 service.kubernetes.io/topology-mode
注解设置为 Auto
,它将优先于 trafficDistribution
。(该注解将来可能被废弃,转而使用 trafficDistribution
字段)。
与流量策略的交互
与 trafficDistribution
字段相比,流量策略字段(externalTrafficPolicy
和 internalTrafficPolicy
)旨在提供更严格的流量本地性要求。以下是 trafficDistribution
与它们的交互方式:
流量策略的优先性:对于给定的 Service,如果流量策略(
externalTrafficPolicy
或internalTrafficPolicy
)设置为Local
,则它对相应的流量类型(分别为外部或内部)具有优先权,高于trafficDistribution
。trafficDistribution
的影响:对于给定的 Service,如果流量策略(externalTrafficPolicy
或internalTrafficPolicy
)设置为Cluster
(默认值),或者如果这些字段未设置,则trafficDistribution
会指导相应流量类型(分别为外部或内部)的路由行为。这意味着会尝试将流量路由到与客户端位于同一区域的端点。
使用流量分布控制的注意事项
使用 trafficDistribution
的 Service 会尝试将流量路由到适当拓扑内的(健康的)端点,即使这意味着某些端点接收的流量比其他端点多得多。如果与客户端处于同一拓扑(“同一区域”、“同一节点”等)的端点数量不足,则端点可能会过载。如果传入流量未按比例分布在拓扑中,这种情况尤其可能发生。为缓解此问题,请考虑以下策略:
Pod 拓扑分布约束:使用 Pod 拓扑分布约束将 Pod 均匀分布到区域或节点上。
特定区域的 Deployment:如果你使用“同一区域”流量分布,但期望在不同区域看到不同的流量模式,可以为每个区域创建一个单独的 Deployment。这种方法允许不同的工作负载独立伸缩。Kubernetes 项目本身之外的生态系统中也提供了一些工作负载管理插件,可以提供帮助。
下一步
要了解更多关于 Services 的信息,请阅读使用 Services 连接应用。
你还可以
- 阅读有关 Services 的概念
- 阅读有关 Ingresses 的概念
- 阅读 Service API 参考