Service 和 Endpoints 的关系?
在 Kubernetes (K8s) 中,Service 和 Endpoints 是两个紧密相关但职责不同的资源对象。
用一句话概括它们的关系:Service 是“前台”也就是访问入口(定义了访问策略),Endpoints 是“通讯录”也就是后端列表(存储了实际的后端 Pod IP 地址)。
以下是详细的深度解析:
1. 核心概念对比
Service (抽象层):
- 作用: 提供一个稳定的虚拟 IP (ClusterIP) 和 DNS 名称。
- 目的: 屏蔽后端 Pod 的动态变化(Pod 可能会挂掉、重启、扩缩容,IP 会变)。客户端只需要访问 Service,不需要关心具体是哪个 Pod 在服务。
- 角色: 它是定义者,定义了“我是谁”以及“我要把流量转发给谁(通过标签选择器)”。
Endpoints (具体层):
- 作用: 维护一个动态的 IP 地址列表。
- 目的: 告诉 Service,当前时刻,真正活着的、准备好接收流量的 Pod 的 IP 和端口具体是多少。
- 角色: 它是执行者/数据源,它是 Service 和 Pod 之间的桥梁。
2. 它们是如何关联的?(工作原理)
它们之间的纽带通常是 Label Selector(标签选择器) 和 资源名称。
场景 A:带有 Selector 的 Service(最常见)
当你创建一个带有 selector 的 Service 时,Kubernetes 的控制平面(Endpoint Controller)会自动执行以下操作:
- 查找: 扫描集群中所有满足
selector条件(例如app: nginx)且状态为 Running 的 Pod。 - 创建/更新: 自动创建一个与 Service 同名 的 Endpoints 对象。
- 填充: 将找到的 Pod IP 地址和端口填入这个 Endpoints 对象中。
- 同步: 如果 Pod 挂了或扩容了,Endpoint Controller 会感知到,并实时更新 Endpoints 对象中的 IP 列表。
数据流向:
Client -> Service (VIP) -> iptables/IPVS 规则 (基于 Endpoints 生成) -> Pod IP
场景 B:不带 Selector 的 Service(特殊场景)
如果你创建 Service 时没有指定 selector:
- K8s 不会自动创建 Endpoints 对象。
- 你需要手动创建一个与 Service 同名 的 Endpoints 对象,并手动填入后端的 IP 地址。
- 用途: 这种方式通常用于将 Service 映射到集群外部的数据库、遗留系统,或者另一个 Namespace 里的服务。
3. 形象的比喻
- Service 就像公司的总机号码(永远不变)。
- Pods 就像客服员工(员工会离职、入职、换工位,分机号会变)。
- Endpoints 就像前台手里的分机通讯录。
当客户打总机号码(访问 Service)时,前台(Kube-proxy)会查阅通讯录(Endpoints),看现在哪位员工(Pod)在位,然后把电话转接过去。如果来了新员工,通讯录会自动更新,但总机号码不需要变。
4. 实际操作中的体现
你可以通过 kubectl 命令验证这种关系:
1. 查看 Service:
kubectl get svc my-service
# 输出: NAME: my-service, CLUSTER-IP: 10.96.0.100
2. 查看对应的 Endpoints:
kubectl get endpoints my-service
# 输出: NAME: my-service, ENDPOINTS: 10.244.1.5:80,10.244.2.3:80
注意:Endpoints 的名字和 Service 是一样的,里面的 IP 就是 Pod 的 IP。
3. 查看 YAML 定义:
Service 的 YAML:
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: demo # 关键点:通过这个标签找 Pod
ports:
- port: 80
自动生成的 Endpoints YAML 片段:
apiVersion: v1
kind: Endpoints
metadata:
name: my-app # 关键点:名字与 Service 相同
subsets:
- addresses:
- ip: 10.244.0.5 # 实际 Pod IP
- ip: 10.244.0.6
ports:
- port: 80
5. 进阶:EndpointSlices (K8s 的演进)
在较新的 Kubernetes 版本(v1.21+)中,为了解决大规模集群中 Endpoints 对象过大导致的性能问题,引入了 EndpointSlices。
- 问题: 如果一个 Service 后面有 5000 个 Pod,那么 Endpoints 对象就会非常巨大。每次有一个 Pod 变动,整个巨大的对象都要在网络中传输给所有的节点,造成网络风暴。
- 解决:
EndpointSlice将原来的大 Endpoints 切分成多个小片(Slice)。 - 关系: Service -> EndpointSlices -> Pods。
- 现状: 虽然现在底层主要使用 EndpointSlices,但为了兼容性,K8s 依然会维护传统的 Endpoints 对象,逻辑关系依然适用。
总结
- Service 定义访问策略(VIP、端口)。
- Endpoints 存储目标地址(Pod IP 列表)。
- 两者通常通过 Selector 自动绑定,且同名。
kube-proxy监听 Endpoints 的变化来配置底层的网络转发规则(iptables/IPVS)。