端点切片
Kubernetes v1.21 [稳定]
Kubernetes 的 EndpointSlice API 提供了一种跟踪 Kubernetes 集群中网络端点的方法。EndpointSlices 提供了一种更可扩展和更灵活的替代方案,可以替代 Endpoints。
EndpointSlice API
在 Kubernetes 中,EndpointSlice 包含对一组网络端点的引用。控制平面会自动为任何具有 选择器 的 Kubernetes 服务创建 EndpointSlices。这些 EndpointSlices 包含对所有匹配服务选择器的 Pod 的引用。EndpointSlices 通过协议、端口号和服务名称的唯一组合将网络端点分组在一起。EndpointSlice 对象的名称必须是有效的 DNS 子域名。
例如,这是一个由 example
Kubernetes 服务拥有的示例 EndpointSlice 对象。
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: example-abc
labels:
kubernetes.io/service-name: example
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "10.1.2.3"
conditions:
ready: true
hostname: pod-1
nodeName: node-1
zone: us-west2-a
默认情况下,控制平面创建和管理 EndpointSlices,使每个 EndpointSlices 不超过 100 个端点。你可以使用 --max-endpoints-per-slice
kube-controller-manager 标志配置此参数,最多可达 1000 个。
当涉及到如何路由内部流量时,EndpointSlices 可以充当 kube-proxy 的事实来源。
地址类型
EndpointSlices 支持三种地址类型
- IPv4
- IPv6
- FQDN(完全限定域名)
每个 EndpointSlice
对象表示特定的 IP 地址类型。如果你有一个通过 IPv4 和 IPv6 可用的服务,那么将至少有两个 EndpointSlice
对象(一个用于 IPv4,一个用于 IPv6)。
条件
EndpointSlice API 存储有关端点的条件,这些条件可能对消费者有用。这三个条件是 ready
、serving
和 terminating
。
就绪
ready
是一个与 Pod 的 Ready
条件相映射的条件。具有 Ready
条件设置为 True
的正在运行的 Pod 应将此 EndpointSlice 条件也设置为 true
。出于兼容性原因,当 Pod 正在终止时,ready
永远不会是 true
。消费者应参考 serving
条件以检查正在终止的 Pod 的就绪状态。此规则的唯一例外是 spec.publishNotReadyAddresses
设置为 true
的服务。这些服务的端点将始终具有设置为 true
的 ready
条件。
提供服务
Kubernetes v1.26 [稳定]
serving
条件与 ready
条件几乎相同。区别在于,EndpointSlice API 的消费者如果关心 Pod 在终止时的就绪状态,则应检查 serving
条件。
注意
虽然serving
与 ready
几乎相同,但它被添加进来是为了防止破坏 ready
的现有含义。如果 ready
对终止端点可能为 true
,这对于现有客户端来说可能出乎意料,因为从历史上看,终止端点从未包含在 Endpoints 或 EndpointSlice API 中。出于这个原因,ready
对于终止端点 始终 为 false
,并且在 v1.20 中添加了一个新的条件 serving
,以便客户端可以独立于 ready
的现有语义来跟踪终止 Pod 的就绪状态。正在终止
Kubernetes v1.22 [beta]
Terminating
是一个指示端点是否正在终止的条件。对于 Pod 来说,这指的是任何设置了删除时间戳的 Pod。
拓扑信息
EndpointSlice 中的每个端点都可以包含相关的拓扑信息。拓扑信息包括端点的地理位置以及相应节点和区域的信息。这些信息可在 EndpointSlices 上的以下每个端点字段中找到
nodeName
- 此端点所在的节点的名称。zone
- 此端点所在的区域。
注意
在 v1 API 中,每个端点的 topology
在实际中被删除,取而代之的是专门的 nodeName
和 zone
字段。
在 EndpointSlice
资源的 endpoint
字段上设置任意拓扑字段已被弃用,并且在 v1 API 中不受支持。相反,v1 API 支持设置单独的 nodeName
和 zone
字段。这些字段会自动在 API 版本之间进行转换。例如,v1beta1 API 中 topology
字段中的 "topology.kubernetes.io/zone"
键的值可以通过 v1 API 中的 zone
字段访问。
管理
在大多数情况下,控制平面(特别是端点切片 控制器)创建和管理 EndpointSlice 对象。EndpointSlices 还有许多其他用例,例如服务网格实现,这会导致其他实体或控制器管理额外的 EndpointSlices 集。
为了确保多个实体可以管理 EndpointSlices 而不相互干扰,Kubernetes 定义了 标签 endpointslice.kubernetes.io/managed-by
,它指示管理 EndpointSlice 的实体。端点切片控制器在它管理的所有 EndpointSlices 上将 endpointslice-controller.k8s.io
设置为此标签的值。管理 EndpointSlices 的其他实体也应为此标签设置唯一的值。
所有权
在大多数用例中,EndpointSlices 由 EndpointSlice 对象跟踪端点的服务拥有。此所有权由每个 EndpointSlice 上的所有者引用以及一个 kubernetes.io/service-name
标签来指示,该标签允许简单地查找属于某个服务的全部 EndpointSlices。
EndpointSlice 镜像
在某些情况下,应用程序会创建自定义 Endpoints 资源。为了确保这些应用程序不需要同时写入 Endpoints 和 EndpointSlice 资源,集群的控制平面会将大多数 Endpoints 资源镜像到相应的 EndpointSlices。
控制平面会镜像 Endpoints 资源,除非
- Endpoints 资源具有设置为
true
的endpointslice.kubernetes.io/skip-mirror
标签。 - Endpoints 资源具有
control-plane.alpha.kubernetes.io/leader
注释。 - 相应的服务资源不存在。
- 相应的服务资源具有非空选择器。
单个 Endpoints 资源可能转换为多个 EndpointSlices。如果 Endpoints 资源具有多个子集或包含具有多个 IP 族(IPv4 和 IPv6)的端点,就会发生这种情况。每个子集最多 1000 个地址将被镜像到 EndpointSlices。
EndpointSlices 的分发
每个 EndpointSlice 都有一个端口集,该端口集适用于资源中的所有端点。当服务使用命名端口时,Pod 可能会针对同一个命名端口获得不同的目标端口号,从而需要不同的 EndpointSlices。这类似于子集如何与 Endpoints 分组的逻辑。
控制平面尝试尽可能地填满 EndpointSlices,但不主动重新平衡它们。逻辑相当简单
- 遍历现有的 EndpointSlices,删除不再需要的端点,并更新已更改的匹配端点。
- 遍历在第一步中被修改的 EndpointSlices,并用所需的任何新端点填充它们。
- 如果仍有新的端点需要添加,请尝试将它们放入以前未更改的切片中,或创建新的切片。
重要的是,第三步优先考虑限制 EndpointSlice 更新,而不是 EndpointSlices 的完美填充分布。例如,如果要添加 10 个新端点,并且有 2 个 EndpointSlices,每个都有空间容纳 5 个额外的端点,则这种方法将创建新的 EndpointSlice,而不是填充 2 个现有的 EndpointSlices。换句话说,创建单个 EndpointSlice 比更新多个 EndpointSlices 更可取。
由于 kube-proxy 在每个节点上运行并监视 EndpointSlices,因此对 EndpointSlice 的每一次更改都相对昂贵,因为它将被传输到集群中的每个节点。这种方法旨在限制需要发送到每个节点的更改数量,即使它可能导致多个 EndpointSlices 未被填满。
在实践中,这种不太理想的分发情况应该是很少见的。EndpointSlice 控制器处理的大多数更改都足够小,可以放入现有的 EndpointSlice 中,如果没有,则可能很快就会需要新的 EndpointSlice。部署的滚动更新还提供了一种自然的方式,可以重新打包 EndpointSlices,其中所有 Pod 及其对应的端点都将被替换。
重复端点
由于 EndpointSlice 更改的性质,端点可能在同一个时间出现在多个 EndpointSlice 中。这种情况会在 Kubernetes 客户端监视/缓存中对不同 EndpointSlice 对象的更改到达时间不同时自然发生。
注意
EndpointSlice API 的客户端必须遍历与服务关联的所有现有 EndpointSlices,并构建一个完整的唯一网络端点列表。值得一提的是,端点可能在不同的 EndpointSlices 中重复。
你可以找到一个关于如何在 kube-proxy
中的 EndpointSliceCache
代码中执行此端点聚合和去重操作的参考实现。
与 Endpoints 的比较
最初的 Endpoints API 提供了一种简单直观的方式来跟踪 Kubernetes 中的网络端点。随着 Kubernetes 集群和 服务 的增长,以处理更多流量并向更多后端 Pod 发送更多流量,最初 API 的局限性变得更加明显。最显著的是,这些局限性包括在扩展到大量网络端点时面临的挑战。
由于服务的所有网络端点都存储在一个 Endpoints 对象中,因此这些 Endpoints 对象可能会变得很大。对于保持稳定的服务(长时间内使用相同的端点集),影响不太明显;即使这样,Kubernetes 的一些用例也无法得到很好的满足。
当服务有大量后端端点,并且工作负载频繁扩展或频繁推出新更改时,对该服务单个 Endpoints 对象的每次更新都意味着 Kubernetes 集群组件之间(在控制平面内,以及节点和 API 服务器之间)存在大量流量。这种额外的流量在 CPU 使用方面也有一定的成本。
使用 EndpointSlices,添加或删除单个 Pod 会触发相同数量的更新通知观察更改的客户端,但在规模很大时,这些更新消息的大小会小得多。
EndpointSlices 还使围绕新功能(如双栈网络和拓扑感知路由)的创新成为可能。