关于K8s的Service存活探针和就绪探针的AI问答

如果一个k8s的cluster ip类型的service的目标是一个deployment,这个deployment有多个replica,其中一个replica的pod关闭了,service还会向这个pod转发流量吗

你想知道 K8s 中 ClusterIP 类型 Service 在后端 Deployment 的某个 Pod 关闭后,是否还会向这个已关闭的 Pod 转发流量,答案是:不会(正常情况下)

要理解这个过程,核心是搞清楚 K8s 的两个关键组件如何协同工作:EndpointSlice(早期是Endpoints)和kube-proxy

具体工作流程

  1. Pod 的生命周期与标签选择器ClusterIP Service 并不是直接关联 Deployment,而是通过 ** 标签选择器(label selector)** 匹配对应的 Pod。Deployment 管理的 Pod 会带有统一的标签,Service 正是通过这个标签找到目标 Pod。

    当其中一个 Pod 被关闭(无论是手动删除、资源不足被驱逐、容器崩溃等),这个 Pod 的状态会变为Terminating(终止中),最终变为Terminated或直接被从集群中移除。

  2. EndpointSlice/Endpoints 的自动更新 K8s 的控制平面中有一个组件会持续监控:Service 对应的标签选择器匹配到的 Pod 是否处于「健康可用」状态

    • 这个组件会过滤掉状态为非Running、或者未通过「就绪探针(Readiness Probe)」的 Pod。
    • 当某个 Pod 被关闭后,它会被从该 Service 对应的EndpointSlice(或Endpoints)资源中移除。EndpointSlice就相当于 Service 的「后端 Pod 地址清单」,记录了所有可用的 Pod 的 IP 和端口。

    你可以通过以下命令查看 Service 对应的后端 Pod 清单,验证这个过程:

    复制代码
    # 查看Endpoints(适用于所有K8s版本,简单直观)
    kubectl get endpoints <你的service名称> -n <命名空间>
    
    # 查看EndpointSlice(K8s 1.17+推荐,支持大规模Pod场景)
    kubectl get endpointslice -n <命名空间>
  3. kube-proxy 的转发规则更新 集群中的每个节点上都运行着kube-proxy组件,它的核心职责之一就是监听EndpointSlice/Endpoints的变化,并更新节点上的转发规则(iptables 或 ipvs 模式)

    • EndpointSlice中移除了已关闭的 Pod IP 后,kube-proxy会立即更新本地的转发规则,删除指向该失效 Pod IP 的路由。
    • 后续 Service 接收的流量,会按照转发规则只转发给EndpointSlice中剩余的、健康可用的 Pod,不会再转发到已关闭的 Pod。

特殊情况:可能出现短暂的流量转发失败

正常情况下上述流程是自动且快速的,但在某些场景下,可能会出现极短暂的「窗口期」,少量流量仍可能被转发到正在关闭的 Pod,导致短暂失败:

  1. Pod 刚进入Terminating状态,但EndpointSlice还未完成更新(毫秒级 / 秒级延迟)。
  2. EndpointSlice已更新,但kube-proxy还未同步更新节点上的转发规则。
  3. 未配置就绪探针(Readiness Probe):如果 Pod 没有配置就绪探针,K8s 只能通过 Pod 的运行状态判断可用性,无法感知 Pod 内部应用是否已经无法处理请求(比如应用已崩溃但 Pod 容器还未退出),这种情况下可能会出现无效转发。

总结

  1. 正常情况下,Pod 关闭后会被从 Service 的EndpointSlice/Endpoints中移除,kube-proxy更新转发规则,Service 不会再向该 Pod 转发流量。
  2. 核心依赖EndpointSlice/Endpoints的自动更新和kube-proxy的规则同步,这是 K8s 服务发现和负载均衡的基础。
  3. 配置就绪探针可以减少特殊场景下的流量转发失败,提升服务的可用性。

这个机制相当于默认实现了nginx的健康探测功能

你这个类比非常精准!K8s Service 结合 就绪探针(Readiness Probe)EndpointSlice/Endpoints 的机制,确实和 Nginx 的健康检查功能异曲同工,核心目标都是只向健康可用的后端实例转发流量

我们可以用表格更直观地对比两者的核心逻辑,帮你加深对底层机制的理解:

对比维度 Kubernetes Service + 就绪探针 Nginx 反向代理 + 健康检查
健康判断主体 K8s 控制平面(kube-controller-manager 中的端点控制器) Nginx 自身进程
健康状态标识 Pod 被加入 / 移出 EndpointSlice(后端地址清单) 后端服务器被标记为 up/down(从负载均衡池中添加 / 移除)
健康检查规则 就绪探针(支持 HTTP/TCP/ 命令行三种方式) 支持 HTTP/TCP/SSL 等多种检查方式
流量转发规则更新 kube-proxy 监听 EndpointSlice 变化,更新节点 iptables/IPVS 规则 Nginx 定期检查后端状态,自动更新内部转发规则
失效实例处理 失效 Pod 被立即移出 EndpointSlice,不再接收流量 失效后端被标记为 down,流量不再转发到该实例

额外补充:就绪探针 vs 存活探针(避免混淆)

很多人会把 K8s 的存活探针(Liveness Probe) 和就绪探针搞混,这里特别提醒:

  • 就绪探针(Readiness Probe) :决定 Pod 是否能接收流量 (直接关联 Service 的 EndpointSlice)。比如 Pod 启动后,应用还在加载配置,此时就绪探针失败,Pod 不会被加入 Service 后端。
  • 存活探针(Liveness Probe) :决定 Pod 是否需要重启。比如应用崩溃,存活探针失败,K8s 会重启 Pod,但不影响 Pod 在 Service 后端的状态(除非重启过程中就绪探针失败)。

