端点切片(Endpoint Slices)

    端点切片(Endpoint Slices) 提供了一种简单的方法来跟踪 Kubernetes 集群中的网络端点 (network endpoints)。它们为 Endpoints 提供了一种可伸缩和可拓展的替代方案。

    Endpoints API 提供了在 Kubernetes 跟踪网络端点的一种简单而直接的方法。 不幸的是,随着 Kubernetes 集群和 逐渐开始为更多的后端 Pods 处理和发送请求,原来的 API 的局限性变得越来越明显。 最重要的是那些因为要处理大量网络端点而带来的挑战。

    由于任一服务的所有网络端点都保存在同一个 Endpoints 资源中,这类资源可能变得 非常巨大,而这一变化会影响到 Kubernetes 组件(比如主控组件)的性能,并 在 Endpoints 变化时需要处理大量的网络流量和处理。 EndpointSlice 能够帮助你缓解这一问题,还能为一些诸如拓扑路由这类的额外 功能提供一个可扩展的平台。

    在 Kubernetes 中,EndpointSlice 包含对一组网络端点的引用。 指定选择器后控制面会自动为设置了 选择算符 的 Kubernetes 服务创建 EndpointSlice。 这些 EndpointSlice 将包含对与服务选择算符匹配的所有 Pod 的引用。 EndpointSlice 通过唯一的协议、端口号和服务名称将网络端点组织在一起。 EndpointSlice 的名称必须是合法的 。

    例如,下面是 Kubernetes 服务 example 的 EndpointSlice 资源示例。

    默认情况下,控制面创建和管理的 EndpointSlice 将包含不超过 100 个端点。 你可以使用 kube-controller-manager--max-endpoints-per-slice 标志设置此值,最大值为 1000。

    EndpointSlice 支持三种地址类型:

    • IPv4
    • IPv6
    • FQDN (完全合格的域名)

    EndpointSlice 中的每个端点都可以包含一定的拓扑信息。 这一信息用来标明端点的位置,包含对应节点、可用区、区域的信息。 当这些值可用时,控制面会为 EndpointSlice 设置如下拓扑标签:

    • kubernetes.io/hostname - 端点所在的节点名称
    • topology.kubernetes.io/zone - 端点所处的可用区

    这些标签的值时根据与切片中各个端点相关联的资源来生成的。 标签 hostname 代表的是对应的 Pod 的 NodeName 字段的取值。 zoneregion 标签则代表的是对应的节点所拥有的同名标签的值。

    管理

    通常,控制面(尤其是端点切片的 ) 会创建和管理 EndpointSlice 对象。EndpointSlice 对象还有一些其他使用场景, 例如作为服务网格(Service Mesh)的实现。这些场景都会导致有其他实体 或者控制器负责管理额外的 EndpointSlice 集合。

    为了确保多个实体可以管理 EndpointSlice 而且不会相互产生干扰,Kubernetes 定义了 标签 endpointslice.kubernetes.io/managed-by,用来标明哪个实体在管理某个 EndpointSlice。端点切片控制器会在自己所管理的所有 EndpointSlice 上将该标签值设置 为 endpointslice-controller.k8s.io。 管理 EndpointSlice 的其他实体也应该为此标签设置一个唯一值。

    在大多数场合下,EndpointSlice 都由某个 Service 所有,(因为)该端点切片正是 为该服务跟踪记录其端点。这一属主关系是通过为每个 EndpointSlice 设置一个 属主(owner)引用,同时设置 标签来标明的, 目的是方便查找隶属于某服务的所有 EndpointSlice。

    EndpointSlice 镜像

    控制面对 Endpoints 资源进行映射的例外情况有:

    • Endpoints 资源上标签 endpointslice.kubernetes.io/skip-mirror 值为 true
    • Endpoints 资源包含标签 control-plane.alpha.kubernetes.io/leader
    • 对应的 Service 资源不存在。
    • 对应的 Service 的选择算符不为空。

    每个 Endpoints 资源可能会被翻译到多个 EndpointSlices 中去。 当 Endpoints 资源中包含多个子网或者包含多个 IP 地址族(IPv4 和 IPv6)的端点时, 就有可能发生这种状况。 每个子网最多有 1000 个地址会被镜像到 EndpointSlice 中。

    每个 EndpointSlice 都有一组端口值,适用于资源内的所有端点。 当为服务使用命名端口时,Pod 可能会就同一命名端口获得不同的端口号,因而需要 不同的 EndpointSlice。这有点像 Endpoints 用来对子网进行分组的逻辑。

    控制面尝试尽量将 EndpointSlice 填满,不过不会主动地在若干 EndpointSlice 之间 执行再平衡操作。这里的逻辑也是相对直接的:

    1. 列举所有现有的 EndpointSlices,移除那些不再需要的端点并更新那些已经 变化的端点。
    2. 列举所有在第一步中被更改过的 EndpointSlices,用新增加的端点将其填满。
    3. 如果还有新的端点未被添加进去,尝试将这些端点添加到之前未更改的切片中, 或者创建新切片。

    这里比较重要的是,与在 EndpointSlice 之间完成最佳的分布相比,第三步中更看重 限制 EndpointSlice 更新的操作次数。例如,如果有 10 个端点待添加,有两个 EndpointSlice 中各有 5 个空位,上述方法会创建一个新的 EndpointSlice 而不是 将现有的两个 EndpointSlice 都填满。换言之,与执行多个 EndpointSlice 更新操作 相比较,方法会优先考虑执行一个 EndpointSlice 创建操作。

    由于 kube-proxy 在每个节点上运行并监视 EndpointSlice 状态,EndpointSlice 的 每次变更都变得相对代价较高,因为这些状态变化要传递到集群中每个节点上。 这一方法尝试限制要发送到所有节点上的变更消息个数,即使这样做可能会导致有 多个 EndpointSlice 没有被填满。

    重复的端点

    由于 EndpointSlice 变化的自身特点,端点可能会同时出现在不止一个 EndpointSlice 中。鉴于不同的 EndpointSlice 对象在不同时刻到达 Kubernetes 的监视/缓存中, 这种情况的出现是很自然的。 使用 EndpointSlice 的实现必须能够处理端点出现在多个切片中的状况。 关于如何执行端点去重(deduplication)的参考实现,你可以在 kube-proxyEndpointSlice 实现中找到。

    • 阅读