在 Kubernetes 上跑 Spring Boot 应用,探针配置是绕不开的一环。配置得当,K8s 能准确感知应用状态,自动摘除不健康的 Pod、重启卡死的进程;配置不当,轻则服务抖动、流量损失,重则应用陷入反复重启的死循环。存活探针、就绪探针、启动探针三者各有分工,本文结合 Spring Boot Actuator,详细讲解如何把它们配置好。

一、三种探针的分工与工作原理
Kubernetes 提供了三种探针机制,它们的语义完全不同,混淆它们是大多数配置事故的根源。存活探针(liveness probe)回答的问题是:这个进程还活着吗?如果探测失败,kubelet 会杀掉容器并按照重启策略重新拉起。就绪探针(readiness probe)回答的问题是:这个服务能接流量了吗?探测失败的 Pod 会从 Service 的 endpoints 列表中摘除,但容器本身不会被重启。启动探针(startup probe)是 K8s 1.16 之后引入的,用于判断应用是否已经完成启动,它会在启动期间暂时屏蔽存活和就绪探针。
一个经典的事故场景是:开发者把就绪探针的逻辑错误地放在了存活探针里。应用启动需要 60 秒,而存活探针的 initialDelaySeconds 只设了 10 秒,结果应用还没起来就被判定死亡,被杀掉重启,重启后又因为同样的原因被杀掉,无限循环,Pod 状态一直处于 CrashLoopBackOff。这类问题的正确解法是用启动探针接管慢启动阶段,而不是无限调大存活探针的延迟时间。
反过来,如果只配了存活探针没配就绪探针,会出现另一个问题:应用刚启动,数据库连接池还没初始化完成,但 Service 已经把流量转过来了,用户请求大量超时或报错。就绪探针的价值就在于把流量与真实的服务能力对齐,让 K8s 只把请求转发给真正准备好的 Pod。
二、结合 Spring Boot Actuator 配置探针端点
Spring Boot Actuator 提供了开箱即用的健康检查端点,默认路径是 /actuator/health。直接探测这个端点可以用,但更推荐的做法是启用专门的探针端点。在配置文件中加入以下内容:
management:
endpoints:
web:
exposure:
include: health
endpoint:
health:
probes:
enabled: true
health:
livenessState:
enabled: true
readinessState:
enabled: true
启用之后,Actuator 会额外暴露两个路径:/actuator/health/liveness 和 /actuator/health/readiness。这两个端点与 AvailabilityChangeEvent 机制联动,Spring Boot 会在应用生命周期的不同阶段自动切换可用性状态:应用刷新上下文时进入 ACCEPTING_TRAFFIC 之前的 REFUSING_TRAFFIC 状态,就绪探针返回 503;上下文就绪后再切换为可用,返回 200。这种状态驱动的探针比直接探测聚合健康端点更精准。
这里有一个容易踩的坑需要特别说明:/actuator/health 聚合端点默认会级联检查数据库、消息队列、Redis 等外部组件。如果数据库临时抖动,聚合健康检查会失败。如果把聚合端点接到存活探针上,K8s 会直接重启应用——但重启应用对修复数据库毫无帮助,反而会造成连接风暴。所以务必用细分的 liveness 端点做存活探针,它只反映应用自身的存活状态,不受外部依赖影响。
对应的 Deployment 探针配置如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
replicas: 3
selector:
matchLabels:
app: demo-app
template:
metadata:
labels:
app: demo-app
spec:
containers:
- name: app
image: demo-app:1.0.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 2
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 3
三、关键参数怎么定才合理
探针参数没有放之四海而皆准的数值,但有一套可参考的推导方法。启动探针的容忍时间等于 failureThreshold 乘以 periodSeconds,上面的配置是 30 乘以 2,也就是给应用 60 秒的启动窗口。如果 Spring Boot 应用依赖多、JVM 堆大,冷启动可能要两三分钟,直接把 failureThreshold 调到 90 即可,无需动其他参数。有了启动探针兜底,存活和就绪探针的 initialDelaySeconds 可以干脆不设或设得很小,因为它们要等启动探针成功后才会生效。
就绪探针的探测频率建议比存活探针更激进一些。上面的配置中就绪探针每 5 秒探测一次,失败 3 次摘除流量,意味着一个 Pod 最多 15 秒左右就会被移出负载均衡;恢复后最多 5 秒就会被重新加回。而存活探针设为 10 秒间隔、3 次失败,给应用约 30 秒的自愈窗口,避免因为一次短暂的 Full GC 或网络抖动就触发重启。timeoutSeconds 建议不要设得太大,2 秒足够,健康检查本身应该非常轻量,如果探测请求都要跑好几秒,说明端点设计有问题。
还有一个细节值得注意:Spring Boot 2.3 之前没有内置的探针端点,很多老项目还在用自定义的 Controller 返回固定字符串来做健康检查。这种做法可以工作,但自定义端点往往缺少与应用生命周期的联动,应用在关闭阶段(处理优雅停机时)仍然报告就绪,导致滚动更新时丢请求。如果暂时无法升级 Spring Boot 版本,可以在自定义端点中监听 ContextClosedEvent,收到事件后立刻返回 503,配合 preStop 钩子和 terminationGracePeriodSeconds 实现平滑下线。
四、常见故障的排查思路
当发现 Pod 反复重启时,第一步用 kubectl describe pod 查看事件信息,重点看是 Liveness probe failed 还是 Readiness probe failed。前者伴随容器退出码 137(被 SIGKILL),通常是启动探针缺失或存活探针误检外部依赖;后者 Pod 不重启但流量全无,需要检查应用是否因外部资源不可用而拒绝就绪。
第二步查看应用日志和 Actuator 状态。kubectl logs --previous 能看到上一次容器被杀前的日志,如果日志停在启动阶段,基本可以确认是启动窗口不够。此外,Spring Boot 暴露的 /actuator/health/group/liveness 详情(需要显示 detail)能帮助确认可用性状态的流转是否正常。
最后提醒一点:滚动更新时的探针行为。配置了 maxSurge 和 maxUnavailable 之后,新 Pod 只有就绪探针通过才会替代旧 Pod,如果就绪探针配置错误(比如路径写错返回 404),滚动更新会卡住不动。所以在上线前务必用 kubectl port-forward 本地访问一次探针路径,确认返回码符合预期,这个小习惯能省掉很多排查时间。
Kubernetes探针Spring Boot健康检查liveness probe修改时间:2026-09-09 15:09:18