一、Service面临问题(会更新所有Node上的Endpoint)
Service的后端是一组Endpoint列表, 为客户端端应用提供了极大的便利。但是随着集群规模的扩大以及Service数量的增加,尤其是Service后端Endpoint数量的增加,kube-proxy需要维护的负载分发规则(例如iptables规则或ipvs)数量也会急剧增加,导致后续对Service后端Endpoint的增加、删除等更新操作成本急剧上升。例如:在kubernetes集群中有10000个Endpoint运行大约5000个Node上,则对单个Pod更新需要约5GB的数据传输,这不进对集群内的网络带宽浪费巨大,而对Master的冲击非常大,会影响Kubernetes整体的性能,在Deployment不断进行滚动升级情况下由于突出。
二、端点分片机制
Kubernetes1.6开始引入端点分片(Endpoint Slices)机制,引入新的EndpointSlice资源对象和一个新的EndpointSlice控制器。EndpointSlice通过对Endpoint分配管理来实现降低Master和各个Node之间的网络传输数量及提高整体性能目标。对与Deployment的滚动升级,可以实现更新部分Node上的Endpoint信息,Master和Node之间的数据传输减少了100倍左右,很大程度上提高了管理效率。
EndpointSlice根据Endpoint所在的Node的拓扑信息进行分配管理。
Endpoint Slice要实现第2 个目标为基于Node拓扑的服务路由提供支持,这需要与服务拓扑(Service Topology)机制共同实现。
三、端点分片(Endpoint Slices)
获取集群中EndpointSlice信息:
默认情况下,在EndpointSlice控制器创建的EndpointSlice中最多包含100个Endpoint,如需修改可以通过kube-controller-manager服务启动参数--max-endpoints-per-slice设置,但是上限不能超过1000。
EndpointSlice关键信息:
- 关联的服务名称:将EndpointSlice与Service的关联信息设置为一个标签kubernetes.io/service-name=webapp,该标签表明服务名称
- 地址类型AddressType: IPv4、IPv6、FQDN(全限定域名)
- 在Endpoint列表中列出的每个Endpoint信息
- Address:Endpoint的IP地址
- Conditions:Endpoint的状态信息,作为EndpointSlice的查询条件
- Hostname:在Endpoint中设置的主机名称
- TargetRef:Endpoint对应的Pod名称
- Topology:拓扑信息,作为基于拓扑感知的服务路由提供数据,目前EndpointSlice控制器自动设置的拓扑信息如下:
-- kubernetes.io/hostname: Endpoint所在的Node的名称
-- topology.kubernetes.io/zone: Endpoint所在的Zone信息,使用Node标签topology.kubernetes.io/zone的值
--topology.kubernetes.io/region: Endpoint所在的Region信息,使用Node标签topology.kubernetes.io/region的值
在大规模的集群中,管理员对不同地域或者不同区域的Node设置相关的topology标签,用于为node设置拓扑信息。
四、EndpointSlice控制器
通过endpointslice.kubernetes.io/managed-by标签进行设置,用于存在多个管理控制器的应用中,例如某个Service Mesh管理工具也可以对EndpointSlice进行分片管理。为了支持多个管理工具对EndpointSlice同时进行管理并且相互不干扰,可以通过endpointslice.kubernetes.io/managed-by标签设置管理器的名称,Kubernetes内置的EndpointSlice控制器自动设置该标签为endpointslice-controller.k8s.io,其它管理器硬设置唯一名称用于标识。
EndpointSlice的复制功能和数据分布管理机制:
- EndpointSlice复制(Mirroring)功能。应用程序有时可能会创建自定义的Endpoint资源,为了避免应用程序在创建Endpoint资源时再去创建EndpointSlice资源,Kubernetes控制平面会自动完成将Endpoint资源复制为EndpointSlice资源的操作,从kubernetes1.19版本默认开启此功能,但是以下情况不会自动执行复制:
--Endpoint资源设置了Label:endpointslice.kubernetes.io/skip-mirror=true
--Endpoint资源设置了Annotation:control-plane.alpha.kubernetes.io/leader
--Endpoint资源对应的Service对象不存在
--Endpoint资源对应的Service资源设置了非空的Selector
一个Endpoint资源同时存在IPv4和IPv6地址类型时,会被复制为多个EndpointSlice资源,每种地址类型最多会被复制为1000个EndpointSlice资源。
- EndpointSlice的数据分布管理机制。每个EndpointSlice资源都包含一组作用于全部Endpoint的端口号(Ports)。如果Service定义中端口号使用了字符串名称,则对于相同name的端口号,目标Pod的targetPort可能是不同的,结果是EndpointSlice资源将会不同。这与Endpoint资源设置自己(subset)的逻辑是相同的。
kubernetes控制平面对于EndpointSlice中数据的管理机制是尽可能填满,但不会在多个EndpointSlice数据不均衡的情况下主动执行重新平衡操作,其逻辑:
- 遍历当前所有EndpointSlice资源,删除其中不需要的Endpoint,更新已更改的匹配Endpoint;
- 遍历第1步中已更新的EndpointSlice资源,将需要添加的新的Endpoint填充进去;
- 如果还有新的待添加的Endpoint,则尝试将其放入之前未更新的EndpointSlice中,或者尝试创建新的EndpointSlice并添加。
第三步优先考虑创建新的EndpointSlice而不是更新原EndpointSilce。例如,如果要添加10个新的Endpoint,则当前两个EndpointSlice各有5个剩余空间可用于填充,系统会创建一个新的EndpointSlice用来填充这10个Endpoint。当个EndpointSlice创建优于对多个Endpoint的更新。
以上主要是由于每个节点上运行的kube-proxy都会持久监控EndpointSlice的变化,对于每次EndpointSlice每次更新的成本都很高,因为每次更新都需要Master将更新数据发送到每个kube-proxy。上述管理机制目的在于限制需要发送到每个节点的更新数据量,即使可能导致最终许多EndpointSlice资源未能填满。
实际上,这种不太理想的数据分布是罕见的。Master的EndpointSlice控制器处理的大多数更新所带来的数据量都足够小,使得对已存在(仍有空余空间)EndpointSlice的数据填充都没有问题。如果实在没有办法填充,则无论如何都需要创建新的EndpointSlice资源。此外对Dployment执行滚动升级操作时,由于后端Pod列表和相关Endpoint列表全部会发生变化,所以也会对EndpointSlice资源的内容全部更新。