【Kubernetes知识点】 解读 Service 和 EndpointSlice 之间的关系
【Kubernetes知识点】 解读 Service 和 EndpointSlice 之间的关系
目录
- 1 概念
- 1.1 Service的概念
- 1.2 Endpoint 的概念
- 1.3 EndpointSlice 的引入
- 1.3.1 EndpointSlice支持的地址
- 1.3.2 EndpointSlice的状态
- 1.3.3 EndpointSlice的拓扑信息
- 1.4 Service 、Endpoint和 EndpointSlice 的关系
- 1.5 Endpoint 和 EndpointSlice 的对比
- 1.6 无选择器 Service 的场景
- 1.7 自定义EndpointSlice 要求
- 2 实验设计:验证Service 和 EndpointSlice关联
- 2.1 创建测试应用:
- 2.2 观察Service和EndpointSlice
- 2.3 模拟 Pod 变化: 扩容和缩容Pod
- 2.4 无选择器 Service 实验:
- 3 结论
- 4 参考文献
❤️ 摘要:在 Kubernetes 中,Service 和 EndpointSlice 是实现负载均衡和服务发现的重要组件。 EndpointSlice让Service更好地处理大量后端,并允许集群有效地更新其健康后端列表。本文将深入探讨它们之间的关系,并设计实验来验证这一关系。
💯 本文关联好文:
- 《一文读懂Service以及实践攻略》
1 概念
1.1 Service的概念
❔说明:想深入了解Service,请提前看前文《一文读懂Service以及实践攻略》
Kubernetes 中的 Service 是一种网络抽象层,旨在为一组 Pod 提供稳定的访问入口。通过 Service,用户可以不必关心 Pod 的变化,而是通过统一的 IP 地址和端口来访问这些 Pod。
1.2 Endpoint 的概念
Endpoints 是 Kubernetes 中用于跟踪服务网络端点的原始 API。每个 Service 都有一个对应的 Endpoints 对象,存储该服务的所有网络端点。然而,随着 Kubernetes 集群和服务的扩展,原有的 Endpoints API 逐渐暴露出其局限性,特别是在处理大量网络端点的时候。
Kubernetes 限制单个 Endpoints 对象中可以容纳的端点数量。当服务有超过 1000 个支持端点时,Kubernetes 会截断 Endpoints 对象中的数据, 并在 Endpoints 上设置注释: endpoints.kubernetes.io/over-capacity: truncated
。
1.3 EndpointSlice 的引入
❔ 说明: Kubernetes v1.21之后, EndpointSlice特性是stable
为了更有效地管理服务的端点,Kubernetes 引入了 EndpointSlice。与传统的 Endpoints 资源相比,EndpointSlice 提供了更好的扩展性,尤其是在处理大规模集群时。它将多个端点组织成切片,减少 API 调用次数,提高效率。
如果某个服务的端点数量达到阈值,那么 Kubernetes 将添加另一个空的 EndpointSlice 并在其中存储新的端点信息。默认情况下,一旦现有 EndpointSlice 全部包含至少 100 个端点,Kubernetes 就会创建一个新的 EndpointSlice。
1.3.1 EndpointSlice支持的地址
EndpointSlices 支持三种地址类型,设置addressType
字段:
- IPv4
- IPv6
- FQDN (Fully Qualified Domain Name)
❔ 说明: 每个
EndpointSlice
对象代表一个特定的IP地址类型。如果您有一项可通过 IPv4 和 IPv6 使用的服务,则至少有两个EndpointSlice
对象。
1.3.2 EndpointSlice的状态
EndpointSlice API有三个状态条件, 说明以下:
条件 | 说明 |
---|---|
ready | 正在运行的Pod将Ready 条件设置为 True 时,同时将 EndpointSlice 条件也设置为 true。 |
serving | serving 条件几乎与 ready 条件相同。不同之处在于,如果用户在 pod 终止时关心 pod是否还运行,则应检查 serving 条件。 |
Terminating | Terminating 是指示端点是否正在终止的条件。对于 Pod,这是设置了删除时间戳的任何 Pod。 |
1.3.3 EndpointSlice的拓扑信息
EndpointSlice 中的每个端点都可以包含相关的拓扑信息。拓扑信息包括端点的位置以及对应的Node和Zone的信息。字段说明以下:
nodeName
- 此端点所在节点的名称。zone
- 此端点所在的区域。
1.4 Service 、Endpoint和 EndpointSlice 的关系
打个比喻:在一个大型活动中,K8s中的Service作为活动入口的前台人员,他为所有来宾提供入口进入指示。每个来宾Endpoint在活动中有自己的身份标识,比如知道他们的座位号。
现在这个活动区,划分了多个小区域,EndpointSlice就像是一个拿着分区名单的组长,名单记录了一组来宾的身份信息和他们的座位分布。通过EndpointSlice,你可以更高效地管理和查找来宾的位置信息,尤其是在活动人数众多时。这种分片的方式让协调工作变得更加灵活和高效。
Kubernetes中,Service与EndpointSlice实现以下机制:
- 选择器机制:Service 使用标签选择器来确定哪些 Pod 作为其后端。EndpointSlice 则维护与这些 Pod 相关联的网络端点信息。
- 动态更新:当 Pod 的状态变化时,Service 控制器会自动更新 EndpointSlice,确保服务始终指向活跃的 Pod。
- 负载均衡:EndpointSlice 的分组机制使得负载均衡变得更加高效,能够快速响应 Pod 的变化。
1.5 Endpoint 和 EndpointSlice 的对比
作为新的替代方案,EndpointSlice与Endpoint有以下不同点:
资源对象 | Endpoints | EndpointSlice |
---|---|---|
数据结构 | 所有网络端点信息存储在一个单一的 Endpoints 对象中。随着后端 Pod 数量的增加,这些对象可能会变得非常庞大。 | 将网络端点组织为多个切片,使得每个切片只包含一部分端点信息,从而减小单个对象的大小。 |
性能 | 在更新过程中,Endpoints 的每次变更都会引发大量的流量,这在高频更新的场景下尤其明显。 | EndpointSlices 的设计使得添加或删除单个 Pod 只需触发相同数量的更新,但更新消息的大小要小得多,从而降低了 CPU 使用率和网络流量。 |
新特性支持 | N/A | EndpointSlices 支持新的网络功能,如双栈网络和拓扑感知路由,使得 Kubernetes 在网络管理上更加灵活和高效。 |
1.6 无选择器 Service 的场景
在某些情况下,用户可能希望定义一个没有选择器的 Service。这种情况通常用于以下场景:
- 外部数据库:在生产环境中使用外部数据库集群,而在测试环境中使用内部数据库。
- 跨命名空间或集群访问:需要将 Service 指向不同命名空间或其他集群中的 Service。
- 迁移工作负载:在评估迁移至 Kubernetes 的过程中,可能只在 Kubernetes 中运行部分后端。
在这些场景中,可以定义一个不带选择器的 Service。由于没有选择器,Kubernetes 不会自动创建对应的 EndpointSlice(和旧版的 Endpoints)对象。用户可以手动添加 EndpointSlice 对象,将 Service 映射到实际运行的网络地址和端口。
1.7 自定义EndpointSlice 要求
对于用户手动创建的 EndpointSlice,建议选择合适的标签值,例如 endpointslice.kubernetes.io/managed-by
。对于直接使用 kubectl
管理 EndpointSlices 的用户,建议使用描述此手动管理的名称,例如 “staff” 或 “cluster-admins”,应避免使用保留值 “controller”,因为它表示由 Kubernetes 自身控制平面管理的 EndpointSlices。
⚠️ 注意: Kubernetes API 服务器不允许代理未映射到 Pods 的端点。由于这一限制,诸如
kubectl port-forward service/<service-name>
的操作在 Service 没有选择器时会失败。
2 实验设计:验证Service 和 EndpointSlice关联
为验证 Service 和 EndpointSlice 之间的关系,我们可以进行以下实验:
2.1 创建测试应用:
沿用《一文读懂Service以及实践攻略》案例,创建一个简单的 Web 应用,定义一个 Deployment 和对应的 Service。
Deployment 示例:
apiVersion: apps/v1
kind: Deployment
metadata:
# 定义Deployment的名字
name: nginx-deployment
labels:
app: nginx
spec:
# 定义副本数
replicas: 1
# 选择器指定label与pod模板的label匹配
selector:
matchLabels:
app: nginx
template:
metadata:
# 与选择器指定label匹配
labels:
app: nginx
spec:
containers:
# pod名字,可自定义
- name: nginx
# 镜像源, 这里设置私有镜像源
image: harbor.zx/hcie/nginx:1.26.1
# pod暴露端口号
ports:
- containerPort: 80
name: http
protocol: TCP
Service 示例:
---
apiVersion: v1
kind: Service
metadata:
name: my-clusterip-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
部署Service和Deployment
kubectl apply -f nginx-deployment.yaml
kubectl apply -f clusterip-service.yaml
2.2 观察Service和EndpointSlice
查看service详细信息
kubectl describe svc my-clusterip-service
输出如下:
Name: my-clusterip-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=nginx
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.245.81.75
IPs: 10.245.81.75
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 172.16.194.102:80
Session Affinity: None
Events: <none>
查看endpointslice
kubectl describe endpointslices.discovery.k8s.io my-clusterip-service-xvl7k
输出如下:
Name: my-clusterip-service-xvl7k
Namespace: default
Labels: endpointslice.kubernetes.io/managed-by=endpointslice-controller.k8s.io
# 通过label,自动关联my-clusterip-service
kubernetes.io/service-name=my-clusterip-service
Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2024-09-28T09:43:42Z
AddressType: IPv4
Ports:
Name Port Protocol
---- ---- --------
<unset> 80 TCP
# 自动关联Endpoint,对应pod
Endpoints:
- Addresses: 172.16.194.102
# pod状态是Ready时, EndpointSlice状态自动设置为Ready
Conditions:
Ready: true
Hostname: <unset>
# 关联某个Deployment下pod
TargetRef: Pod/nginx-deployment-64db67d8bc-zblx6
# 描述pod在当前的节点
NodeName: k8s-worker1
# 描述pod在当前的域
Zone: <unset>
Events: <none>
❔ 说明:
- 通过创建上述 Deployment 和 Service,Kubernetes 会自动生成 EndpointSlice。
2.3 模拟 Pod 变化: 扩容和缩容Pod
扩容Deployment的副本数到3,观察EndpointSlice的变化:
kubectl scale deployment/nginx-deployment --replicas=3
观察Endpointslice,执行以下命令
kubectl describe endpointslices.discovery.k8s.io my-clusterip-service-xvl7k
输出如下:
erip-service-xvl7k
Name: my-clusterip-service-xvl7k
Namespace: default
Labels: endpointslice.kubernetes.io/managed-by=endpointslice-controller.k8s.io
kubernetes.io/service-name=my-clusterip-service
Annotations: endpoints.kubernetes.io/last-change-trigger-time: 2024-09-28T09:57:26Z
AddressType: IPv4
Ports:
Name Port Protocol
---- ---- --------
<unset> 80 TCP
# 新增两个Endpoint,并分配到不同的节点
Endpoints:
- Addresses: 172.16.194.102
Conditions:
Ready: true
Hostname: <unset>
TargetRef: Pod/nginx-deployment-64db67d8bc-zblx6
NodeName: k8s-worker1
Zone: <unset>
- Addresses: 172.16.135.200
Conditions:
Ready: true
Hostname: <unset>
TargetRef: Pod/nginx-deployment-64db67d8bc-qbmvd
NodeName: k8s-master3
Zone: <unset>
- Addresses: 172.16.126.43
Conditions:
Ready: true
Hostname: <unset>
TargetRef: Pod/nginx-deployment-64db67d8bc-p7xqj
NodeName: k8s-worker2
Zone: <unset>
Events: <none>
手动删除一个 Pod,并观察 EndpointSlice 的变化:
kubectl delete pod nginx-deployment-64db67d8bc-zblx6
观察Endpointslice,执行以下命令
kubectl describe endpointslices.discovery.k8s.io my-clusterip-service-xvl7k
输出如下:
# Endpoint列表会剔除被删除的pod
Endpoints:
- Addresses: 172.16.135.200
Conditions:
Ready: true
Hostname: <unset>
TargetRef: Pod/nginx-deployment-64db67d8bc-qbmvd
NodeName: k8s-master3
Zone: <unset>
- Addresses: 172.16.126.43
Conditions:
Ready: true
Hostname: <unset>
TargetRef: Pod/nginx-deployment-64db67d8bc-p7xqj
NodeName: k8s-worker2
Zone: <unset>
Events: <none>
---
Endpoints:
- Addresses: 172.16.135.200
Conditions:
Ready: true
Hostname: <unset>
TargetRef: Pod/nginx-deployment-64db67d8bc-qbmvd
NodeName: k8s-master3
Zone: <unset>
- Addresses: 172.16.126.43
Conditions:
Ready: true
Hostname: <unset>
TargetRef: Pod/nginx-deployment-64db67d8bc-p7xqj
NodeName: k8s-worker2
Zone: <unset>
- Addresses: 172.16.194.101
Conditions:
Ready: false -> Ready状态从false转为true
Hostname: <unset>
TargetRef: Pod/nginx-deployment-64db67d8bc-v86vr -> 添加新的pod
NodeName: k8s-worker1
Zone: <unset>
Events: <none>
❔步骤说明:
- EndpointSlice监听到Pod被删除的事件;
- 更新Endpoint列表,剔除被删除的Pod;
- EndpointSlice监听到新的Pod被创建;
- 更新Endpoint列表,新增新创建的Pod;
- 监听Pod的状态,如果Pod状态转为Ready, 则设置Endpoint的状态为Ready。
❔ 总结:
- 动态更新:当 Pod 的状态变化时,Service 控制器会自动更新 EndpointSlice,确保服务始终指向活跃的 Pod。
- 负载均衡:EndpointSlice 的分组机制使得负载均衡变得更加高效,能够快速响应 Pod 的变化。
2.4 无选择器 Service 实验:
如果是Service需要代理外部的应用,或者应用的部署在Service之后的, 可能会采用无选择器的Service这种方式部署Service资源。
创建一个不带选择器的 Service:
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
ports:
- port: 8080
targetPort: 8080
然后手动创建 EndpointSlice:
apiVersion: discovery.k8s.io/v1
addressType: IPv4
endpoints:
# 外部应用的IP地址
- addresses:
- 192.168.1.100
conditions:
ready: true
kind: EndpointSlice
metadata:
labels:
endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io
# 关联没有选择器的service
kubernetes.io/service-name: external-service
name: external-service-endpoints
namespace: default
# 指定映射的端口
ports:
- name: ""
port: 8080
protocol: TCP
创建service和endpointslice
kubectl apply -f external-service.yaml
kubectl apply -f external-service-endpoints.yaml
观察service状态
[root@k8s-master1 hcie]# kubectl describe svc external-service
Name: external-service
Namespace: default
Labels: <none>
Annotations: <none>
# 确实没有selector
Selector: <none>
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.245.23.244
IPs: 10.245.23.244
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
# 没有Endpoint
Endpoints: <none>
Session Affinity: None
Events: <none>
观察Endpointslice状态
[root@k8s-master1 hcie]# kubectl describe endpointslices.discovery.k8s.io external-service-endpoints
Name: external-service-endpoints
Namespace: default
Labels: endpointslice.kubernetes.io/managed-by=endpointslice-controller.k8s.io
kubernetes.io/service-name=external-service
Annotations: <none>
AddressType: IPv4
Ports:
Name Port Protocol
---- ---- --------
<unset> 8080 TCP
Endpoints:
- Addresses: 192.168.1.100
Conditions:
Ready: true
Hostname: <unset>
NodeName: <unset>
Zone: <unset>
Events: <none>
成功创建并关联。
3 结论
通过以上实验,我们可以验证 Service 和 EndpointSlice 之间的密切关系。EndpointSlices 的引入解决了原有 Endpoints 的性能瓶颈,使 Kubernetes 在网络管理上更加高效和灵活。在实验中,验证了Service 通过标签选择器关联 Pod,而 EndpointSlice 则负责维护这些 Pod 的网络信息。无选择器的 Service 也能够与外部后端进行整合,提供更大的灵活性。希望本文可以帮助你更深入了解它们间的关系。
4 参考文献
[1]Kubernetes 官方文档-service
[2]EndpointSlices