K8S服务发现原理及开发框架的配合
1. K8S 服务发现技术层面底层原理
Kubernetes 的服务发现完全抛弃了传统服务注册中心的模式(如 Eureka, Consul),而是基于其内部的两个核心组件:Service 和 DNS。
底层工作流程如下:
- 标签选择器 (Label Selector) 是基础:当用户创建一个 Deployment 时,会为其 Pod 模板指定一个标签(例如 app: my-backend)。Deployment 会确保运行的 Pod 都带有这个标签。
- Service 创建虚拟 IP (ClusterIP):用户创建一个 Service 资源,并定义一个 selector,使其指向用户在上一步中定义的标签(selector: {app: my-backend})。Kubernetes 会立即为这个 Service 分配一个稳定不变的虚拟 IP,我们称之为 ClusterIP。这个 IP 只在集群内部有效。
- Endpoints/EndpointSlices 的自动更新:Kubernetes 控制平面中有一个 Endpoint Controller。它会持续监控带有特定标签的 Pod 的创建、销毁和健康状态。一旦发现 Pod 列表发生变化(比如一个新 Pod 启动并就绪),它就会更新一个与 Service 同名的 Endpoints 或 EndpointSlice 对象,其中包含了所有健康 Pod 的实际 IP 地址和端口列表(例如 [10.1.1.2:8080, 10.1.2.5:8080])。
- kube-proxy 实现流量转发:
- 每个 Node 节点上都运行着一个 kube-proxy 进程。
- kube-proxy 持续 watch Service 和 Endpoints/EndpointSlice 对象的变化。
- 当它拿到最新的 ClusterIP -> [PodIP1, PodIP2, ...] 的映射关系后,它会在每个 Node 的内核中创建网络规则(通常使用 IPVS 或 iptables)。
- 这些规则的作用是:“任何发送到 ClusterIP:Port 的流量,都将被拦截,并以负载均衡的方式(如轮询)转发到后端某一个真实的 PodIP:TargetPort”。
- CoreDNS 提供域名解析:
- 集群内部署了一套 DNS 服务(通常是 CoreDNS)。
- Kubernetes 会自动为每个 Service 创建一条 DNS A 记录,格式为:<service-name>.<namespace-name>.svc.cluster.local。
- 当一个 Pod(客户端)想要访问另一个服务时,它不需要知道 ClusterIP,而是直接使用这个 DNS 名称(例如 http://my-backend.default)。
- Pod 内的 DNS 解析请求会被发送到 CoreDNS,CoreDNS 解析该域名,并返回 Service 的 ClusterIP。
- 客户端 Pod 拿到 ClusterIP 后,向其发起请求。请求到达 Node 内核时,被 kube-proxy 设置的规则捕获,并转发给后端某个健康的真实 Pod。
K8S 服务发现的本质是 DNS 解析 + VIP (虚拟IP) 路由。它将服务发现的责任从客户端(如 Spring Cloud 的 Ribbon/LoadBalancer)下沉到了 Kubernetes 平台层,对应用完全透明。
2. K8S 自动扩缩容 (HPA) 原理
Kubernetes 的自动扩缩容主要由 Horizontal Pod Autoscaler (HPA) 实现。
工作原理如下:
- 指标收集 (Metrics Aggregation):
- 集群中必须部署 Metrics Server。它是一个轻量级的组件,定期从每个节点的 kubelet 收集 Pod 的 CPU 和内存等基础资源使用情况。
- kubelet 本身通过 CRI (Container Runtime Interface) 从 cgroup 获取容器的实际资源用量。
- Metrics Server 将这些指标聚合起来,并通过 Kubernetes Aggregation Layer 以标准的 metrics.k8s.io API 形式暴露出来。
- HPA 控制器循环 (Controller Loop):
- HPA Controller 是 kube-controller-manager 的一部分。
- 它定期(默认每 15 秒)通过 Metrics API 查询它所管理的 Deployment (或其他可伸缩资源) 的所有 Pod 的指标。
- 期望副本数计算:
- HPA Controller 获取到所有 Pod 的当前指标值(如 current_cpu_utilization)后,会与用户在 HPA 中定义的目标值(desired_cpu_utilization)进行比较。
- 计算公式为:desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]
- 示例:用户设置目标 CPU 为 50%,当前有 3 个副本,测得的平均 CPU 使用率为 75%。那么期望副本数 = ceil[3 * (75 / 50)] = ceil[4.5] = 5。
- 执行伸缩:
- 计算出期望副本数后,HPA Controller 并不会直接操作 Pod,而是去更新其管理的 Deployment 对象的 spec.replicas 字段。
- Deployment 的控制器(Deployment Controller)检测到 spec.replicas 发生变化,就会执行其本职工作——通过增加或删除 Pod 来使实际副本数与期望值匹配。
注意: HPA 也支持基于内存或自定义指标(如每秒请求数 QPS)进行扩缩容,这需要更复杂的监控设置(如 Prometheus Adapter)。
3. K8S 实现熔断和限流
Kubernetes 本身不直接提供应用级的熔断和限流功能。这些是 L7 的流量治理能力。在 Kubernetes 生态中,主要通过以下两种方式实现:
- 服务网格 (Service Mesh) - 推荐方式:
- 代表技术: Istio, Linkerd。
- 原理: Service Mesh 通过在每个 Pod 中自动注入一个轻量级网络代理(Sidecar Proxy,如 Envoy)来接管所有进出 Pod 的流量。
- 实现熔断 (Circuit Breaking): 用户可以在 Istio 的 DestinationRule 中配置熔断策略,例如“连续 5 次 5xx 错误,则将该 Pod 实例从负载均衡池中移除 1 分钟”。这些策略会被动态下发到所有客户端的 Envoy Sidecar 中,由 Envoy 来执行熔断逻辑。
- 实现限流 (Rate Limiting): 可以在 Istio 的 VirtualService 或专门的策略对象中配置全局或局部的速率限制,例如“对某个服务的访问速率不能超过 100 QPS”。
- API 网关 (API Gateway):
- 代表技术: Ambassador, Kong, APISIX 等,它们通常作为 Ingress Controller 运行。
- 原理: 所有从集群外部进入的流量都会先经过 API 网关。
- 实现: 用户可以在网关层针对特定路由(API)配置限流和熔断策略。这主要用于管理南北向(Ingress)流量,对于集群内部的东西向流量管理能力较弱。
4. 与 Java Spring 全家桶的配合策略
当把 Spring Cloud 应用迁移到 Kubernetes 时,需要调整思路,将平台能力与框架能力结合,避免冗余。
功能 | Spring Cloud 传统方式 | Kubernetes + Spring 推荐方式 | 配合说明 |
服务注册发现 | Eureka Server + @EnableEurekaClient | 完全禁用 Eureka | 使用 K8S 的 DNS 和 Service 进行服务发现。Spring 应用直接通过 http://service-name 访问,无需任何客户端发现组件。 |
客户端负载均衡 | Ribbon / Spring Cloud LoadBalancer | 禁用或简化 | 负载均衡由 K8S 的 kube-proxy (IPVS/iptables) 在内核层完成。Spring Cloud LoadBalancer 仍可保留,但它只会得到一个 ClusterIP,实际的 Pod 级别负载均衡已由 K8S 处理。 |
API 网关 | Spring Cloud Gateway / Zuul | 推荐使用 Ingress Controller (如 Nginx, Traefik) 或 API Gateway (如 Kong) | 将网关能力下沉到基础设施层,更高效、更通用。Spring Cloud Gateway 仍然可以用,但通常部署为集群内的一个普通服务。 |
配置管理 | Spring Cloud Config Server | 使用 K8S 的 ConfigMap 和 Secret | 将配置信息作为 ConfigMap/Secret 挂载到 Pod 中。可以使用 Spring Boot's Kubernetes integration (spring-cloud-starter-kubernetes-client-config) 自动读取这些配置,实现动态刷新。 |
熔断/降级 | Hystrix / Resilience4j | 方案一 (推荐): Service Mesh (Istio) 方案二: 应用内实现 (Resilience4j) | 方案一:将熔断下沉到 Service Mesh (Envoy),对应用透明,支持多语言,集中管理。方案二:如果不想引入 Service Mesh,继续在 Spring 应用中使用 @CircuitBreaker (Resilience4j) 注解。这提供了更精细的应用内控制(如 Fallback 方法),但配置分散。两者可以共存,形成多层防护。 |
限流 | Sentinel, Resilience4j | 方案一 (推荐): Service Mesh / Ingress 方案二: 应用内实现 | 与熔断类似。方案一:在入口 (Ingress) 或服务间 (Mesh) 进行限流,保护整个服务。方案二:在应用内对某个具体方法或资源进行限流。 |
最佳实践是: 将网络层和服务治理的通用能力(服务发现、负载均衡、路由、熔断、限流)下沉到 Kubernetes 和服务网格,让 Java 应用更专注于业务逻辑。应用内只保留那些需要业务逻辑感知的、非常精细的熔断降级策略。
5. 其他语言微服务框架与 K8S 的配合
- Go:
- 框架: Go-Kit, Go-Micro, gRPC。
- 配合: Go 语言开发的微服务天生轻量,非常适合 K8S。它们同样不再需要自己的服务发现机制,直接通过 K8S DNS 访问其他服务。熔断限流等可以通过引入服务网格(如 Istio)来解决,或者使用 Go 语言的库(如 gobreaker, ratelimit)在代码中实现。
- Python:
- 框架: Flask, Django, FastAPI。
- 配合: 与 Go 类似。通过 Gunicorn/uWSGI 部署后,打包成容器镜像。服务发现依赖 K8S DNS。东西向流量治理依赖服务网格,南北向依赖 Ingress。
- Node.js:
- 框架: Express, NestJS。
- 配合: 模式完全一致。利用 K8S 进行部署、扩缩容和服务发现。通过 Nginx Ingress Controller 处理入口流量和 TLS。服务间通信通过 K8S Service 名称。