实践建议

如果你想让 K8s 的「健康探测」效果和 Nginx 一样稳定,务必为 Deployment 配置就绪探针。示例 YAML 片段如下:

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: my-app:v1
        ports:
        - containerPort: 8080
        # 就绪探针:判断应用是否就绪,能接收流量
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5  # 容器启动后5秒开始探测
          periodSeconds: 10       # 每10秒探测一次
        # 存活探针:判断应用是否存活,需要重启
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 20

配置后,当 Pod 内应用异常时,就绪探针会失败,Pod 会被立即移出 Service 后端,完全避免无效流量转发 ------ 这和 Nginx 健康检查的最终效果一致,且是 K8s 原生实现,无需额外配置反向代理!

接下来,你可以执行第一条操作指令,验证上述机制:

  1. 针对你集群中一个带就绪探针的 Deployment,查看其对应的 Service 的 Endpoints 资源:

    复制代码
    kubectl get endpoints <service-name> -n <namespace>

请执行该命令并反馈输出结果,我们再进行下一步分析。

如果不用就绪探针,只使用默认配置会怎样

你想知道如果不为 Pod 配置就绪探针,仅依赖 K8s 的默认配置,Service 的流量转发机制会表现出怎样的行为,我们来详细拆解这个问题。

简单来说:仅用默认配置(无就绪探针),K8s 的健康判断逻辑会变得非常 "粗糙",只能判断 Pod 的容器是否处于Running状态,无法感知应用内部的可用性,可能会导致无效流量转发、服务可用性下降。

一、 默认配置下的判断逻辑(无就绪探针)

当你不配置readinessProbe时,K8s 会使用默认的就绪状态判断规则

  1. Pod 的所有容器都已成功启动(容器状态为Running)。
  2. 容器没有处于崩溃重启的循环中(即容器启动后稳定运行,没有频繁退出)。

只要满足以上两个条件,K8s 就会认为这个 Pod "就绪",并将其加入对应 Service 的EndpointSlice/Endpoints,允许 Service 向其转发流量;反之,只有当 Pod 容器停止运行(状态变为TerminatingExited等),才会被从EndpointSlice中移除。

二、 这种情况的核心问题(痛点)

这种 "只看容器状态,不看应用状态" 的判断方式,会带来几个典型问题,也是就绪探针要解决的核心场景:

1. 应用启动慢,容器已运行但应用未就绪,接收无效流量

很多应用启动时需要做额外准备工作:加载配置文件、初始化数据库连接、预热缓存、加载大模型 / 静态资源等。

  • 此时 Pod 容器已经是Running状态(默认就绪),会被立即加入 Service 后端。
  • 但应用还没准备好处理请求,此时 Service 转发过来的流量都会失败(返回 500、超时等),直到应用完全启动。

举例:一个 Java Spring Boot 应用,容器启动只需 1 秒,但应用初始化、加载 Bean 需要 10 秒。这 10 秒内,该 Pod 已经在 Service 后端,会接收并丢弃流量。

2. 应用内部异常,但容器仍在运行,持续接收无效流量

这是更常见的问题:应用本身出现故障,但容器进程并没有崩溃,仍处于Running状态。

  • 比如:应用线程池耗尽、数据库连接池满了、接口卡死、无法响应业务请求,但 Java/Go 进程本身还在运行。
  • 此时 K8s 的默认配置无法感知这个问题,会一直认为 Pod 就绪,Service 会持续向这个 "带病" 的 Pod 转发流量,导致部分用户请求失败。

举例 :一个 Web 应用,/api接口已经无法响应,但nginx容器(或应用进程)还在运行,Pod 状态为Running,流量会持续转发到这个 Pod,直到应用进程崩溃或手动删除 Pod。

3. Pod 终止阶段,可能接收更多无效流量

当 Pod 被删除(或被 Deployment 滚动更新)时,会进入Terminating状态:

  • 无就绪探针时,K8s 不会提前将 Pod 从EndpointSlice中移除,而是等到 Pod 容器完全停止后才移除。
  • 而 Pod 在Terminating状态时,K8s 会给 Pod 发送SIGTERM信号,应用开始优雅关闭,但此时仍可能接收 Service 转发的流量,导致这部分流量处理失败。

(如果配置了就绪探针,Pod 进入终止阶段时,就绪探针会先失败,Pod 被提前移出后端,减少无效流量)

三、 补充:默认配置的唯一优势

虽然问题不少,但默认配置也不是完全没用,它的优势是简单、无侵入性

  • 无需在应用中开发健康检查接口(如/health),适合快速测试、简单演示场景。
  • 对于启动极快、无复杂初始化、稳定性极高的简单应用(如静态文件服务、简单脚本),基本可以满足需求。

总结

  1. 无就绪探针时,K8s 仅通过 ** 容器是否Running** 判断 Pod 是否就绪,逻辑粗糙。
  2. 核心痛点是无法感知应用内部状态,可能导致「应用未就绪 / 内部异常,但仍接收流量」,降低服务可用性。
  3. 默认配置仅适合快速测试、简单应用,生产环境建议必配就绪探针。
  4. 就绪探针的核心价值是感知应用内部可用性,让 K8s 的流量转发更精准,和 Nginx 健康检查的核心目标一致。

