如果一个k8s的cluster ip类型的service的目标是一个deployment,这个deployment有多个replica,其中一个replica的pod关闭了,service还会向这个pod转发流量吗
你想知道 K8s 中 ClusterIP 类型 Service 在后端 Deployment 的某个 Pod 关闭后,是否还会向这个已关闭的 Pod 转发流量,答案是:不会(正常情况下)。
要理解这个过程,核心是搞清楚 K8s 的两个关键组件如何协同工作:EndpointSlice(早期是Endpoints)和kube-proxy。
具体工作流程
-
Pod 的生命周期与标签选择器ClusterIP Service 并不是直接关联 Deployment,而是通过 ** 标签选择器(label selector)** 匹配对应的 Pod。Deployment 管理的 Pod 会带有统一的标签,Service 正是通过这个标签找到目标 Pod。
当其中一个 Pod 被关闭(无论是手动删除、资源不足被驱逐、容器崩溃等),这个 Pod 的状态会变为
Terminating(终止中),最终变为Terminated或直接被从集群中移除。 -
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 <命名空间> - 这个组件会过滤掉状态为非
-
kube-proxy 的转发规则更新 集群中的每个节点上都运行着
kube-proxy组件,它的核心职责之一就是监听EndpointSlice/Endpoints的变化,并更新节点上的转发规则(iptables 或 ipvs 模式)。- 当
EndpointSlice中移除了已关闭的 Pod IP 后,kube-proxy会立即更新本地的转发规则,删除指向该失效 Pod IP 的路由。 - 后续 Service 接收的流量,会按照转发规则只转发给
EndpointSlice中剩余的、健康可用的 Pod,不会再转发到已关闭的 Pod。
- 当
特殊情况:可能出现短暂的流量转发失败
正常情况下上述流程是自动且快速的,但在某些场景下,可能会出现极短暂的「窗口期」,少量流量仍可能被转发到正在关闭的 Pod,导致短暂失败:
- Pod 刚进入
Terminating状态,但EndpointSlice还未完成更新(毫秒级 / 秒级延迟)。 EndpointSlice已更新,但kube-proxy还未同步更新节点上的转发规则。- 未配置就绪探针(Readiness Probe):如果 Pod 没有配置就绪探针,K8s 只能通过 Pod 的运行状态判断可用性,无法感知 Pod 内部应用是否已经无法处理请求(比如应用已崩溃但 Pod 容器还未退出),这种情况下可能会出现无效转发。
总结
- 正常情况下,Pod 关闭后会被从 Service 的
EndpointSlice/Endpoints中移除,kube-proxy更新转发规则,Service 不会再向该 Pod 转发流量。 - 核心依赖
EndpointSlice/Endpoints的自动更新和kube-proxy的规则同步,这是 K8s 服务发现和负载均衡的基础。 - 配置就绪探针可以减少特殊场景下的流量转发失败,提升服务的可用性。
这个机制相当于默认实现了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 原生实现,无需额外配置反向代理!
接下来,你可以执行第一条操作指令,验证上述机制:
-
针对你集群中一个带就绪探针的 Deployment,查看其对应的 Service 的 Endpoints 资源:
kubectl get endpoints <service-name> -n <namespace>
请执行该命令并反馈输出结果,我们再进行下一步分析。
如果不用就绪探针,只使用默认配置会怎样
你想知道如果不为 Pod 配置就绪探针,仅依赖 K8s 的默认配置,Service 的流量转发机制会表现出怎样的行为,我们来详细拆解这个问题。
简单来说:仅用默认配置(无就绪探针),K8s 的健康判断逻辑会变得非常 "粗糙",只能判断 Pod 的容器是否处于Running状态,无法感知应用内部的可用性,可能会导致无效流量转发、服务可用性下降。
一、 默认配置下的判断逻辑(无就绪探针)
当你不配置readinessProbe时,K8s 会使用默认的就绪状态判断规则:
- Pod 的所有容器都已成功启动(容器状态为
Running)。 - 容器没有处于崩溃重启的循环中(即容器启动后稳定运行,没有频繁退出)。
只要满足以上两个条件,K8s 就会认为这个 Pod "就绪",并将其加入对应 Service 的EndpointSlice/Endpoints,允许 Service 向其转发流量;反之,只有当 Pod 容器停止运行(状态变为Terminating、Exited等),才会被从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),适合快速测试、简单演示场景。 - 对于启动极快、无复杂初始化、稳定性极高的简单应用(如静态文件服务、简单脚本),基本可以满足需求。
总结
- 无就绪探针时,K8s 仅通过 ** 容器是否
Running** 判断 Pod 是否就绪,逻辑粗糙。 - 核心痛点是无法感知应用内部状态,可能导致「应用未就绪 / 内部异常,但仍接收流量」,降低服务可用性。
- 默认配置仅适合快速测试、简单应用,生产环境建议必配就绪探针。
- 就绪探针的核心价值是感知应用内部可用性,让 K8s 的流量转发更精准,和 Nginx 健康检查的核心目标一致。
就绪探针和存活探针是靠请求path属性配置的接口来判断吗(比如上文中出现的"/health"接口)?那如果是的话,接口返回什么样的内容,探针认为是成功的呢?是靠http状态码吗?比如200认为成功,其他认为失败?还是说对response body也有要求,必须按照指定格式返回?
你这个问题问到了探针配置的核心细节,首先明确两个关键点:
- 不是所有探针都依赖
path接口 :path只是HTTP 类型探针的配置项,存活探针和就绪探针都支持 3 种探测方式,只有 HTTP 方式才需要配置接口路径; - 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 状态码,不关心响应体,具体规则如下:
- 成功条件 :探针发送 HTTP 请求后,收到的响应状态码在
200 <= 状态码 <= 399区间内(包含 200、301、302、399 等),就判定为探测成功。- 最常用的就是
200 OK(正常响应),这也是绝大多数健康检查接口的返回值。 - 即使返回
302 Found(重定向)、304 Not Modified(缓存命中),K8s 也会认为探测成功(但实际场景中不建议这么做,健康检查接口应尽量返回 200)。
- 最常用的就是
- 失败条件 :
- 状态码小于 200(如 1xx,极少出现)或大于 399(如 404、500、503);
- 超时未收到响应(可通过
timeoutSeconds配置,默认 1 秒); - 无法建立 HTTP 连接(如接口不存在、应用崩溃)。
- 关于响应体 :K8s完全不解析、不要求响应体的内容和格式 。
- 你返回
{"status": "UP"}可以,返回ok可以,甚至返回空内容也可以; - 即使返回的 JSON 中
status为DOWN,但 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 接口,简单了解下另外两种探针的成功标准:
-
TCP Socket 方式:
- 配置示例:
tcpSocket: { port: 8080 }; - 成功标准:K8s 能成功和 Pod 的 8080 端口建立 TCP 连接(类似
telnet <pod-ip> 8080通了); - 适用场景:非 HTTP 应用(如 MySQL、Redis、TCP 服务),无需开发额外接口,只需端口开放即可。
- 配置示例:
-
Exec 命令执行方式:
- 配置示例:
exec: { command: ["cat", "/tmp/healthy"] }; - 成功标准:容器内执行指定命令后,退出码为 0(Linux/Unix 系统中,退出码 0 表示命令执行成功,非 0 表示失败);
- 适用场景:自定义判断逻辑,比如检查某个文件是否存在、检查某个进程是否在运行、执行自定义脚本判断应用状态等。
- 配置示例:
总结
path仅属于 HTTP 类型探针的配置,就绪 / 存活探针都支持 HTTP、TCP、Exec 三种探测方式,并非都需要接口。- HTTP 类型探针只判断 HTTP 状态码(200-399 为成功),对响应体的内容、格式无任何要求。
- 实际生产中,HTTP 探针的健康检查接口建议返回
200 OK,响应体可携带应用状态(方便人工排查),但不影响 K8s 的探针判断。 - 非 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 状态会反复显示
Running→CrashLoopBackOff→Running)。 - 你可以通过
kubectl describe pod <pod名称>查看事件,会看到类似Liveness probe failed: HTTP probe failed with status code: 404和Container 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。
- Java Spring Boot:引入
- 优势:可以在接口中集成自定义判断逻辑(如数据库连接是否正常、缓存是否可用),让探针更精准地感知应用内部状态。
方案 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: 暂时删除探针配置(仅用于测试 / 演示,不推荐生产)
如果只是快速测试应用,不需要探针的健康检查能力,可以直接删除readinessProbe和livenessProbe的配置,依赖 K8s 的默认判断逻辑(容器Running即为就绪 / 存活)。
- 优势:最快解决重启循环和未就绪问题,快速验证应用核心功能。
- 劣势:失去健康检查能力,可能出现无效流量转发,不适合生产环境。
总结
- 配置
/health的 HTTP 探针但未开发对应接口,会返回 404,导致就绪 + 存活探针均失败,触发 Pod 未就绪 + 重启循环。 - 生产环境首选开发
/health接口 ,可精准感知应用内部状态;快速解决可选择更换 TCP/Exec 探针类型。 - 探针失败的核心判断依据是: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 状态码:
-
添加 Maven 依赖(Gradle 用户可对应转换):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> -
配置 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 优化)
-
initialDelaySeconds配置优化 :Spring Boot 应用启动需要加载 IOC 容器、初始化 Bean、连接数据库等,比普通轻量应用慢,因此:- 就绪探针的
initialDelaySeconds建议设置为10-20秒; - 存活探针的
initialDelaySeconds建议设置为30-60秒,避免应用还没完全启动就被判定为存活失败而重启。
- 就绪探针的
-
端口一致性 :确保
httpGet中的port和containerPort一致,且和 Spring Boot 应用的启动端口一致(默认 8080,若你在application.yml中配置了server.port: 8081,则此处需改为 8081)。 -
接口访问权限 :如果你的 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(); } }
四、 验证配置是否生效
-
应用上述 YAML 配置:
kubectl apply -f your-deployment.yaml
-
查看 Pod 状态,确认
READY列为1/1(就绪探针成功),且无重启循环(存活探针成功):kubectl get pods
-
查看 Pod 详情,验证探针是否正常:
kubectl describe pod <你的pod名称>
在输出结果中找到Events部分,若看到类似Readiness probe succeeded和Liveness probe succeeded的日志,说明探针配置生效,且/actuator/health接口正常返回 200 状态码。
总结
- 核心配置是将探针的
httpGet.path指定为/actuator/health,并保证端口与 Spring Boot 应用一致。 - Spring Boot 端需引入
actuator依赖并暴露health接口,避免接口 404。 - 针对 Spring Boot 启动较慢的特性,适当调大
initialDelaySeconds,避免探针误判。 - 若有权限框架,需放行
/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移出服务后端)
就绪探针的完整工作流程(以这段配置为例)
- Pod 容器启动成功(状态变为 Running),开始等待 10 秒(
initialDelaySeconds: 10); - 10 秒后,第一次发送 HTTP GET 请求到
http://<pod-ip>:8080/actuator/health; - 若 3 秒内收到 200-399 的 HTTP 状态码,本次探测成功,Pod 标记为「就绪」,被加入 Service 后端,后续每 15 秒重复一次探测(持续验证就绪状态);
- 若 3 秒内未收到有效响应(或返回 404/500 等状态码),本次探测失败,继续保持「未就绪」状态,15 秒后进行第二次探测;
- 若连续 3 次探测都失败(
failureThreshold: 3),则永久标记为「未就绪」,不会被加入 Service 后端,直到后续探测成功(恢复就绪状态); - 后续每 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上的连接中断、请求丢失),因此需要更高的容错次数,避免因短暂的网络抖动、应用临时卡顿导致不必要的重启
存活探针的完整工作流程(以这段配置为例)
- Pod 容器启动成功,开始等待 30 秒(
initialDelaySeconds: 30); - 30 秒后,第一次发送 HTTP GET 请求到
http://<pod-ip>:8080/actuator/health; - 若 3 秒内收到 200-399 的 HTTP 状态码,本次探测成功,Pod 标记为「存活」,后续每 20 秒重复一次探测(持续验证存活状态);
- 若 3 秒内未收到有效响应,本次探测失败,15 秒后进行第二次探测,Pod 暂时保持 Running 状态,不触发重启;
- 若连续 5 次探测都失败(
failureThreshold: 5),则判定 Pod「存活异常」,K8s 会立即触发 Pod 重启(先发送 SIGTERM 信号让应用优雅关闭,超时后发送 SIGKILL 信号强制终止容器,再重新创建一个新的 Pod 容器); - 新 Pod 启动后,重新执行上述存活探针流程,循环验证。
第三部分:关键补充(两者的参数差异与设计逻辑总结)
这段配置的参数不是随意设置的,而是针对「就绪探针」和「存活探针」的不同使命,做了针对性优化,核心差异和设计逻辑如下:
| 参数 | 就绪探针 | 存活探针 | 差异原因 |
|---|---|---|---|
| initialDelaySeconds | 10 | 30 | 存活探针需要给应用更完整的启动时间,避免误重启;就绪探针可提前验证,尽快让 Pod 就绪接收流量 |
| periodSeconds | 15 | 20 | 就绪探针需更及时感知状态变化(快速加入 / 移出服务后端),存活探针状态变化缓慢,可降低探测频率 |
| failureThreshold | 3 | 5 | 存活探针失败后果是重启 Pod(代价大),需更高容错;就绪探针失败仅移出服务后端(代价小),可快速容错 |
总结
- 该配置采用
httpGet方式对接 Spring Boot 的/actuator/health接口,分别实现「判断是否可接收流量」(就绪探针)和「判断是否需要重启」(存活探针)的功能。 - 就绪探针参数更 "激进"(延迟短、间隔短、容错低),目的是快速让健康 Pod 上线、异常 Pod 下线;存活探针参数更 "保守"(延迟长、间隔长、容错高),目的是避免不必要的 Pod 重启,保证服务稳定性。
- 所有参数均针对 Spring Boot 应用的启动特性优化,避免了探针误判,是生产环境中较为规范的 Spring Boot 应用探针配置。
"若连续 3 次探测都失败(failureThreshold: 3),则永久标记为「未就绪」,不会被加入 Service 后端,直到后续探测成功(恢复就绪状态)"这个有什么意义吗?我没理解连续探测失败就永久标记为"未就绪"和之前判定的"未就绪"有什么区别,下次探测如果就绪了,不也是恢复就绪状态吗?何来永久一说呢?
你这个疑问特别好,戳中了我表述里的不严谨 ------ 这里的「永久标记」并不是真正的 "永久生效、无法恢复" ,而是相对于「单次探测失败」的「临时失败」而言的,更准确的说法应该是「最终判定为未就绪状态(并持续保持该状态,直到后续探测成功恢复)」。
我先纠正这个表述偏差,再详细拆解这个设计的核心意义,帮你理解「为什么要设置failureThreshold(连续失败阈值),而不是单次失败就直接标记未就绪」。
一、 先澄清:「永久标记」的真实含义(消除误解)
这里的「永久」是 **"在后续探测成功之前,持续保持未就绪状态"**,而不是 "一旦标记,永远无法恢复",具体逻辑是:
- 单次探测失败:只是「本次失败」,不会立即标记 Pod 为未就绪,探针会按照
periodSeconds的间隔继续探测; - 连续
failureThreshold次(这里是 3 次)失败:才会「最终敲定」Pod 的未就绪状态,此时 Pod 会被从 Service 后端移出(或不加入); - 后续依然会按照
periodSeconds(15 秒)的间隔继续探测,一旦某一次探测成功,就会立即恢复 Pod 的就绪状态,重新加入 Service 后端; - 所谓「永久」,只是相对于「单次失败不生效」而言,强调 "不是临时波动,而是确实验证为未就绪",并非真正的不可恢复。
二、 核心意义: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,就绪探针的状态变更不是 "非黑即白",而是有缓冲的,完整逻辑如下:
- Pod 启动后,首次探测前:状态为「未就绪」;
- 首次探测成功:直接标记为「就绪」,加入 Service 后端;
- 若某次探测失败(非连续失败阈值):保持当前状态(就绪→仍就绪,未就绪→仍未就绪),继续周期性探测;
- 若连续
failureThreshold次失败:「就绪→未就绪」,移出 Service 后端; - 未就绪状态下,后续某次探测成功:直接「未就绪→就绪」,重新加入 Service 后端,无需连续成功多次; 注意:K8s 探针的「恢复逻辑」是「单次成功即可恢复」,而「失败逻辑」是「连续多次失败才判定」,这也是为了快速恢复健康 Pod,保证服务可用。
总结
- 「永久标记未就绪」的表述不严谨,真实含义是「连续失败阈值后,持续保持未就绪状态,直到后续单次探测成功恢复」,并非无法恢复。
failureThreshold(连续失败阈值)的核心意义是容错缓冲,避免偶发异常(网络抖动、应用短暂卡顿)导致的误判和服务抖动。- 探针的设计逻辑是「失败要谨慎(连续多次才判定),恢复要快速(单次成功即可上线)」,最终目的是保证服务的稳定性和可用性。
- 配置 3 次连续失败,是生产环境的最优实践之一,既可以过滤大部分偶发异常,又不会延迟太久才发现真正的应用故障。