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