本文发布于Cylon的收藏册,转载请著名原文链接~
Endpoint
Endpoints 就是 service 中后端的server,通常来说 endpoint 与 service是关联的,例如下面的一个endpoints 资源。
apiVersion: v1
kind: Endpoints
metadata:
name: nginx
subsets:
- addresses:
- ip: 172.17.0.2
- ip: 172.17.0.3
ports:
- port: 80
name: "111" # 多个端口需要用name
- port: 88
name: "222"
而 Endpoints 资源是由控制平面的 Endpoints controller 进行管理的,主要用于将外部server引入至集群内时使用的,例如Kube-apiserver 在集群外的地址,以及external service所需要创建的。
我们看到 Endpoints controller 代码中,在对 该 informer 监听的包含 service 与 Pod,位于 NewEndpointController()
// NewEndpointController returns a new *EndpointController.
func NewEndpointController(podInformer coreinformers.PodInformer, serviceInformer coreinformers.ServiceInformer,
endpointsInformer coreinformers.EndpointsInformer, client clientset.Interface, endpointUpdatesBatchPeriod time.Duration) *EndpointController {
broadcaster := record.NewBroadcaster()
broadcaster.StartStructuredLogging(0)
broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: client.CoreV1().Events("")})
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "endpoint-controller"})
if client != nil && client.CoreV1().RESTClient().GetRateLimiter() != nil {
ratelimiter.RegisterMetricAndTrackRateLimiterUsage("endpoint_controller", client.CoreV1().RESTClient().GetRateLimiter())
}
e := &EndpointController{
client: client,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "endpoint"),
workerLoopPeriod: time.Second,
}
serviceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: e.onServiceUpdate,
UpdateFunc: func(old, cur interface{}) {
e.onServiceUpdate(cur)
},
DeleteFunc: e.onServiceDelete,
})
e.serviceLister = serviceInformer.Lister()
e.servicesSynced = serviceInformer.Informer().HasSynced
podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: e.addPod,
UpdateFunc: e.updatePod,
DeleteFunc: e.deletePod,
})
e.podLister = podInformer.Lister()
e.podsSynced = podInformer.Informer().HasSynced
endpointsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
DeleteFunc: e.onEndpointsDelete,
})
e.endpointsLister = endpointsInformer.Lister()
e.endpointsSynced = endpointsInformer.Informer().HasSynced
....
}
EndpointSlices [1]
EndpointSlices 是提供为集群内用于替换 Endpoints 资源的一种灵活并具有扩展性的一种资源,由控制平面的 EndpointSlices Controller 来创建和管理的,默认情况下 EndpointSlices Controller 创建和管理的EndpointSlices 资源将不大于100个Endpoints;可以通过 kube-controller-manager
的参数 --max-endpoints-per-slice
设置,该参数最大为1000 [2]
通常情况下无需自行创建该资源,因为在创建 service 资源时 通常是通过 label 来匹配到对应的 backend server
下面是一个完整的 EndpointSlices 资源清单
addressType: IPv4
apiVersion: discovery.k8s.io/v1beta1 #注意版本 1.21后是 v1
endpoints:
- addresses:
- 192.168.1.241
conditions:
ready: true
targetRef:
kind: Pod
name: netbox-ff6dd9445-kxr4s
namespace: default
resourceVersion: "1994535"
topology:
kubernetes.io/hostname: master-machine
- addresses:
- 192.168.1.242
conditions:
ready: true
targetRef:
kind: Pod
name: netbox-ff6dd9445-566tj
namespace: default
topology:
kubernetes.io/hostname: master-machine
kind: EndpointSlice
metadata:
annotations:
endpoints.kubernetes.io/last-change-trigger-time: "2023-02-24T22:40:20+08:00"
labels:
endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io
kubernetes.io/service-name: netbox
name: netbox-l489z
namespace: default
ports:
- name: ""
port: 80
protocol: TCP
在代码中 EndpointSlices 资源是这么呈现的,可以看到主要的就是包含一组 Endpoints 资源
type EndpointSlice struct {
metav1.TypeMeta
metav1.ObjectMeta
AddressType AddressType
Endpoints []Endpoint
Ports []EndpointPort
}
EndpointSlices 在 kube-proxy中的应用
Google 工程师 Rob Scott 在2020年一文 [3] 中提到了 EndpointSlices 的作用,从kubernetes 1.19 开始EndpointSlices 默认被开启,而开启后的kube-proxy将使用 EndpointSlices 读取集群内的service的 Endpoints,而这个最大的变化就是『拓扑感知路由』(Topology Aware Routing)
Rob Scott 在文中提到 EndpointSlice API 是为了提升 Endpoints API 的限制,例如,etcd的存储大小,以及pod规模变动时最大产生的超过22TB数据的问题
而这些问题可以通过文中变化图来说明,开启功能后会将所有匹配到的 Endpoint,划分为多个EndpointSlices,而在大规模集群环境场景下,每次的变更只需要修改其中一个 EndpointSlices 即可,这将带给 kube-proxy 提供超Endpoint模式 10倍的性能
Notes:该文中没有提到的一点是:”EndpointSlices资源解决的是集群内的service节点问题,如你使用了endpoint类资源,那么不会触发到EndpointSlices的资源,这部分在
kube-proxy
源码中可以很清晰的看到
下面的 kube-proxy 日志可以看到获取 server是通过 Endpoints 还是 EndpointSlices
endpointslicecache.go:322] Setting endpoints for "default/netbox" to [192.168.1.241:80 192.168.1.242:80]
10008 proxier.go:1057] Syncing ipvs Proxier rules
10008 iptables.go:343] running iptables-save [-t filter]
10008 iptables.go:343] running iptables-save [-t nat]
10008 ipset.go:173] Successfully add entry: 192.168.1.241,tcp:80,192.168.1.241 to ip set: KUBE-LOOP-BACK
总结
- Endpoints 与 EndpointSlices 均是为service提供端点的
- Service规模越大,那么Endpoints中的 Pod 数量越大,传输的 EndPoints 对象就越大。集群中 Pod 更改的频率越高,也意味着传输在网络中发生的频率就越高
- Endpoints 对象在大规模集群场景下存在下列问题:
- 增加网络流量
- 超大规模的 service 理论上会无法存储 该 Endpoints
- 处理Endpoints资源的 worker 会消耗更多的计算资源
- 隐性增加对控制平面的影响,service的可扩展性将降低
- Endpointslices 解决了:
- 部分更新,更少的网络流量
- Worker 处理 Endpoints 更新所需的资源更少
- 减少对控制平面的影响,提升的性能和 service 规模
Reference
[1] EndpointSlices
本文发布于Cylon的收藏册,转载请著名原文链接~
链接:https://www.oomkill.com/2023/02/ch18-endpointslices/
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」 许可协议进行许可。