第29篇 k8s之Service 与 Endpoints 深入:服务发现原理

发布时间:2026/6/1 5:38:02

第29篇 k8s之Service 与 Endpoints 深入:服务发现原理 IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章助你少走弯路。在第 28 篇中我们学会了用 Service 为 Pod 提供稳定的虚拟 IP 和 DNS 名称。Service 让你不再需要关心 Pod IP 的变化——只要记住redis-service这个名称就能访问后端的 Redis 实例。但这里有一个关键问题我们没有拆解Service 是怎么知道哪些 Pod 是“自己人”的当新 Pod 创建或旧 Pod 被删除时Service 的后端列表是如何自动更新的当 Pod 的 readiness probe 失败时流量又是如何被摘除的今天这篇我们要深入 Service 的底层机制把Endpoints、kube-proxy、CoreDNS这三个服务发现的核心组件彻底搞清楚。理解这些你才能在 Service “不工作”时快速定位到问题根源。一、回顾Service 解决了什么又留下了什么第 28 篇的核心结论Service 提供了一个稳定的虚拟 IPClusterIP客户端通过这个 IP 或 DNS 名称访问服务流量被自动分发到后端 Pod。但 Service YAML 里并没有任何 Pod IP 列表。我们只写了一个selector指定了app: redis这样的标签。那么问题来了谁负责找到所有匹配标签的 PodPod 的 IP 列表存储在哪里Service 的虚拟 IP 怎么把流量转发到真实的 Pod IPredis-service这个名称是怎么变成 IP 地址的这四个问题的答案分别对应 K8s 服务发现机制的四块拼图Endpoints 对象、kube-proxy、iptables/IPVS 规则、CoreDNS。二、EndpointsService 的“后端 Pod 清单”2.1 什么是 EndpointsEndpoints 是 K8s 中一个独立的资源对象由Endpoints ControllerController Manager 的一部分自动创建和维护。它的作用很简单存储与 Service 的 selector 匹配的所有 Pod 的 IP 和端口。每创建一个 ServiceK8s 就会自动创建一个同名的 Endpoints 对象。你可以把它想象成 Service 的“影子”——Service 是前台接待Endpoints 是后台维护的真实服务器列表。2.2 动手观察 Endpoints在第 28 篇中我们创建了redis-service和flask-service。现在来看看它们背后的 Endpoints输出NAME ENDPOINTS AGE redis-service10.244.1.5:6379 10m flask-service10.244.1.10:5000,10.244.1.11:5000,10.244.1.12:5000 10mENDPOINTS列展示了所有匹配标签的健康 Pod 的 IP 和端口。redis-service目前只有 1 个 Pod单副本flask-service有 3 个 Pod对应 Deployment 的 3 个副本。查看更详细的信息kubectl describe endpoints redis-service输出Name: redis-service Namespace: default Labels:appredis Subsets: Addresses:10.244.1.5 NotReadyAddresses:nonePorts: Name Port Protocol ---- ---- --------unset6379TCP Events:none关键字段解读Addresses当前健康且就绪的 Pod IP 列表。只有 readiness probe 通过的 Pod 才会出现在这里。NotReadyAddresses匹配了标签但 readiness probe 尚未通过的 Pod。这些 Pod 不会接收 Service 转发的流量。Subsets一组 IP:Port 的集合。一个 Service 可以有多个端口每个端口对应一个 Subset。2.3 Endpoints 的动态更新现在来验证 Endpoints 的自动更新能力。在一个终端持续观察 Endpointskubectl get endpoints flask-service-w在另一个终端删除一个 Flask Podkubectl delete pod flask-deployment-8f9a0b1c2d3-abcde观察-w的输出被删除 Pod 的 IP 会从ENDPOINTS列表中消失而 Deployment 重建的新 Pod IP 在 readiness probe 通过后会被自动加入。这个动态过程揭示了 Service 服务发现的完整闭环Pod 创建 → 标签匹配 → readiness probe 通过 → Endpoints Controller 更新 Endpoints → kube-proxy 更新 iptables 规则 → 流量开始分发到新 Pod。反过来Pod 的 readiness probe 失败 → 从 Addresses 移到 NotReadyAddresses → 流量摘除。对比 Docker ComposeCompose 的 DNS 轮询不会检查容器是否真正健康——即使容器 healthcheck 失败DNS 仍会返回其 IP导致请求被路由到不可用的实例。K8s 通过 readiness probe → Endpoints 的联动机制从根本上解决了这个问题。三、kube-proxy将虚拟 IP 翻译成真实 IP3.1 kube-proxy 的三种模式第 28 篇讲过Service 的 ClusterIP 是虚拟的——没有任何网络接口真正持有这个 IP。流量之所以能到达后端 Pod靠的是每个节点上运行的kube-proxy组件。kube-proxy 监听 API Server 中 Service 和 Endpoints 的变化在节点上创建网络规则将对 ClusterIP 的访问重定向到后端 Pod 的真实 IP。它支持三种工作模式iptables 模式与第 8 篇学到的 Docker 端口映射原理完全一致——都是通过 iptables 的 DNAT 规则修改数据包的目标地址。3.2 iptables 规则链示意当你访问redis-service的 ClusterIP10.96.100.50:6379时数据包经过的 iptables 规则链大致如下客户端 Pod → PREROUTING → KUBE-SERVICES → KUBE-SVC-REDIS │ └── 随机选择一条后端规则 ├── KUBE-SEP-POD1 → DNAT →10.244.1.5:6379 ├── KUBE-SEP-POD2 → DNAT →10.244.1.6:6379 └── KUBE-SEP-POD3 → DNAT →10.244.1.7:6379每条KUBE-SEP-*规则对应一个 Endpoint即一个 Pod IP。kube-proxy 确保这些规则与 Endpoints 对象始终保持同步。3.3 IPVS 模式的优势iptables 是顺序匹配的当 Service 和 Pod 数量达到数千个时规则数量呈指数增长新增连接延迟会明显升高。IPVSIP Virtual Server是 Linux 内核专门为负载均衡设计的模块使用哈希表查找后端性能几乎不受规则数量影响。生产环境中如果集群规模超过 1000 个 Service建议启用 IPVS 模式。四、CoreDNS将服务名翻译成 IP4.1 CoreDNS 在 K8s 中的角色Endpoints 解决了“Service 的后端有哪些 Pod”kube-proxy 解决了“虚拟 IP 怎么转发到 Pod IP”。但还缺一环客户端怎么从服务名找到 ClusterIP答案是CoreDNS。CoreDNS 是 K8s 集群的内置 DNS 服务器从 v1.13 起取代了 kube-dns运行在kube-system命名空间中。每个 Service 创建时CoreDNS 自动为其添加一条 A 记录服务名.命名空间.svc.cluster.local →ClusterIP4.2 动手验证 DNS 解析# 查看 CoreDNS Podkubectl get pods-nkube-system-lk8s-appkube-dns# NAME READY STATUS RESTARTS AGE# coredns-7c8b6f9d5f-abcde 1/1 Running 0 1d# 从任意 Pod 验证 DNS 解析kubectl run-it--rmdebug--imagealpine --sh# 在容器内执行nslookupredis-service输出Server:10.96.0.10 Address:10.96.0.10:53 Name: redis-service.default.svc.cluster.local Address:10.96.100.5010.96.0.10是 CoreDNS 的 ClusterIPkube-dnsServiceredis-service.default.svc.cluster.local是完整的 DNS 名称服务名.命名空间.svc.cluster.local10.96.100.50是redis-service的 ClusterIP你也可以直接使用短名称redis-service因为 Pod 的/etc/resolv.conf中配置了搜索域default.svc.cluster.local会自动补齐命名空间和集群域名。4.3 CoreDNS 与 Docker DNS 的对比回想第 9 篇学过的 Docker 内嵌 DNS127.0.0.11CoreDNS 的设计思想与之完全一致——都是通过 DNS 服务器实现服务名到 IP 的解析。关键区别在于规模Docker DNS 只服务于单台宿主机上的容器而 CoreDNS 服务于整个集群中所有 Pod 和 Service。在 Docker 中DNS 记录的生命周期与容器绑定在 K8s 中DNS 记录的生命周期与 Service 对象绑定——即使后端 Pod 全部重建DNS 解析结果ClusterIP也保持不变。4.4 常见 DNS 故障排查问题 1nslookup返回server cant find排查步骤# 检查 Service 是否存在kubectl get svc服务名# 检查 CoreDNS Pod 是否正常运行kubectl get pods-nkube-system-lk8s-appkube-dns# 检查 Pod 的 DNS 配置kubectlexecPod名--cat/etc/resolv.conf如果 CoreDNS Pod 全部 CrashLoopBackOff整个集群的服务发现将瘫痪——这也是为什么生产环境通常部署至少 2 个 CoreDNS 副本并配置反亲和性避免所有副本同时故障。问题 2DNS 解析正确但连接超时这说明 CoreDNS 工作正常问题出在网络层。检查 NetworkPolicy 是否阻止了流量或 Pod 和 Service 是否在同一个命名空间跨命名空间访问必须用完整域名。五、Headless Service直接返回 Pod IP5.1 什么是 Headless ServiceClusterIP 类型的 Service 返回的是虚拟 IP客户端不感知后端 Pod。但在某些场景下如数据库主从复制、StatefulSet 中的 Pod 需要直接通信客户端需要绕过 ClusterIP直接获取所有 Pod 的 IP 列表。Headless Service 就是为这种需求设计的——将clusterIP设为NoneDNS 查询不再返回 ClusterIP而是直接返回所有健康 Pod 的 IP。5.2 动手创建 Headless ServiceapiVersion: v1 kind: Service metadata: name: redis-headless spec: clusterIP: None selector: app: redis ports: - port:6379targetPort:6379kubectl apply-fredis-headless.yaml kubectl get svc redis-headless# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE# redis-headless ClusterIP None none 6379/TCP 10sCLUSTER-IPNone表示这是一个 Headless Service。5.3 验证 DNS 行为kubectl run-it--rmdebug--imagealpine --shnslookupredis-headless输出会包含所有匹配app: redis标签的健康 Pod 的 IP 地址而不是一个虚拟 ClusterIP。这与普通 Service 的 DNS 行为截然不同——Headless Service 的 DNS 查询直接返回 Pod IP A 记录客户端需要自己实现负载均衡和故障转移逻辑。5.4 Headless Service 的应用场景StatefulSet每个 Pod 有稳定的网络标识如redis-0.redis-headless.default.svc.cluster.localHeadless Service 为每个 Pod 提供独立的 DNS 记录数据库集群客户端需要连接特定实例如 MySQL 主库不能依赖随机负载均衡自定义服务发现应用需要获取所有后端实例列表实现客户端侧负载均衡六、服务发现的全链路拼图现在把四块拼图组装在一起完成一次完整的服务发现流程1. kubectl apply-fredis-service.yaml → Service 对象写入 etcd2. Endpoints Controller 监听 Service → 找到匹配 selector 的 Pod → 创建 Endpoints 对象3. CoreDNS 监听 Service → 创建 DNS A 记录redis-service →10.96.100.504. kube-proxy 监听 Service Endpoints → 在节点上创建 iptables/IPVS 规则5. 客户端 Pod 发起 DNS 查询redis-service → CoreDNS 返回 ClusterIP6. 客户端 Pod 向 ClusterIP:6379 发起 TCP 连接 → iptables 规则截获 → DNAT 到 Pod IP7. Pod 的 readiness probe 失败 → Endpoints 移除该 Pod IP → kube-proxy 更新规则 → 流量摘除这就是 K8s 服务发现的完整闭环。每一个环节都有对应的控制器在持续工作——Service Controller 管理 Service 生命周期Endpoints Controller 维护 Pod IP 列表kube-proxy 同步转发规则CoreDNS 提供名称解析。七、命令速查表八、本篇总结EndpointsService 的“后端 Pod 清单”由 Endpoints Controller 自动维护只包含 readiness probe 通过的 Pod IP。kube-proxy将 ClusterIP 的流量通过 iptables/IPVS 规则转发到后端 Pod支持 iptables 和 IPVS 两种模式。CoreDNSK8s 集群级 DNS 服务器自动为每个 Service 创建 A 记录服务名.命名空间.svc.cluster.local是 Docker DNS 的集群级升级版。Headless ServiceclusterIP: NoneDNS 直接返回 Pod IP 而非 ClusterIP适用于 StatefulSet 和数据库集群等需要直接通信的场景。服务发现全链路Service → Endpoints → CoreDNS → kube-proxy四者协作完成从名称解析到流量转发的完整过程。这篇彻底拆解了 K8s 服务发现的底层机制。但 Service 只能做四层TCP/UDP负载均衡——如果你需要基于 HTTP Host 或 URL 路径路由请求就需要七层负载均衡。下一篇——第 30 篇Ingress 基础域名路由与 Ingress Controller我们将解锁 K8s 的 HTTP 路由能力。想了解更多还可以去各个平台搜索「IT策士」一起升级 IT 思维

相关新闻