微服务与 K8s 协作的完整运行全流程(从传统到云原生)

微服务与 K8s 协作的完整运行全流程(从传统到云原生)

  • [一、先从"纯微服务时代"说起(没有 K8s)](#一、先从“纯微服务时代”说起(没有 K8s))
    • [1.1 服务是如何"存在"的:Nacos(注册中心)](#1.1 服务是如何“存在”的:Nacos(注册中心))
    • [1.2 请求是如何进来的:Spring Cloud Gateway(网关)](#1.2 请求是如何进来的:Spring Cloud Gateway(网关))
    • [1.3 服务是如何被调用的:Feign(服务调用)](#1.3 服务是如何被调用的:Feign(服务调用))
    • [1.4 到底调哪一个实例:Ribbon(客户端负载均衡)](#1.4 到底调哪一个实例:Ribbon(客户端负载均衡))
    • [1.5 服务不稳定怎么办:Sentinel / Hystrix(自我保护)](#1.5 服务不稳定怎么办:Sentinel / Hystrix(自我保护))
    • [1.6 把整条链路串起来(纯微服务完整运行图)](#1.6 把整条链路串起来(纯微服务完整运行图))
    • [1.7 一句话总结"纯微服务时代"的本质](#1.7 一句话总结“纯微服务时代”的本质)
  • 二、微服务"单独使用"时的完整逻辑链
    • [2.1 流量如何进来:Gateway(网关)](#2.1 流量如何进来:Gateway(网关))
      • [2.1.1 为什么所有请求必须先到网关](#2.1.1 为什么所有请求必须先到网关)
      • [2.1.2 一个非常重要的设计含义](#2.1.2 一个非常重要的设计含义)
    • [2.2 服务如何被发现:Nacos(注册中心)](#2.2 服务如何被发现:Nacos(注册中心))
      • [2.2.1 服务启动时发生了什么](#2.2.1 服务启动时发生了什么)
      • [2.2.2 注册中心真正解决的是什么问题](#2.2.2 注册中心真正解决的是什么问题)
    • [2.3 服务如何调用:Feign + Ribbon](#2.3 服务如何调用:Feign + Ribbon)
      • [2.3.1 Feign:让调用"像本地方法"](#2.3.1 Feign:让调用“像本地方法”)
      • [2.3.2 Ribbon:真正决定"打到谁"](#2.3.2 Ribbon:真正决定“打到谁”)
    • [2.4 如何避免系统被拖垮:Sentinel / Hystrix](#2.4 如何避免系统被拖垮:Sentinel / Hystrix)
      • [2.4.1 问题的根源是什么](#2.4.1 问题的根源是什么)
      • [2.4.2 应用如何"自救"](#2.4.2 应用如何“自救”)
    • [2.5 把整条调用链完整串起来](#2.5 把整条调用链完整串起来)
    • [2.6 传统微服务架构的本质特征(核心结论)](#2.6 传统微服务架构的本质特征(核心结论))
  • 三、问题从哪里开始暴露?------规模一上来,一切都会变
    • [3.1 实例频繁上下线,注册信息开始变得"不可信"](#3.1 实例频繁上下线,注册信息开始变得“不可信”)
      • [3.1.1 规模变化带来的第一个冲击](#3.1.1 规模变化带来的第一个冲击)
      • [3.1.2 典型现象](#3.1.2 典型现象)
    • [3.2 心跳与注册中心压力,开始指数级放大](#3.2 心跳与注册中心压力,开始指数级放大)
      • [3.2.1 小规模时不明显的问题](#3.2.1 小规模时不明显的问题)
      • [3.2.2 问题是如何被放大的](#3.2.2 问题是如何被放大的)
    • [3.3 应用被迫关心过多"非业务问题"](#3.3 应用被迫关心过多“非业务问题”)
      • [3.3.1 应用职责开始失控](#3.3.1 应用职责开始失控)
      • [3.3.2 一个典型场景](#3.3.2 一个典型场景)
    • [3.4 部署、扩容、迁移,开始严重依赖人工经验](#3.4 部署、扩容、迁移,开始严重依赖人工经验)
      • [3.4.1 "能跑"不等于"好管"](#3.4.1 “能跑”不等于“好管”)
      • [3.4.2 系统规模越大,风险越集中](#3.4.2 系统规模越大,风险越集中)
    • [3.5 问题的根因是什么?(非常关键)](#3.5 问题的根因是什么?(非常关键))
    • [3.6 为什么此时引入 K8s 是"必然选择"](#3.6 为什么此时引入 K8s 是“必然选择”)
  • [四、引入 K8s 后,系统发生了什么根本变化?](#四、引入 K8s 后,系统发生了什么根本变化?)
    • [4.1 从"应用自理"到"平台托管"的根本转变](#4.1 从“应用自理”到“平台托管”的根本转变)
    • [4.2 服务是如何"存在"的:从注册中心 → Pod 生命周期](#4.2 服务是如何“存在”的:从注册中心 → Pod 生命周期)
      • [4.2.1 纯微服务时代的"存在方式"](#4.2.1 纯微服务时代的“存在方式”)
      • [4.2.2 K8s 时代的"存在方式"](#4.2.2 K8s 时代的“存在方式”)
    • [4.3 实例感知方式的变化:从"我去找你"到"平台给你一条路"](#4.3 实例感知方式的变化:从“我去找你”到“平台给你一条路”)
      • [4.3.1 之前:应用必须"知道所有实例"](#4.3.1 之前:应用必须“知道所有实例”)
      • [4.3.2 现在:应用只面对 Service](#4.3.2 现在:应用只面对 Service)
    • [4.4 负载均衡发生了"位置迁移"](#4.4 负载均衡发生了“位置迁移”)
      • [4.4.1 以前:负载均衡在应用内部](#4.4.1 以前:负载均衡在应用内部)
      • [4.4.2 现在:负载均衡下沉到平台层](#4.4.2 现在:负载均衡下沉到平台层)
    • [4.5 故障判断权的转移:从"我感觉不行了"到"平台说你不行了"](#4.5 故障判断权的转移:从“我感觉不行了”到“平台说你不行了”)
      • [4.5.1 应用视角的局限性](#4.5.1 应用视角的局限性)
      • [4.5.2 K8s 的全局视角](#4.5.2 K8s 的全局视角)
    • [4.6 微服务框架的角色变化(非常关键)](#4.6 微服务框架的角色变化(非常关键))
      • [4.6.1 微服务框架"退回本职工作"](#4.6.1 微服务框架“退回本职工作”)
    • [4.7 一张"前后对照"的核心变化表(强记忆点)](#4.7 一张“前后对照”的核心变化表(强记忆点))
    • [4.8 总结](#4.8 总结)
  • [五、K8s 介入后,各组件如何重新分工](#五、K8s 介入后,各组件如何重新分工)
    • [5.1 流量入口:Ingress + Gateway 的分层协作](#5.1 流量入口:Ingress + Gateway 的分层协作)
      • [5.1.1 K8s 时代的完整入口路径](#5.1.1 K8s 时代的完整入口路径)
      • [5.1.2 Ingress:集群级流量入口](#5.1.2 Ingress:集群级流量入口)
      • [5.1.3 Gateway:应用级流量治理中心](#5.1.3 Gateway:应用级流量治理中心)
    • [5.2 服务发现:Service + kube-proxy 全面接管](#5.2 服务发现:Service + kube-proxy 全面接管)
      • [5.2.1 为什么原来的服务发现模式行不通了](#5.2.1 为什么原来的服务发现模式行不通了)
      • [5.2.2 Service:稳定抽象的核心](#5.2.2 Service:稳定抽象的核心)
      • [5.2.3 kube-proxy:真正的"流量分发者"](#5.2.3 kube-proxy:真正的“流量分发者”)
    • [5.3 那 Nacos 还要不要?------角色必须收缩](#5.3 那 Nacos 还要不要?——角色必须收缩)
      • [5.3.1 结论先行(非常重要)](#5.3.1 结论先行(非常重要))
      • [5.3.2 为什么不能"双注册"](#5.3.2 为什么不能“双注册”)
    • [5.4 Feign / Ribbon:从"决策者"退回"调用工具"](#5.4 Feign / Ribbon:从“决策者”退回“调用工具”)
      • [5.4.1 Feign:依然有价值](#5.4.1 Feign:依然有价值)
      • [5.4.2 Ribbon:角色发生本质变化](#5.4.2 Ribbon:角色发生本质变化)
    • [5.5 Sentinel / Hystrix:依然不可或缺,但边界更清楚了](#5.5 Sentinel / Hystrix:依然不可或缺,但边界更清楚了)
      • [5.5.1 K8s 能解决什么,不能解决什么](#5.5.1 K8s 能解决什么,不能解决什么)
      • [5.5.2 微服务治理框架的真实定位](#5.5.2 微服务治理框架的真实定位)
    • [5.6 把新的分工体系一口气串起来](#5.6 把新的分工体系一口气串起来)
  • [5.7 总结](#5.7 总结)
  • 六、一个"推荐的完整协作流程"
    • [6.1 推荐的请求全流程(先给结论)](#6.1 推荐的请求全流程(先给结论))
    • [6.2 Ingress:把"外部世界"与"集群内部"彻底隔离](#6.2 Ingress:把“外部世界”与“集群内部”彻底隔离)
      • [6.2.1 Ingress 解决的不是业务,而是"边界问题"](#6.2.1 Ingress 解决的不是业务,而是“边界问题”)
      • [6.2.2 为什么不让 Gateway 直接暴露到公网](#6.2.2 为什么不让 Gateway 直接暴露到公网)
    • [6.3 Gateway:真正的"业务入口中枢"](#6.3 Gateway:真正的“业务入口中枢”)
      • [6.3.1 Gateway 在 K8s 中的定位更清晰了](#6.3.1 Gateway 在 K8s 中的定位更清晰了)
      • [6.3.2 一个典型例子](#6.3.2 一个典型例子)
    • [6.4 Service:整个体系中最容易被低估的一层](#6.4 Service:整个体系中最容易被低估的一层)
      • [6.4.1 Service 是"服务发现"的最终形态](#6.4.1 Service 是“服务发现”的最终形态)
      • [6.4.2 为什么 Service 是微服务的"地基"](#6.4.2 为什么 Service 是微服务的“地基”)
    • [6.5 kube-proxy:负载均衡真正发生的地方](#6.5 kube-proxy:负载均衡真正发生的地方)
      • [6.5.1 kube-proxy 在做什么](#6.5.1 kube-proxy 在做什么)
      • [6.5.2 一个关键变化(必须理解)](#6.5.2 一个关键变化(必须理解))
    • [6.6 Pod:业务真正发生的地方](#6.6 Pod:业务真正发生的地方)
      • [6.6.1 Pod 不再需要"证明自己活着"](#6.6.1 Pod 不再需要“证明自己活着”)
    • [6.7 Sentinel:最后一道"业务级安全网"](#6.7 Sentinel:最后一道“业务级安全网”)
      • [6.7.1 为什么 K8s 之后仍然需要 Sentinel](#6.7.1 为什么 K8s 之后仍然需要 Sentinel)
    • [6.8 Controller:真正的"兜底之王"](#6.8 Controller:真正的“兜底之王”)
      • [6.8.1 Controller 干的不是"救请求",而是"救系统"](#6.8.1 Controller 干的不是“救请求”,而是“救系统”)
    • [6.9 把整条"推荐协作链路"一次记住](#6.9 把整条“推荐协作链路”一次记住)
    • [6.10 总结](#6.10 总结)
    • 七、服务不可用时,谁在兜底?
      • [7.1 分层兜底原则](#7.1 分层兜底原则)
      • [7.2 场景串联示例](#7.2 场景串联示例)
  • 八、最终总结
    • [8.1 核心理解:职责下沉 + 分层清晰](#8.1 核心理解:职责下沉 + 分层清晰)
    • [8.2 功能性变化的组件总结](#8.2 功能性变化的组件总结)
    • [8.3 为什么这些改变重要?](#8.3 为什么这些改变重要?)
    • [8.4 场景串联记忆法](#8.4 场景串联记忆法)
    • [8.5 本章一句话高度抽象](#8.5 本章一句话高度抽象)

一、先从"纯微服务时代"说起(没有 K8s)

在不使用 Kubernetes 的情况下,

微服务并不是"天生就有平台托管"的,而是完全靠应用自己把一切撑起来

我们先看一套最经典、最具代表性的组合(很多系统今天仍在使用):

  • Nacos(注册中心 / 配置中心)
  • Spring Cloud Gateway(网关)
  • Ribbon(客户端负载均衡)
  • Feign(服务调用)
  • Sentinel(限流 / 熔断)
  • Hystrix(容错 / 降级,旧体系)

这套体系有一个非常鲜明的特点:

它是一套"以应用为中心"的架构

什么意思?

一句话概括就是:

服务不仅要"干业务",还要"操心自己怎么活、怎么找别人、怎么扛风险"。

下面我们把这套体系从底层逻辑拆开讲清楚


1.1 服务是如何"存在"的:Nacos(注册中心)

在纯微服务时代,一个服务要想被别人调用,第一步不是写接口,而是"报到"

一个服务启动时,内部会做这样几件事:

  1. 启动 JVM / 进程

  2. 读取自身配置(端口、服务名、环境)

  3. 向 Nacos 注册:

    • 服务名
    • IP
    • Port
  4. 定期发送心跳,证明"我还活着"

于是,Nacos 里保存着类似这样的信息:

text 复制代码
order-service:
  - 10.0.1.12:8080
  - 10.0.1.15:8080

可以这样理解:

Nacos 是"服务通讯录 + 存活证明中心"

⚠️ 这里有一个非常重要的隐含点(后面和 K8s 对比会用到):

实例 IP 和端口,是"真实存在、必须准确"的


1.2 请求是如何进来的:Spring Cloud Gateway(网关)

在没有容器平台兜底的情况下,系统通常会设计一个唯一入口

text 复制代码
用户 → Gateway → 各个服务

Gateway 在这里承担的角色非常重:

  • 路由:请求该去哪个服务
  • 鉴权:你有没有权限
  • 限流:你是不是请求太猛
  • 统一入口:不允许直接打到服务

可以把它理解为:

整个微服务系统的"门禁 + 分流大厅"

👉 所有流量,必须先经过 Gateway,

👉 任何服务都不应该直接暴露给外部。


1.3 服务是如何被调用的:Feign(服务调用)

当一个服务要调用另一个服务时,开发者并不会手写 HTTP 代码,而是用 Feign:

java 复制代码
@FeignClient("order-service")
public interface OrderClient {
    Order getOrder(Long id);
}

Feign 的本质是:

"把 HTTP 调用,伪装成一次本地方法调用"

但注意一件事:

👉 Feign 自己并不知道 order-service 在哪

它只知道一个名字。


1.4 到底调哪一个实例:Ribbon(客户端负载均衡)

这一步是纯微服务时代最核心、也最容易被忽略的地方

流程是这样的:

  1. Feign 发起调用

  2. Ribbon 向 Nacos 拉取:

    • order-service 的所有实例列表
  3. Ribbon 在调用方进程内部

    • 根据算法(轮询、随机、权重等)
    • 选出一个 IP:Port
  4. 请求直接打到该实例

也就是说:

负载均衡不是发生在"中间层",而是发生在"每一个调用方内部"

这是"客户端负载均衡"这个词的真实含义。


1.5 服务不稳定怎么办:Sentinel / Hystrix(自我保护)

在纯微服务体系中,还有一个非常现实的问题:

下游一旦慢或挂,上游就可能被拖死

因此,每个服务必须自带保护机制

Sentinel / Hystrix 在这里做的事情包括:

  • 请求太多 → 限流
  • 下游太慢 → 熔断
  • 错误率过高 → 快速失败
  • 返回兜底结果,避免线程堆积

可以这样理解:

这是服务"对外依赖"的安全气囊

⚠️ 非常关键的一点:

这些保护逻辑,全部运行在应用进程内部


1.6 把整条链路串起来(纯微服务完整运行图)

现在,把上面所有组件连成一条线:

text 复制代码
用户
 ↓
Gateway(统一入口 / 限流 / 鉴权)
 ↓
Feign(发起服务调用)
 ↓
Ribbon(从 Nacos 拉实例并选择一个)
 ↓
目标服务实例
 ↓
Sentinel / Hystrix(兜底保护)

你会发现一个非常明显的特征:

应用"知道"并"参与"了整个系统运行的每一个细节


1.7 一句话总结"纯微服务时代"的本质

请记住这段话,后面和 K8s 对比时非常重要:

在纯微服务架构中:
服务 = 业务逻辑 + 服务发现 + 负载均衡 + 容错 + 自我保护

这套体系:

  • 能跑
  • 能扛
  • 能扩展

但代价是:

👉 应用承担了太多本该由平台解决的事情


二、微服务"单独使用"时的完整逻辑链

这一节我们只做一件事

从"一个请求进入系统"开始,一步一步把整套微服务体系如何协作讲清楚

先记住一个总前提:

此时,没有平台兜底,一切系统能力都在"应用内部"完成


2.1 流量如何进来:Gateway(网关)

2.1.1 为什么所有请求必须先到网关

在传统微服务架构中,任何外部请求都不允许直接访问业务服务,而是统一走网关,例如:

text 复制代码
用户
 ↓
Gateway

这里的 Gateway,通常是 Spring Cloud Gateway

它的核心职责并不是"转发请求"这么简单,而是:

  • 统一入口:屏蔽内部服务结构
  • 请求路由:决定请求该去哪一个服务
  • 身份鉴权:你有没有访问权限
  • 基础限流:防止恶意或突发流量

可以把它理解为:

系统的"唯一大门 + 第一层安全防线"


2.1.2 一个非常重要的设计含义

Gateway 的存在,意味着:

内部服务不关心"用户从哪来",只关心"我该怎么处理业务"

这是微服务分层思想的第一步。


2.2 服务如何被发现:Nacos(注册中心)

2.2.1 服务启动时发生了什么

在没有平台托管的情况下,服务要想被访问,必须主动"告诉世界我在哪"

一个服务启动后的典型流程是:

  1. 启动 JVM 进程
  2. 读取服务名、IP、端口
  3. Nacos 注册实例信息
  4. 定期发送心跳
  5. 下线或宕机后被摘除

注册中心中维护的数据结构,本质上是:

text 复制代码
order-service:
  - 10.0.0.1:8080
  - 10.0.0.2:8080

2.2.2 注册中心真正解决的是什么问题

可以用一句话概括:

Nacos 解决的是"服务在哪"和"还活着吗"的问题

但要注意一个关键前提:

IP + Port 是稳定、可直连、可信的

这一点,在后面与 K8s 协作时,会发生根本性变化。


2.3 服务如何调用:Feign + Ribbon

2.3.1 Feign:让调用"像本地方法"

当服务 A 调用服务 B 时,通常不会手写 HTTP 请求,而是使用 Feign

java 复制代码
@FeignClient("order-service")
Order getOrder(Long id);

Feign 的核心价值是:

隐藏网络细节,让开发者"像调用本地方法一样调用远程服务"

但这里有一个容易忽略的事实:

Feign 并不知道 order-service 在哪


2.3.2 Ribbon:真正决定"打到谁"

真正完成实例选择的是 Ribbon

完整调用链路是:

  1. Feign 发起调用
  2. Ribbon 向 Nacos 拉取所有实例
  3. 调用方 JVM 内部执行负载均衡算法
  4. 选中一个 IP:Port
  5. 请求直接发往该实例
text 复制代码
Feign
 ↓
Ribbon(选择实例)
 ↓
某个真实 IP:Port

👉 这意味着一件非常重要的事情:

调用方"知道并维护着所有下游实例列表"

这就是所谓的客户端负载均衡


2.4 如何避免系统被拖垮:Sentinel / Hystrix

2.4.1 问题的根源是什么

在分布式系统中,一个非常现实的风险是:

下游服务一慢,上游线程就会被大量占用

如果不加控制,最终结果往往是:

  • 线程池耗尽
  • 请求堆积
  • 整个系统级联失败

2.4.2 应用如何"自救"

因此,纯微服务体系中,每个服务都必须内置保护能力,常见实现是:

  • Sentinel
  • Hystrix(旧体系)

它们提供的能力包括:

  • 限流:请求过多直接拒绝
  • 熔断:错误率过高时短路调用
  • 降级:返回兜底结果,保护系统

一个非常关键的事实是:

这些判断和决策,全部发生在应用进程内部


2.5 把整条调用链完整串起来

现在,我们把所有组件连成一条完整路径:

text 复制代码
用户
 ↓
Gateway(统一入口 / 第一层防护)
 ↓
Feign(发起服务调用)
 ↓
Ribbon(从 Nacos 选实例)
 ↓
目标服务实例
 ↓
Sentinel / Hystrix(限流 / 熔断 / 降级)

你会发现一个非常鲜明的特点:

应用不仅在"处理业务",还在"维护系统稳定性"


2.6 传统微服务架构的本质特征(核心结论)

这一整套机制,可以浓缩为一句话:

在纯微服务体系中:
应用自己管理应用的一切

包括但不限于:

  • 实例注册与发现
  • 实例健康判断
  • 负载均衡决策
  • 故障识别与自我保护
  • 流量控制与降级

👉 能力很强,但复杂度完全压在应用身上


三、问题从哪里开始暴露?------规模一上来,一切都会变

在系统规模较小时,前面那套纯微服务逻辑链看起来运行良好:

  • 服务能注册
  • 调用能成功
  • 限流、熔断也能兜住

但一旦进入真实生产环境 + 持续扩张阶段,问题并不是"会不会出现",而是:

一定会出现,而且是系统性、结构性的

下面我们从几个必然发生的角度,把问题拆开来看。


3.1 实例频繁上下线,注册信息开始变得"不可信"

3.1.1 规模变化带来的第一个冲击

当系统规模扩大后,服务实例会频繁发生:

  • 自动扩容
  • 手动扩容
  • 版本发布
  • 机器重启
  • 故障迁移

这意味着一件事:

实例的 IP 和 Port,开始变成"高频变动资源"

但在纯微服务体系中:

  • 注册中心假设实例是相对稳定的
  • 调用方假设拉到的列表是可直接使用的

当这个前提被打破时,问题就开始显现。


3.1.2 典型现象

你会逐渐遇到这些情况:

  • 注册中心里存在已经不可达的实例
  • 刚下线的实例仍被调用
  • 刚上线的实例流量迟迟打不过来
  • 调用方缓存的实例列表与真实情况不一致

本质原因只有一句话:

实例状态变化速度,已经超过了"应用感知能力"的极限


3.2 心跳与注册中心压力,开始指数级放大

3.2.1 小规模时不明显的问题

在实例数量不多时:

  • 心跳包很轻
  • 注册中心压力可控
  • 网络消耗可以忽略

但当实例数量上升到 几百、上千 时,情况会完全不同。


3.2.2 问题是如何被放大的

假设:

  • 500 个服务实例
  • 每个实例 5 秒一次心跳

那么注册中心每分钟要处理的就是:

text 复制代码
500 × 12 = 6000 次心跳

这还不包括:

  • 服务上下线的注册 / 反注册
  • 调用方频繁拉取实例列表
  • 配置变更通知

于是你会看到:

  • 注册中心 CPU 飙升
  • 心跳超时被误判为下线
  • 大量实例被反复摘除、恢复

👉 服务本身没问题,系统却开始"自我扰乱"


3.3 应用被迫关心过多"非业务问题"

3.3.1 应用职责开始失控

回顾纯微服务体系中,一个服务实际要承担的事情:

  • 业务逻辑
  • 服务注册与发现
  • 实例健康判断
  • 负载均衡策略
  • 限流与熔断
  • 线程池保护
  • 调用超时管理

随着规模扩大,开发者会明显感觉到:

写业务代码的时间越来越少,处理系统问题的时间越来越多


3.3.2 一个典型场景

当线上出现问题时,排查路径往往是:

  • 是不是注册中心没同步?
  • 是不是实例列表缓存没更新?
  • 是不是 Ribbon 选到了坏实例?
  • 是不是限流规则过严?
  • 是不是线程池被打满?

这些问题的共同点是:

它们本不该由"业务服务"直接承担


3.4 部署、扩容、迁移,开始严重依赖人工经验

3.4.1 "能跑"不等于"好管"

在没有统一运行平台的情况下:

  • 部署新服务,需要人工挑机器
  • 扩容实例,需要手动调整配置
  • 发布新版本,需要人为控制顺序
  • 迁移机器,需要逐个处理服务

结果往往是:

  • 强依赖经验丰富的人
  • 操作步骤不可复制
  • 容易出现人为失误

3.4.2 系统规模越大,风险越集中

当系统依赖少数人"稳住"时,本质上意味着:

系统的稳定性,建立在"人"而不是"机制"之上

这是任何长期运行系统都无法接受的状态。


3.5 问题的根因是什么?(非常关键)

把上面所有问题抽象后,你会发现它们都指向同一个根因:

"运行应用"这件事,被拆散在了无数个应用进程里

也就是说:

  • 每个服务都在做一部分运行决策
  • 没有一个统一的"全局视角"
  • 没有一个持续对齐状态的控制系统

3.6 为什么此时引入 K8s 是"必然选择"

所以,在这个阶段引入 Kubernetes,并不是因为:

  • 技术更新
  • 架构流行
  • 工具更炫

而是因为:

原有的"应用自理型运行模式",已经无法支撑系统规模继续增长

K8s 带来的,不是"替换某个组件",而是:

把"如何运行应用"这件事,从应用中剥离出来,交给一个统一、自动、可控的系统


四、引入 K8s 后,系统发生了什么根本变化?

一句话先给出结论(这是整篇里非常重要的一句话):

Kubernetes 接管了「服务如何存在」,
微服务框架退回到「服务如何协作」。

这不是"谁取代谁",而是一次系统职责的重新分工


4.1 从"应用自理"到"平台托管"的根本转变

在引入 K8s 之前,我们已经看到:

应用既要干业务,又要操心自己怎么活

而 K8s 出现后,最核心的一件事发生了变化:

"实例是否存在、是否健康、是否需要重建"
不再由应用决定,而由平台统一负责

这一步,是整个架构发生质变的起点。


4.2 服务是如何"存在"的:从注册中心 → Pod 生命周期

4.2.1 纯微服务时代的"存在方式"

之前,一个服务实例的存在依赖于:

  • 应用进程是否启动
  • 是否成功注册到注册中心
  • 心跳是否被正确感知

👉 "存在"是一个应用行为


4.2.2 K8s 时代的"存在方式"

在 K8s 中,一个服务实例(Pod)的存在由以下机制保证:

  • 你声明:我要 3 个副本
  • K8s 保证:任何时候都有 3 个 Pod 在运行
  • Pod 挂了 → 自动重建
  • Node 挂了 → 自动迁移

可以这样理解:

Pod 的"生死",不再属于应用,而属于平台

应用只需要回答一个问题:

"我被启动后,该怎么处理请求?"


4.3 实例感知方式的变化:从"我去找你"到"平台给你一条路"

4.3.1 之前:应用必须"知道所有实例"

在纯微服务体系中:

  • 应用需要:

    • 从注册中心拉实例列表
    • 缓存
    • 失效
    • 自己做负载均衡
  • 调用方直接感知 IP + Port

这是强感知、强耦合的方式。


4.3.2 现在:应用只面对 Service

在 K8s 中,服务访问的基本形态变成:

text 复制代码
服务 A → Service → Pod

这里的 Service 是 K8s 的原生对象,它提供:

  • 稳定的虚拟 IP
  • 稳定的 DNS 名称
  • 自动关联后端 Pod

应用只需要记住一件事:

我只访问 Service,不关心后面有多少 Pod、在哪台机器


4.4 负载均衡发生了"位置迁移"

这是一个非常容易被忽略、但极其关键的变化。

4.4.1 以前:负载均衡在应用内部

之前:

  • Ribbon 在 JVM 内

  • 每个应用实例都在做:

    • 实例选择
    • 失败重试
    • 列表更新

👉 负载逻辑分散在所有服务里


4.4.2 现在:负载均衡下沉到平台层

在 K8s 中:

  • Service + kube-proxy
  • 通过 iptables / IPVS
  • 节点层面完成转发

也就是说:

负载均衡从"应用逻辑",变成了"基础设施能力"

应用层只负责:

  • 发请求
  • 处理响应

4.5 故障判断权的转移:从"我感觉不行了"到"平台说你不行了"

4.5.1 应用视角的局限性

在没有 K8s 时:

  • 应用通过:

    • 超时
    • 异常
    • 错误率
  • 来"猜测"对方是否健康

但应用只能看到请求结果,而看不到:

  • 进程是否 OOM
  • Node 是否异常
  • 网络是否抖动

4.5.2 K8s 的全局视角

K8s 判断 Pod 是否健康,基于的是:

  • 进程状态
  • 探针(存活 / 就绪)
  • Node 状态
  • 实际运行情况

于是出现了一个本质变化:

"是否存活"由平台判断,
"是否可用"由应用决定

这是一个非常清晰、非常合理的职责边界。


4.6 微服务框架的角色变化(非常关键)

引入 K8s 后,很多人会产生疑问:

那微服务框架是不是就没用了?

答案恰恰相反。

4.6.1 微服务框架"退回本职工作"

在 K8s 体系下,微服务框架更专注于:

  • 接口治理
  • 调用规范
  • 限流策略
  • 熔断与降级
  • 业务级容错

而不再需要关心:

  • 实例在哪
  • 实例怎么扩
  • 实例挂了怎么办

4.7 一张"前后对照"的核心变化表(强记忆点)

可以用一句对照来记忆:

以前:
应用 = 业务 + 运行逻辑
现在:
应用 = 业务逻辑
平台 = 运行逻辑


4.8 总结

请牢牢记住这句话,它贯穿后面所有章节:

K8s 并不是替代微服务框架,
而是把"应用不该操心的事",统一收回到平台层。


五、K8s 介入后,各组件如何重新分工

这一章我们继续沿着"一次请求的完整路径"来看,但你会明显发现一件事:

请求路径变得更短了,
应用承担的责任也变少了。

这不是功能减少,而是职责被重新放回到更合适的位置


5.1 流量入口:Ingress + Gateway 的分层协作

5.1.1 K8s 时代的完整入口路径

在 K8s 中,一个外部请求进入系统,典型路径是:

text 复制代码
用户
 ↓
Ingress / LoadBalancer
 ↓
Gateway Service
 ↓
Gateway Pod

这里涉及两个层级的"入口",但职责完全不同。


5.1.2 Ingress:集群级流量入口

Ingress 的角色可以总结为一句话:

把"集群外的流量",安全、稳定地送进集群

它负责的事情包括:

  • 域名与路径规则
  • TLS 终止
  • 七层转发
  • 对接云负载均衡或边缘入口

可以把 Ingress 理解为:

K8s 集群的"城门"

它不关心业务,只关心:

  • 流量从哪来
  • 要进哪个 Service

5.1.3 Gateway:应用级流量治理中心

Ingress 把流量送进来之后,真正的业务入口,仍然是:

  • Spring Cloud Gateway

Gateway 在 K8s 中的职责没有减弱,反而更纯粹

  • 业务路由
  • 鉴权
  • 灰度规则
  • 应用级限流

于是边界变得非常清晰:

Ingress 解决"怎么进集群",
Gateway 解决"进来后怎么走业务逻辑"。


5.2 服务发现:Service + kube-proxy 全面接管

这是K8s 介入后最根本、最关键的变化


5.2.1 为什么原来的服务发现模式行不通了

在 K8s 中,有一个必须接受的事实:

Pod 是"随时可能消失、重建、迁移"的

因此:

  • Pod IP 不稳定
  • 实例数量动态变化
  • Node 随时可能被剔除

这意味着:

任何基于"实例 IP 稳定性"的注册模式,都会天然失效


5.2.2 Service:稳定抽象的核心

K8s 引入了 Service 这个核心抽象,它提供:

  • 稳定的虚拟 IP(ClusterIP)
  • 稳定的 DNS 名称
  • 自动关联后端 Pod

应用之间的调用关系,变成:

text 复制代码
服务 A
 ↓
访问 service-b
 ↓
Service
 ↓
kube-proxy
 ↓
某一个 Pod

应用只记住一件事:

"我访问的是 Service,不是某个实例。"


5.2.3 kube-proxy:真正的"流量分发者"

kube-proxy 运行在每个 Node 上,负责:

  • 监听 Service 与 Pod 变化
  • 维护后端 Pod 列表
  • 通过 iptables / IPVS 转发流量

本质上:

负载均衡从"应用代码",下沉到了"节点网络层"。

这是一次极其重要的职责迁移


5.3 那 Nacos 还要不要?------角色必须收缩

这是很多团队在实践中最容易纠结的一点。

5.3.1 结论先行(非常重要)

K8s 场景下的推荐做法是

Nacos 只做配置中心,不再作为注册中心


5.3.2 为什么不能"双注册"

如果你同时使用:

  • K8s Service 做服务发现
  • Nacos 做实例注册

必然会出现:

  • 两套实例视图
  • 状态不同步
  • 故障判断冲突

根本原因在于:

"实例的最终生死权",已经交给了 K8s

注册中心再介入,只会制造混乱。


5.4 Feign / Ribbon:从"决策者"退回"调用工具"

5.4.1 Feign:依然有价值

Feign 在 K8s 中仍然非常有用:

  • 接口抽象
  • 参数映射
  • 调用封装
  • 统一规范

它依然负责:

"如何发起一次服务调用"


5.4.2 Ribbon:角色发生本质变化

Ribbon 的核心职责已经被削弱:

  • 不再拉取实例列表
  • 不再做真正的负载均衡
  • 不再感知 Pod 变化

Feign 的调用目标,直接变成:

text 复制代码
http://order-service

真正的流量分发发生在:

Service + kube-proxy 层


5.5 Sentinel / Hystrix:依然不可或缺,但边界更清楚了

5.5.1 K8s 能解决什么,不能解决什么

K8s 负责的是:

  • Pod 挂了 → 重建
  • Node 挂了 → 迁移
  • 副本数不对 → 补齐

但 K8s 不关心

  • 接口是不是慢
  • 某个依赖是不是抖
  • 局部是否已经不可用

5.5.2 微服务治理框架的真实定位

因此:

  • Sentinel
  • Hystrix(旧体系)

仍然非常重要,它们负责:

  • 接口级限流
  • 熔断
  • 降级
  • 业务兜底

可以用一句话准确区分:

K8s 管的是"活不活",
微服务治理管的是"好不好用"。


5.6 把新的分工体系一口气串起来

现在,我们把 K8s + 微服务 的完整协作路径串成一条线:

text 复制代码
用户
 ↓
Ingress(集群入口)
 ↓
Gateway(业务入口)
 ↓
Feign(调用封装)
 ↓
Service(稳定访问点)
 ↓
kube-proxy(流量分发)
 ↓
Pod(业务实例)
 ↓
Sentinel / Hystrix(业务级保护)

你会清楚地看到:

平台负责"运行与调度",
应用专注"业务与协作"。


5.7 总结

请记住这句高度抽象、但极其准确的总结:

K8s 不是让微服务变复杂,
而是把"不该由微服务操心的复杂性",
统一收回到平台层。


六、一个"推荐的完整协作流程"

这一章非常重要,因为它不是在讲"概念",而是在回答一个现实问题:

在微服务 + K8s 体系下,一次请求,最合理、最稳定、最可扩展的流转方式是什么?

下面这条链路,你可以直接当成:

长期可演进的系统设计蓝本


6.1 推荐的请求全流程(先给结论)

text 复制代码
用户
 ↓
Ingress(流量进入集群)
 ↓
Gateway(鉴权 / 路由 / 限流)
 ↓
Service(稳定发现)
 ↓
kube-proxy(转发)
 ↓
Pod(服务实例)
 ↓
Sentinel(调用保护)
 ↓
Controller(实例兜底)

接下来,我们逐层拆解,说明每一层"为什么必须存在、不能省"。


6.2 Ingress:把"外部世界"与"集群内部"彻底隔离

6.2.1 Ingress 解决的不是业务,而是"边界问题"

Ingress 的核心职责只有一句话:

负责"集群外 → 集群内"的流量接入

它做的事情包括:

  • 域名、路径规则
  • TLS 终止
  • 与云负载均衡或边缘入口对接

可以这样理解:

Ingress 是集群的"对外接口规范层"


6.2.2 为什么不让 Gateway 直接暴露到公网

如果 Gateway 直接对外暴露:

  • Gateway 必须关心网络暴露方式
  • 必须处理证书、入口流量抖动
  • 扩容与入口强绑定

而 Ingress 的存在,让你可以做到:

业务入口稳定,入口实现可替换


6.3 Gateway:真正的"业务入口中枢"

6.3.1 Gateway 在 K8s 中的定位更清晰了

这里的 Gateway,通常是 Spring Cloud Gateway

它在 K8s 中只做业务相关的事

  • 路由到哪个服务
  • 是否有访问权限
  • 是否需要限流
  • 是否走灰度规则

可以用一句话概括:

Gateway 只关心"业务请求怎么走",不关心"实例在哪"


6.3.2 一个典型例子

比如:

  • /api/order/** → order-service
  • /api/user/** → user-service

Gateway 并不知道:

  • order-service 有几个 Pod
  • Pod 跑在哪台 Node

它只需要知道:

"order-service 这个名字是稳定的"


6.4 Service:整个体系中最容易被低估的一层

6.4.1 Service 是"服务发现"的最终形态

在 K8s 中,Service 提供了三样极其关键的能力:

  • 稳定虚拟 IP
  • 稳定 DNS 名称
  • 自动维护后端 Pod 列表

这意味着:

服务发现,从"应用协议",变成了"平台能力"


6.4.2 为什么 Service 是微服务的"地基"

一旦所有服务只通过 Service 互相访问:

  • Pod 可随时重建
  • 实例可任意扩缩
  • Node 可自由迁移

而应用层:

完全无感


6.5 kube-proxy:负载均衡真正发生的地方

6.5.1 kube-proxy 在做什么

kube-proxy 运行在每个 Node 上,负责:

  • 监听 Service / Pod 变化
  • 维护转发规则
  • 将请求分发给后端 Pod

底层实现可能是:

  • iptables
  • 或 IPVS

但无论哪种,本质都是:

在网络层完成负载均衡


6.5.2 一个关键变化(必须理解)

以前:

负载均衡 = 应用逻辑

现在:

负载均衡 = 基础设施能力

这是系统稳定性的一个巨大提升点。


6.6 Pod:业务真正发生的地方

6.6.1 Pod 不再需要"证明自己活着"

在这条链路中,Pod 的职责非常纯粹:

  • 接收请求
  • 执行业务逻辑
  • 返回结果

不需要

  • 注册自己
  • 发送心跳
  • 感知其他实例

因为:

"我活不活"已经由 K8s 负责


6.7 Sentinel:最后一道"业务级安全网"

6.7.1 为什么 K8s 之后仍然需要 Sentinel

Sentinel 解决的是 K8s 刻意不解决的问题

  • 接口是否慢
  • 下游是否抖动
  • 局部是否过载

Sentinel 提供的是:

  • 接口级限流
  • 熔断
  • 降级
  • 快速失败

可以理解为:

这是"业务调用层"的安全网


6.8 Controller:真正的"兜底之王"

6.8.1 Controller 干的不是"救请求",而是"救系统"

在请求链路的最末端,还有一个不直接参与请求、但决定系统稳定性的角色:

  • Deployment Controller
  • ReplicaSet Controller
  • Node Controller

它们做的事情是:

  • Pod 少了 → 补
  • Pod 挂了 → 重建
  • Node 不行了 → 迁移

所以可以这样理解:

Controller 不救某一次请求,
但保证系统长期一定可用


6.9 把整条"推荐协作链路"一次记住

你可以用这句话来快速复述整套体系:

Ingress 把流量送进来,
Gateway 决定业务怎么走,
Service 提供稳定访问点,
kube-proxy 分发请求,
Pod 执行业务,
Sentinel 兜住抖动,
Controller 保证实例始终存在。


6.10 总结

这是一个可以直接写进设计文档的结论:

微服务 + K8s 的最佳协作方式,不是"谁替代谁",
而是让平台负责"运行确定性",
让应用专注"业务不确定性"。


七、服务不可用时,谁在兜底?

在微服务 + K8s 协作体系中,故障处理是分层的、职责明确的

每一层只兜自己能管的事,避免重复处理或遗漏。

故障类型 处理者 原理解析与示例
容器崩溃 kubelet 节点级别的"管家" 。kubelet 监控容器进程状态,如果发现进程退出或 OOM,会立即调用容器运行时重启容器。 例如:order-service 容器因内存溢出退出,kubelet 在 5 秒内自动拉起新容器。
Pod 消失 Controller 期望状态守护者 。Controller(如 Deployment Controller)持续对比期望状态 vs 实际状态,发现副本不足时会创建新的 Pod。 例如:声明 3 个副本,实际 2 个存活 → Controller 自动补齐 1 个。
Node 宕机 Node Controller 节点级健康管理 。Node Controller 发现 Node 不可达,会剔除该 Node 上的 Pod,并触发 Controller 重新调度到其他健康 Node。 例如:某台 Node 宕机,Pod 自动迁移到其他 Node,不影响业务请求(通过 Service + kube-proxy)。
实例不足 HPA + Controller 弹性扩缩容组合拳 。HPA 根据指标(CPU、内存、QPS)调整副本数,Controller 保证期望副本在集群中存在。 例如:订单高峰,CPU 使用率超过 80%,HPA 指令增加副本 → Controller 创建新 Pod。
接口慢 Sentinel 业务级调用保护 。Sentinel 对接口请求速率、响应时间进行监控,当接口延迟超过阈值时,限流或熔断,避免雪崩。 例如:user-service 响应超过 1s,Sentinel 限流 50% 流量,保障系统整体稳定。
下游异常 熔断 / 降级 局部服务自救 。当下游系统不可用时,调用方触发熔断或返回降级方案(fallback),防止故障传导。 例如:支付服务接口异常,订单服务返回默认支付状态而不阻塞主流程。
流量洪峰 Gateway 入口级防护 。Gateway 对请求进行限流、熔断、鉴权,防止瞬时洪峰冲垮内部服务。 例如:秒杀活动瞬间 10 万请求涌入,Gateway 对非 VIP 用户进行限流,保证系统可用。

7.1 分层兜底原则

  1. 节点层:kubelet → 保证容器存活
  2. Pod / 副本层:Controller → 保证期望副本数量
  3. 集群 / 节点健康层:Node Controller → 保证节点健康、Pod 迁移
  4. 弹性层:HPA → 按指标自动扩缩容
  5. 接口调用层:Sentinel / 熔断 → 保证请求可控
  6. 入口层:Gateway → 保证流量可控

口诀记忆法

"容器自生,Pod 自补,节点迁移,副本扩,接口限,调用降,入口稳"。

每个动作对应一行表格,顺口又容易串联。


7.2 场景串联示例

假设秒杀场景:

  1. 秒杀开始,流量瞬间涌入 → Gateway 限流
  2. 部分 Pod CPU 飙升 → HPA 扩容
  3. 某 Pod 因 OOM 崩溃 → kubelet 重启
  4. Node 宕机 → Node Controller 调度 Pod
  5. 下游库存接口慢 → Sentinel 限流 / 熔断
  6. 支付服务异常 → 降级返回默认结果

整条链路无缝协作,系统稳健,应用只专注业务逻辑


八、最终总结

如果你只能记住一段话,那就记住:

K8s 负责让服务"存在且正确",
微服务框架负责让服务"协作且克制"。
不要让应用操心实例,
不要让平台理解业务。


8.1 核心理解:职责下沉 + 分层清晰

这句话背后的深意:

  1. K8s 负责"活着"

    • Pod 挂了 → Controller 自动补
    • 容器崩 → kubelet 自动拉起
    • Node 宕 → Node Controller 调度 Pod
    • 扩缩容 → HPA + Controller 自动完成
  2. 微服务负责"好用"

    • 接口慢 → Sentinel 限流 / 熔断
    • 下游异常 → 降级 / fallback
    • 灰度、路由、鉴权 → Gateway 管理

一句话理解:平台解决"存在性",应用解决"业务健康"。


8.2 功能性变化的组件总结

组件 在传统微服务的作用 在 K8s 下的作用/变化 原理解释
注册中心(Nacos) 服务注册与发现,负载均衡 只做配置中心,不再做注册与发现 Pod IP 不稳定,注册中心重复管理会冲突
客户端负载均衡(Ribbon) JVM 内选实例 不再负载均衡,Feign 直接访问 Service kube-proxy + Service 网络层 LB 已取代
kube-proxy 负责将 Service 请求分发到 Pod 网络层维护 Pod 列表,自动更新规则
Sentinel / Hystrix 限流、熔断、降级 职责保持不变,仍管理业务健康 平台保证"存在",应用管"好不好用"
Gateway 业务路由、限流、鉴权 职责保持不变,站在最外层 Ingress 提供集群入口,Gateway 专注业务治理
Deployment / Controller 负责 Pod 数量、自愈 持续对比期望状态 vs 实际状态,保证稳定副本数

8.3 为什么这些改变重要?

  1. 注册中心退位

    • 不再是实例管理者,避免状态冲突
    • K8s 提供更稳定、更实时的 Service 发现与负载均衡
  2. kube-proxy 替代 Ribbon

    • 负载均衡下沉到网络层,消除应用层复杂逻辑
    • Pod 弹性扩缩容时应用无需感知
  3. 微服务治理框架不可替代

    • Sentinel 管的是"慢与异常",K8s 不处理
    • 保持业务调用安全和局部稳定
  4. 网关必须站在最外层

    • 边界清晰,统一做鉴权、路由、限流
    • 避免每个服务重复实现流量控制
  5. 分层清晰 = 系统稳定

    • 平台只管存在性
    • 应用只管协作
    • 每层兜自己该兜的,故障不会互相传递

8.4 场景串联记忆法

假设秒杀场景:

  1. 流量大 → Gateway 限流
  2. Pod CPU 高 → HPA 自动扩容
  3. Pod 崩 → kubelet 重启
  4. Node 宕 → Node Controller 调度 Pod
  5. 下游接口慢 → Sentinel 限流 / 熔断

每一层只管自己能管的事 → 系统整体稳,应用只关心业务。


8.5 本章一句话高度抽象

K8s 负责"活着",微服务负责"好用",
分层越清晰,系统越稳,运维越轻松,业务越专注。

这就是你现在真正理解的微服务 + K8s 协作哲学


相关推荐
刘一说2 小时前
拒绝 500 与 404:Spring Boot 全局异常处理机制深度解析与常见 API 错误避坑指南
spring boot·后端·状态模式
苏三说技术2 小时前
RabbitMQ和RocketMQ,哪个更好?
后端
摸鱼的春哥2 小时前
Agent🤖记忆的提取与压缩!再也不担心我的Agent记忆混乱了
前端·javascript·后端
代龙涛4 小时前
WordPress 主题初体验:从 style.css 到 index.php、single.php 简单实战
后端·php·wordpress
zzb158010 小时前
RAG from Scratch-优化-query
java·数据库·人工智能·后端·spring·mybatis
必胜刻12 小时前
RESTful 基础:资源、路径与方法对应关系详解
后端·restful
XPoet12 小时前
AI 编程工程化:Hook——AI 每次操作前后的自动检查站
前端·后端·ai编程
J2虾虾12 小时前
在SpringBoot中使用Druid
java·spring boot·后端·druid
程序员小假12 小时前
为什么要有 time _wait 状态,服务端这个状态过多是什么原因?
java·后端