微服务与 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 协作哲学


相关推荐
IT_陈寒11 小时前
Redis突然吃掉所有内存,我的服务差点挂了
前端·人工智能·后端
鹏程十八少12 小时前
Android TransactionTooLargeException 的真相与修复:从 1.13MB Bundle 到 Binder 内核的完整剖析
前端·后端·面试
geovindu12 小时前
go: Monitor Pattern
开发语言·后端·设计模式·golang·监控模式
ZHOUPUYU12 小时前
PHP 开发实战:从零搭建一个高性能的 RESTful API 服务
运维·开发语言·后端·html·php
身如柳絮随风扬12 小时前
除了 JWT,你还用过哪些认证方案?Spring Security 中如何集成 JWT?
java·后端·spring
techdashen12 小时前
Rust 能帮你捕获什么,又不能捕获什么
开发语言·后端·rust
YOU OU12 小时前
Spring MVC 练习项目
java·后端·spring
Spider Cat 蜘蛛猫1 天前
Springboot SSO系统设计文档
java·spring boot·后端
zyk_computer1 天前
AI 时代,或许 Rust 比 Python 更合适
人工智能·后端·python·ai·rust·ai编程·vibe coding
雨辰AI1 天前
SpringBoot3 项目国产化改造完整流程|从 MySQL 到人大金仓落地
java·数据库·后端·mysql·政务