就绪探针和存活探针是靠请求path属性配置的接口来判断吗(比如上文中出现的"/health"接口)?那如果是的话,接口返回什么样的内容,探针认为是成功的呢?是靠http状态码吗?比如200认为成功,其他认为失败?还是说对response body也有要求,必须按照指定格式返回?

你这个问题问到了探针配置的核心细节,首先明确两个关键点:

  1. 不是所有探针都依赖path接口path只是HTTP 类型探针的配置项,存活探针和就绪探针都支持 3 种探测方式,只有 HTTP 方式才需要配置接口路径;
  2. HTTP 类型探针仅关注「HTTP 状态码」,不关心响应体(response body):只要返回符合要求的状态码,就判定为探测成功,对返回的内容格式、字符串没有任何要求。

下面我们详细拆解,帮你彻底理清这个问题。

一、 先明确:探针的 3 种探测方式(就绪 / 存活探针都支持)

readinessProbe(就绪探针)和livenessProbe(存活探针)是探针的用途 ,而探测的实现方式有 3 种,path只属于其中一种(HTTP Get):

探测方式 配置关键字 是否需要应用提供接口 核心判断逻辑
HTTP Get httpGet 是(需要提供可访问的 HTTP 接口,如/health 检查 HTTP 响应状态码是否在「200-399」区间
TCP Socket tcpSocket 否(仅需要开放指定 TCP 端口) 检查能否成功建立 TCP 连接(端口可访问)
命令执行 exec 否(或仅需要容器内可执行命令) 检查命令执行的退出码是否为0(0 表示成功,非 0 表示失败)

其中,你关注的path就是httpGet方式下的配置项,用于指定 HTTP 接口的路径(比如/health/actuator/health)。

二、 HTTP 类型探针(httpGet):判断成功的标准

核心结论:只看 HTTP 状态码,不关心响应体,具体规则如下:

  1. 成功条件 :探针发送 HTTP 请求后,收到的响应状态码在 200 <= 状态码 <= 399 区间内(包含 200、301、302、399 等),就判定为探测成功。
    • 最常用的就是200 OK(正常响应),这也是绝大多数健康检查接口的返回值。
    • 即使返回302 Found(重定向)、304 Not Modified(缓存命中),K8s 也会认为探测成功(但实际场景中不建议这么做,健康检查接口应尽量返回 200)。
  2. 失败条件
    • 状态码小于 200(如 1xx,极少出现)或大于 399(如 404、500、503);
    • 超时未收到响应(可通过timeoutSeconds配置,默认 1 秒);
    • 无法建立 HTTP 连接(如接口不存在、应用崩溃)。
  3. 关于响应体 :K8s完全不解析、不要求响应体的内容和格式
    • 你返回{"status": "UP"}可以,返回ok可以,甚至返回空内容也可以;
    • 即使返回的 JSON 中statusDOWN,但 HTTP 状态码是 200,K8s 依然会判定为探测成功(这是新手容易踩的坑,核心看状态码,不是响应体内容)。
举个实际例子(有效配置)
复制代码
readinessProbe:
  httpGet:
    path: /health  # 应用的健康检查接口路径
    port: 8080     # 应用的端口
    scheme: HTTP   # 可选,默认HTTP,也可以配置HTTPS
  initialDelaySeconds: 5
  periodSeconds: 10
  • 当应用的http://<pod-ip>:8080/health返回200 OK,无论响应体是什么,探针成功;
  • 当该接口返回503 Service Unavailable,探针失败,Pod 会被移出 Service 后端。

三、 另外两种探针的判断逻辑(补充理解,避免局限)

为了让你不局限于 HTTP 接口,简单了解下另外两种探针的成功标准:

  1. TCP Socket 方式

    • 配置示例:tcpSocket: { port: 8080 }
    • 成功标准:K8s 能成功和 Pod 的 8080 端口建立 TCP 连接(类似telnet <pod-ip> 8080通了);
    • 适用场景:非 HTTP 应用(如 MySQL、Redis、TCP 服务),无需开发额外接口,只需端口开放即可。
  2. Exec 命令执行方式

    • 配置示例:exec: { command: ["cat", "/tmp/healthy"] }
    • 成功标准:容器内执行指定命令后,退出码为 0(Linux/Unix 系统中,退出码 0 表示命令执行成功,非 0 表示失败);
    • 适用场景:自定义判断逻辑,比如检查某个文件是否存在、检查某个进程是否在运行、执行自定义脚本判断应用状态等。

总结

  1. path仅属于 HTTP 类型探针的配置,就绪 / 存活探针都支持 HTTP、TCP、Exec 三种探测方式,并非都需要接口。
  2. HTTP 类型探针只判断 HTTP 状态码(200-399 为成功),对响应体的内容、格式无任何要求。
  3. 实际生产中,HTTP 探针的健康检查接口建议返回200 OK,响应体可携带应用状态(方便人工排查),但不影响 K8s 的探针判断。
  4. 非 HTTP 应用可优先选择 TCP Socket 探针,需要自定义判断逻辑时可选择 Exec 探针。

如果我配置了就绪探针和存活探针(假设path都是"/health"),但是在应用中未开发/health接口,请求/health是404,那岂不是就绪探针和存活探针都是失败的?

你的判断完全正确!这是一个非常典型的新手踩坑场景,如果配置了httpGet类型的探针(路径为/health),但应用中没有开发对应的/health接口,那么请求该接口会返回404 Not Found状态码 ,而404不在 K8s HTTP 探针判定成功的200-399区间内,因此就绪探针和存活探针都会持续探测失败,进而触发对应的 K8s 行为。

我们来详细拆解这种情况下会发生的具体后果,以及对应的解决方案:

一、 探针持续失败的具体后果(就绪 + 存活都失败)

因为就绪探针和存活探针的用途不同,它们失败后触发的 K8s 行为也完全不同,会出现 "双重问题":

1. 就绪探针持续失败的后果
  • Pod 会一直处于「未就绪」状态(通过kubectl get pods查看,Pod 的READY列会显示0/1,而非1/1)。
  • 该 Pod 永远不会被加入对应 Service 的EndpointSlice/Endpoints完全无法接收任何 Service 转发的流量,相当于这个 Pod 是 "闲置" 的,无法提供服务。
  • 如果 Deployment 的replicas配置为 3,K8s 会尝试启动 3 个 Pod,但每个 Pod 的就绪探针都失败,最终会出现 3 个0/1就绪状态的 Pod,整个服务无法对外提供能力。
2. 存活探针持续失败的后果

存活探针有两个关键配置参数(默认值):

  • failureThreshold:探测失败多少次后,判定为 Pod 整体存活失败(默认 3 次)。
  • periodSeconds:每次探测的间隔时间(默认 10 秒)。

/health返回 404 导致存活探针失败时:

  • 连续失败failureThreshold次后,K8s 会判定该 Pod "存活异常",触发Pod 重启
  • 重启后,Pod 容器重新启动,存活探针会再次开始探测,依然返回 404,然后再次失败、再次重启,形成 **"重启循环"**(Pod 状态会反复显示RunningCrashLoopBackOffRunning)。
  • 你可以通过kubectl describe pod <pod名称>查看事件,会看到类似Liveness probe failed: HTTP probe failed with status code: 404Container restarted because liveness probe failed的日志。

二、 解决方案(3 种可选,按需选择)

针对这种情况,有 3 种直接可行的解决方案,覆盖不同场景:

方案 1: 开发/health健康检查接口(推荐,生产环境首选)

这是最规范的做法,为应用开发/health接口,让其返回200 OK状态码(响应体可自定义,不影响探针判断)。

  • 不同语言 / 框架有成熟的健康检查组件,无需手动从零开发:
    • Java Spring Boot:引入spring-boot-starter-actuator,默认提供/actuator/health接口(可配置映射为/health),正常状态下返回200 OK
    • Go Gin/Beego:添加一个GET /health接口,直接返回c.Status(200)
    • Python Flask/Django:添加路由,返回response.status_code = 200
  • 优势:可以在接口中集成自定义判断逻辑(如数据库连接是否正常、缓存是否可用),让探针更精准地感知应用内部状态。
方案 2: 更换探针类型(无需开发接口,快速解决)

如果暂时无法开发/health接口,可以放弃httpGet方式,更换为无需应用额外开发的探针类型:

  • 场景 1:应用是网络服务(有开放端口,如 8080)→ 使用tcpSocket类型

    复制代码
    # 就绪探针(TCP方式)
    readinessProbe:
      tcpSocket:
        port: 8080  # 应用开放的端口
      initialDelaySeconds: 5
      periodSeconds: 10
    # 存活探针(TCP方式)
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

    成功标准:能和 8080 端口建立 TCP 连接即可,无需开发任何接口。

  • 场景 2:无开放端口,或需要自定义判断→ 使用exec类型

    复制代码
    # 就绪探针(Exec方式,检查应用进程是否存在)
    readinessProbe:
      exec:
        command: ["pgrep", "java"]  # 检查java进程是否在运行(适用于Java应用)
      initialDelaySeconds: 5
      periodSeconds: 10
    # 存活探针(Exec方式,同上)
    livenessProbe:
      exec:
        command: ["pgrep", "java"]
      initialDelaySeconds: 15
      periodSeconds: 20

    成功标准:命令执行退出码为 0,无需开发 HTTP 接口。

方案 3: 暂时删除探针配置(仅用于测试 / 演示,不推荐生产)

如果只是快速测试应用,不需要探针的健康检查能力,可以直接删除readinessProbelivenessProbe的配置,依赖 K8s 的默认判断逻辑(容器Running即为就绪 / 存活)。

  • 优势:最快解决重启循环和未就绪问题,快速验证应用核心功能。
  • 劣势:失去健康检查能力,可能出现无效流量转发,不适合生产环境。

总结

  1. 配置/health的 HTTP 探针但未开发对应接口,会返回 404,导致就绪 + 存活探针均失败,触发 Pod 未就绪 + 重启循环。
  2. 生产环境首选开发/health接口 ,可精准感知应用内部状态;快速解决可选择更换 TCP/Exec 探针类型
  3. 探针失败的核心判断依据是:HTTP 探针看状态码(200-399)、TCP 探针看端口连通性、Exec 探针看命令退出码(0)。

我是Spring Boot服务,默认接口是/actuator/health, yaml文件应该怎么配置?

你使用的是 Spring Boot 服务,默认健康检查接口是/actuator/health,需要在 K8s 的 YAML 配置中把探针的path指定为这个接口即可,同时还要注意几个 Spring Boot 的前置小细节,确保探针能正常返回 200 状态码。

一、 前置准备(Spring Boot 端,必做)

首先要确保你的 Spring Boot 项目已经引入了actuator依赖,并且/actuator/health接口可以正常访问、返回 200 状态码:

  1. 添加 Maven 依赖(Gradle 用户可对应转换):

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
  2. 配置 application.yml/application.properties(确保健康接口暴露,且无权限拦截):Spring Boot Actuator 的接口默认需要配置暴露,否则无法访问(会返回 404),最简化配置如下:

yaml

复制代码
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health  # 暴露health接口(如需暴露所有接口可写"*")
  endpoint:
    health:
      show-details: always  # 可选,显示健康检查详情(方便人工排查,不影响K8s探针判断)

配置完成后,启动应用,访问http://localhost:8080/actuator/health,如果返回类似以下内容,且 HTTP 状态码为 200,说明接口正常可用:

json

复制代码
{
  "status": "UP",
  "details": { ... }
}

二、 K8s YAML 配置(核心:指定 path 为 /actuator/health)

下面给出完整的 Deployment YAML 示例(包含就绪探针和存活探针,均指向/actuator/health),你可以直接参考使用,关键是在httpGet中配置path: /actuator/health

yaml

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-boot-app  # 你的应用名称
  namespace: default     # 命名空间,可根据你的需求修改
spec:
  replicas: 3            # 副本数
  selector:
    matchLabels:
      app: spring-boot-app  # 标签选择器,和Pod模板中的标签一致
  template:
    metadata:
      labels:
        app: spring-boot-app  # Pod标签,供Service和Deployment选择
    spec:
      containers:
      - name: spring-boot-app
        image: your-spring-boot-image:v1  # 替换为你的Spring Boot镜像地址
        ports:
        - containerPort: 8080  # Spring Boot应用的端口(默认8080,若你修改过请对应更新)
        # 就绪探针:指向/actuator/health
        readinessProbe:
          httpGet:
            path: /actuator/health  # 核心配置:Spring Boot默认健康检查接口
            port: 8080              # 应用端口,和上面的containerPort一致
            scheme: HTTP            # 默认为HTTP,若你的应用启用HTTPS可改为HTTPS
          initialDelaySeconds: 10   # 容器启动后10秒开始第一次探测(Spring Boot启动略慢,比普通应用稍长)
          periodSeconds: 15         # 每15秒探测一次
          timeoutSeconds: 3         # 探测超时时间3秒
          failureThreshold: 3       # 连续3次失败判定为未就绪
        # 存活探针:同样指向/actuator/health
        livenessProbe:
          httpGet:
            path: /actuator/health  # 核心配置:Spring Boot默认健康检查接口
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 30   # 容器启动后30秒开始第一次探测(给应用足够的初始化时间,避免误判)
          periodSeconds: 20         # 每20秒探测一次
          timeoutSeconds: 3
          failureThreshold: 5       # 连续5次失败触发Pod重启(存活探针可适当放宽失败阈值)
        resources:  # 可选,配置资源限制,避免应用占用过多资源
          requests:
            cpu: 100m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi

三、 关键配置说明(针对 Spring Boot 优化)

  1. initialDelaySeconds 配置优化 :Spring Boot 应用启动需要加载 IOC 容器、初始化 Bean、连接数据库等,比普通轻量应用慢,因此:

    • 就绪探针的initialDelaySeconds建议设置为10-20秒;
    • 存活探针的initialDelaySeconds建议设置为30-60秒,避免应用还没完全启动就被判定为存活失败而重启。
  2. 端口一致性 :确保httpGet中的portcontainerPort一致,且和 Spring Boot 应用的启动端口一致(默认 8080,若你在application.yml中配置了server.port: 8081,则此处需改为 8081)。

  3. 接口访问权限 :如果你的 Spring Boot 应用添加了 Spring Security 等权限框架,需要放行/actuator/health接口,避免探针请求被拦截返回 401/403,示例(Spring Security):

    java

    复制代码
    @Configuration
    public class SecurityConfig {
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            http
                .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/actuator/health/**").permitAll()  // 放行健康检查接口
                    .anyRequest().authenticated()
                );
            return http.build();
        }
    }

四、 验证配置是否生效

  1. 应用上述 YAML 配置:

    kubectl apply -f your-deployment.yaml

  2. 查看 Pod 状态,确认READY列为1/1(就绪探针成功),且无重启循环(存活探针成功):

    kubectl get pods

  3. 查看 Pod 详情,验证探针是否正常:

    kubectl describe pod <你的pod名称>

在输出结果中找到Events部分,若看到类似Readiness probe succeededLiveness probe succeeded的日志,说明探针配置生效,且/actuator/health接口正常返回 200 状态码。

总结

  1. 核心配置是将探针的httpGet.path指定为/actuator/health,并保证端口与 Spring Boot 应用一致。
  2. Spring Boot 端需引入actuator依赖并暴露health接口,避免接口 404。
  3. 针对 Spring Boot 启动较慢的特性,适当调大initialDelaySeconds,避免探针误判。
  4. 若有权限框架,需放行/actuator/health接口,确保探针请求能正常返回 200 状态码。

详细解读以下配置: # 就绪探针:指向/actuator/health readinessProbe: httpGet: path: /actuator/health # 核心配置:Spring Boot默认健康检查接口 port: 8080 # 应用端口,和上面的containerPort一致 scheme: HTTP # 默认为HTTP,若你的应用启用HTTPS可改为HTTPS initialDelaySeconds: 10 # 容器启动后10秒开始第一次探测(Spring Boot启动略慢,比普通应用稍长) periodSeconds: 15 # 每15秒探测一次 timeoutSeconds: 3 # 探测超时时间3秒 failureThreshold: 3 # 连续3次失败判定为未就绪 # 存活探针:同样指向/actuator/health livenessProbe: httpGet: path: /actuator/health # 核心配置:Spring Boot默认健康检查接口 port: 8080 scheme: HTTP initialDelaySeconds: 30 # 容器启动后30秒开始第一次探测(给应用足够的初始化时间,避免误判) periodSeconds: 20 # 每20秒探测一次 timeoutSeconds: 3 failureThreshold: 5 # 连续5次失败触发Pod重启(存活探针可适当放宽失败阈值)

你想要详细解读这段 K8s 探针配置,我们先整体说明:这段配置为 Spring Boot 应用分别定义了就绪探针(readinessProbe)存活探针(livenessProbe) ,均采用httpGet方式访问应用的/actuator/health接口,通过不同的参数调优,分别实现「判断 Pod 是否可接收流量」和「判断 Pod 是否需要重启」的核心功能,且针对 Spring Boot 应用的启动特性做了参数优化。

下面我们逐部分、逐参数拆解解读,先讲就绪探针,再讲存活探针,最后对比两者的参数差异和设计逻辑。


第一部分:就绪探针(readinessProbe)解读

就绪探针的核心使命是判断 Pod 内的应用是否已经准备就绪,能够正常接收并处理 Service 转发的流量,只有探针成功,Pod 才会被加入 Service 的后端清单,否则会被排除在外,不接收任何流量。

yaml

复制代码
readinessProbe:
  # 探测方式:HTTP Get请求(针对Spring Boot的actuator健康接口)
  httpGet:
    path: /actuator/health  # 核心配置:指定HTTP请求的路径,对应Spring Boot Actuator的健康检查接口
    port: 8080              # 核心配置:指定HTTP请求的端口,与Spring Boot应用的启动端口、Pod的containerPort保持一致,确保请求能到达应用
    scheme: HTTP            # HTTP请求的协议,默认值就是HTTP,无需额外修改;若应用启用了HTTPS(如端口443),需改为HTTPS,此时会发送HTTPS请求
  # 以下是探针的行为参数(控制探测的时机、频率、容错等)
  initialDelaySeconds: 10   # 「首次探测延迟」:容器启动成功后,等待10秒再执行第一次健康探测
                            # 为什么是10秒?Spring Boot启动需要加载IOC容器、初始化Bean,比轻量应用慢,避免容器刚启动、应用还没就绪就被判定为失败
  periodSeconds: 15         # 「探测间隔」:每次探测完成后,间隔15秒执行下一次探测(无论上一次成功还是失败)
                            # 就绪探针间隔适中即可,15秒既能及时发现应用就绪状态变化,又不会过于频繁消耗集群资源
  timeoutSeconds: 3         # 「单次探测超时时间」:发送HTTP请求后,若3秒内没有收到应用的响应(包括响应超时、连接建立失败),则判定本次探测失败
                            # 健康检查接口通常是轻量接口,3秒足够应对绝大多数场景,避免长时间等待占用探针资源
  failureThreshold: 3       # 「失败阈值」:连续3次探测都失败后,才最终判定Pod「未就绪」
                            # 配置容错次数,避免网络抖动、应用短暂卡顿导致的单次探测失败,造成误判(比如偶尔一次网络延迟,不会直接把Pod移出服务后端)
就绪探针的完整工作流程(以这段配置为例)
  1. Pod 容器启动成功(状态变为 Running),开始等待 10 秒(initialDelaySeconds: 10);
  2. 10 秒后,第一次发送 HTTP GET 请求到http://<pod-ip>:8080/actuator/health
  3. 若 3 秒内收到 200-399 的 HTTP 状态码,本次探测成功,Pod 标记为「就绪」,被加入 Service 后端,后续每 15 秒重复一次探测(持续验证就绪状态);
  4. 若 3 秒内未收到有效响应(或返回 404/500 等状态码),本次探测失败,继续保持「未就绪」状态,15 秒后进行第二次探测;
  5. 若连续 3 次探测都失败(failureThreshold: 3),则永久标记为「未就绪」,不会被加入 Service 后端,直到后续探测成功(恢复就绪状态);
  6. 后续每 15 秒的周期性探测中,若原本就绪的 Pod 突然探测失败,连续 3 次后会被从 Service 后端移出,停止接收流量。

第二部分:存活探针(livenessProbe)解读

存活探针的核心使命是判断 Pod 内的应用是否还「存活」(是否处于正常运行状态),若应用卡死、崩溃无法自愈,就触发 Pod 重启,恢复应用的正常运行,它不影响 Pod 是否接收流量,只负责 Pod 的 "生死重启"。

yaml

复制代码
livenessProbe:
  # 探测方式:与就绪探针一致,HTTP Get请求访问/actuator/health接口
  httpGet:
    path: /actuator/health  # 同样指向Spring Boot的健康检查接口,通过接口状态判断应用是否存活
    port: 8080              # 与应用端口、就绪探针端口一致,确保请求有效性
    scheme: HTTP            # 协议与应用一致,默认HTTP
  # 以下是探针的行为参数(与就绪探针有差异,体现了存活探针的设计逻辑)
  initialDelaySeconds: 30   # 「首次探测延迟」:容器启动成功后,等待30秒再执行第一次健康探测
                            # 为什么比就绪探针长(30秒 vs 10秒)?
                            # 存活探针的目的是判断应用是否卡死,需要给应用足够的完整启动时间(包括初始化数据库连接、加载缓存等),避免应用还在启动中就被误判为"存活失败"而重启
  periodSeconds: 20         # 「探测间隔」:每次探测完成后,间隔20秒执行下一次探测
                            # 比就绪探针间隔稍长(20秒 vs 15秒),因为存活状态的变化比就绪状态更缓慢,无需过于频繁探测,减少集群资源消耗
  timeoutSeconds: 3         # 「单次探测超时时间」:与就绪探针一致,3秒内未收到有效响应则判定本次探测失败
                            # 健康检查接口轻量,3秒足够,保持配置统一即可
  failureThreshold: 5       # 「失败阈值」:连续5次探测都失败后,才最终判定Pod「存活失败」,触发Pod重启
                            # 为什么比就绪探针容错更高(5次 vs 3次)?
                            # 存活探针的后果是「重启Pod」,代价较大(会导致该Pod上的连接中断、请求丢失),因此需要更高的容错次数,避免因短暂的网络抖动、应用临时卡顿导致不必要的重启
存活探针的完整工作流程(以这段配置为例)
  1. Pod 容器启动成功,开始等待 30 秒(initialDelaySeconds: 30);
  2. 30 秒后,第一次发送 HTTP GET 请求到http://<pod-ip>:8080/actuator/health
  3. 若 3 秒内收到 200-399 的 HTTP 状态码,本次探测成功,Pod 标记为「存活」,后续每 20 秒重复一次探测(持续验证存活状态);
  4. 若 3 秒内未收到有效响应,本次探测失败,15 秒后进行第二次探测,Pod 暂时保持 Running 状态,不触发重启;
  5. 若连续 5 次探测都失败(failureThreshold: 5),则判定 Pod「存活异常」,K8s 会立即触发 Pod 重启(先发送 SIGTERM 信号让应用优雅关闭,超时后发送 SIGKILL 信号强制终止容器,再重新创建一个新的 Pod 容器);
  6. 新 Pod 启动后,重新执行上述存活探针流程,循环验证。

第三部分:关键补充(两者的参数差异与设计逻辑总结)

这段配置的参数不是随意设置的,而是针对「就绪探针」和「存活探针」的不同使命,做了针对性优化,核心差异和设计逻辑如下:

参数 就绪探针 存活探针 差异原因
initialDelaySeconds 10 30 存活探针需要给应用更完整的启动时间,避免误重启;就绪探针可提前验证,尽快让 Pod 就绪接收流量
periodSeconds 15 20 就绪探针需更及时感知状态变化(快速加入 / 移出服务后端),存活探针状态变化缓慢,可降低探测频率
failureThreshold 3 5 存活探针失败后果是重启 Pod(代价大),需更高容错;就绪探针失败仅移出服务后端(代价小),可快速容错

总结

  1. 该配置采用httpGet方式对接 Spring Boot 的/actuator/health接口,分别实现「判断是否可接收流量」(就绪探针)和「判断是否需要重启」(存活探针)的功能。
  2. 就绪探针参数更 "激进"(延迟短、间隔短、容错低),目的是快速让健康 Pod 上线、异常 Pod 下线;存活探针参数更 "保守"(延迟长、间隔长、容错高),目的是避免不必要的 Pod 重启,保证服务稳定性。
  3. 所有参数均针对 Spring Boot 应用的启动特性优化,避免了探针误判,是生产环境中较为规范的 Spring Boot 应用探针配置。

"若连续 3 次探测都失败(failureThreshold: 3),则永久标记为「未就绪」,不会被加入 Service 后端,直到后续探测成功(恢复就绪状态)"这个有什么意义吗?我没理解连续探测失败就永久标记为"未就绪"和之前判定的"未就绪"有什么区别,下次探测如果就绪了,不也是恢复就绪状态吗?何来永久一说呢?

你这个疑问特别好,戳中了我表述里的不严谨 ------ 这里的「永久标记」并不是真正的 "永久生效、无法恢复" ,而是相对于「单次探测失败」的「临时失败」而言的,更准确的说法应该是「最终判定为未就绪状态(并持续保持该状态,直到后续探测成功恢复)」。

我先纠正这个表述偏差,再详细拆解这个设计的核心意义,帮你理解「为什么要设置failureThreshold(连续失败阈值),而不是单次失败就直接标记未就绪」。

一、 先澄清:「永久标记」的真实含义(消除误解)

这里的「永久」是 **"在后续探测成功之前,持续保持未就绪状态"**,而不是 "一旦标记,永远无法恢复",具体逻辑是:

  1. 单次探测失败:只是「本次失败」,不会立即标记 Pod 为未就绪,探针会按照periodSeconds的间隔继续探测;
  2. 连续failureThreshold次(这里是 3 次)失败:才会「最终敲定」Pod 的未就绪状态,此时 Pod 会被从 Service 后端移出(或不加入);
  3. 后续依然会按照periodSeconds(15 秒)的间隔继续探测,一旦某一次探测成功,就会立即恢复 Pod 的就绪状态,重新加入 Service 后端;
  4. 所谓「永久」,只是相对于「单次失败不生效」而言,强调 "不是临时波动,而是确实验证为未就绪",并非真正的不可恢复。

二、 核心意义:failureThreshold(连续失败阈值)的价值 ------ 避免「误判」

这是 K8s 探针设计的核心容错机制,设置连续 3 次失败才最终判定,而不是单次失败就生效,最大的意义就是过滤短暂的、偶发的异常,避免因 "小波动" 导致不必要的服务抖动

想象一下没有failureThreshold(相当于阈值 = 1)的场景:单次探测失败就直接标记 Pod 未就绪,移出 Service 后端,会出现很多问题,而设置阈值 = 3 就能完美规避这些问题,我们通过具体场景对比理解:

场景 1:网络短暂抖动(最常见)

K8s 集群内部的网络偶尔会有毫秒级 / 秒级的抖动,比如:

  • 节点间网络临时拥塞,探针发送的 HTTP 请求到 Pod 的/actuator/health接口时,短暂超时;
  • 集群 DNS 临时解析延迟,导致探针无法快速连接 Pod IP。

如果阈值 = 1(单次失败就标记未就绪):

  • 这次网络抖动会导致单次探测失败,Pod 被立即移出 Service 后端;
  • 15 秒后下一次探测,网络已经恢复,探测成功,Pod 又被重新加入 Service 后端;
  • 这个过程会导致 Pod "短暂下线又上线",Service 的后端清单频繁变化,甚至可能导致部分请求被丢弃,出现服务抖动。

如果阈值 = 3(连续 3 次失败才标记未就绪):

  • 第一次探测因网络抖动失败,探针继续探测,不做任何状态变更;
  • 15 秒后第二次探测,网络已经恢复,探测成功,整个过程 Pod 的就绪状态不变,依然正常接收流量;
  • 完美过滤了这次偶发的网络抖动,避免了不必要的服务波动。
场景 2:应用短暂卡顿(无实质故障)

Spring Boot 应用运行过程中,可能会有短暂的卡顿,比如:

  • 瞬间处理大量请求,导致线程池暂时耗尽,/actuator/health接口响应延迟(超过 3 秒超时);
  • 应用正在执行定时任务(如缓存刷新),占用大量 CPU,短暂无法响应探针请求。

如果阈值 = 1:

  • 单次卡顿导致探测失败,Pod 被移出 Service 后端,即使卡顿只持续了 1 秒,后续应用恢复正常,也需要等 15 秒后探测成功才能重新上线;
  • 这个过程中,该 Pod 无法接收流量,增加了其他 Pod 的负载压力,甚至可能导致整体服务吞吐量下降。

如果阈值 = 3:

  • 第一次探测因卡顿失败,15 秒后第二次探测,应用卡顿已经结束,能够正常响应,探测成功,Pod 就绪状态不变;
  • 即使卡顿持续了两次探测(30 秒),第三次探测恢复正常,也依然不会标记未就绪,最大程度保证了服务的稳定性。
场景 3:探针请求本身的偶发异常

比如:探针发送的 HTTP 请求被容器内的防火墙短暂拦截、应用的/actuator/health接口短暂被线程阻塞,这些都不是应用的实质故障,只是偶发异常。

  • 阈值 = 3 可以给应用 "容错缓冲",避免因这些非实质性问题导致 Pod 被误下线;
  • 只有当连续 3 次探测都失败,才说明应用确实存在持续的问题(比如真的无法响应请求),此时再标记未就绪,移出 Service 后端,才是合理的决策。

三、 补充:就绪探针的状态变更完整逻辑(再梳理,加深理解)

结合failureThreshold,就绪探针的状态变更不是 "非黑即白",而是有缓冲的,完整逻辑如下:

  1. Pod 启动后,首次探测前:状态为「未就绪」;
  2. 首次探测成功:直接标记为「就绪」,加入 Service 后端;
  3. 若某次探测失败(非连续失败阈值):保持当前状态(就绪→仍就绪,未就绪→仍未就绪),继续周期性探测;
  4. 若连续failureThreshold次失败:「就绪→未就绪」,移出 Service 后端;
  5. 未就绪状态下,后续某次探测成功:直接「未就绪→就绪」,重新加入 Service 后端,无需连续成功多次; 注意:K8s 探针的「恢复逻辑」是「单次成功即可恢复」,而「失败逻辑」是「连续多次失败才判定」,这也是为了快速恢复健康 Pod,保证服务可用。

总结

  1. 「永久标记未就绪」的表述不严谨,真实含义是「连续失败阈值后,持续保持未就绪状态,直到后续单次探测成功恢复」,并非无法恢复。
  2. failureThreshold(连续失败阈值)的核心意义是容错缓冲,避免偶发异常(网络抖动、应用短暂卡顿)导致的误判和服务抖动
  3. 探针的设计逻辑是「失败要谨慎(连续多次才判定),恢复要快速(单次成功即可上线)」,最终目的是保证服务的稳定性和可用性。
  4. 配置 3 次连续失败,是生产环境的最优实践之一,既可以过滤大部分偶发异常,又不会延迟太久才发现真正的应用故障。
相关推荐
fanruitian2 小时前
k8s pv pvc 持久化存储
java·linux·kubernetes
南宫乘风2 小时前
Kubernetes 中如何避免僵尸进程:从原理到 tini 落地实践
云原生·容器·kubernetes
Zfox_3 小时前
【Docker#2】容器化虚拟化
运维·后端·docker·容器
fanruitian3 小时前
k8s 设置副本数
linux·容器·kubernetes
建群新人小猿14 小时前
陀螺匠企业助手——组织框架图
android·java·大数据·开发语言·容器
SilentCodeY14 小时前
iptables 防 Docker 绕过:DOCKER-USER 链限制 Nacos 8848 仅 localhost 可访问
docker·容器·iptables
爱上猫de鱼19 小时前
linux环境docker部署前后端应用
linux·运维·docker
小王努力学编程19 小时前
LangChain—AI应用开发框架(认识模型)
linux·服务器·人工智能·机器学习·容器·langchain
南宫乘风19 小时前
Kubernetes 中 ConfigMap 和 Secret 热更新:Reloader 实战指南
容器·kubernetes·configmap