ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Kubernetes探针(Probe)全解析:核心参数、故障案例与最佳实践

Kubernetes探针(Probe)全解析:核心参数、故障案例与最佳实践

先把话说在前面:Kubernetes 里探针(Probe)这四五个字段,可能是整个 Pod 定义里最不起眼、却最能决定线上稳定性的一小段配置。很多人用 k8s 用了一两年,Deployment、Service、Ingress 摸得滚瓜烂熟,唯独探针这段配置常年靠复制粘贴,改个端口就往上扔。结果就是容器偶尔被误杀、发布时流量被导到没就绪的 Pod、慢启动应用被反复重启,排查到最后发现根因全在探针上。

这篇文章不聊大而全的 Kubernetes 原理,就专注把 k8s 探针这件事讲透。我会从三种探针的分工逻辑讲起,拆解每个检测参数背后的设计意图,给出可落地的配置模板和调优建议,再把我自己踩过和帮别人排查过的典型故障案例整理成速查表。无论你是刚接触 K8s 的运维新手,还是已经被探针折磨过的开发者,这篇内容都能直接拿来用。

1. 先搞明白探针到底在解决什么问题

1.1 没有探针的时候,K8s 是怎么“盲管”应用的

最朴素的容器管理逻辑是这样的:只要容器进程还活着,K8s 就认为这个 Pod 是可用的。但现实中“进程活着”和“服务可用”之间,隔着十万八千里。我见过太多例子:Java 应用进程还在,但线程池已经满到拒绝新请求;Nginx 进程健在,但后端 upstream 全挂,它只能返回 502;数据库连接池被耗尽,应用线程全部阻塞在获取连接上。这些情况下进程都没有退出,可流量打到 Pod 上就是错误。

没有探针时,K8s 对这种情况毫无办法。它不知道你的应用内部发生了什么,也不会主动做任何干预。你只能等用户来投诉,然后手动重启,或者写一堆乱七八糟的外部监控脚本去调 API 删 Pod。这显然不是平台该有的行为——编排平台的核心能力之一,就是自动发现“不健康”的实例并做自愈。探针就是 K8s 给出的答案。

1.2 三种探针,三种完全不同的“健康观”

K8s 提供了三种探针,对应三个不同层面的健康检查视角:

  • 存活探针(livenessProbe):回答“这个容器还活着吗”。失败时 kubelet 会杀掉容器并触发重启策略。它关注的是进程级、内部状态级的问题,比如死锁、内存泄漏导致的假死。
  • 就绪探针(readinessProbe):回答“这个容器能不能接收流量”。失败时 Pod 会从 Service 的 Endpoints 里摘掉,但容器不会被杀死。它关注的是服务可用性,比如依赖组件没准备好、缓存没加载完、流量过载。
  • 启动探针(startupProbe):回答“这个应用启动完成了吗”。它专门用来保护慢启动的应用,在启动探针成功之前,存活探针和就绪探针都不会生效。

三者的失败后果差异很关键:存活探针失败是“杀掉重启”,就绪探针失败是“摘流量不杀”。如果搞混这两者的使用场景,后果相当严重。举个反向例子:有个团队为了“确保服务健康”,把存活探针配得特别敏感,一有波动就重启容器。结果流量高峰期容器被连续重启,Pod 反复 CrashLoopBackOff,整个集群被他们自己玩挂了。存活探针应该是保守的、低频的、只查最致命的指标;就绪探针才应该对业务状态敏感。

1.3 探针能探测的三种检测机制

每种探针都可以用三种方式之一来执行检测:

  • HTTP 请求(httpGet):kubelet 向指定路径发起 HTTP GET 请求,只要返回码在 200~399 之间就算成功。这是最常用的方式,适合所有暴露 HTTP 接口的应用。
  • TCP 连接(tcpSocket):kubelet 尝试建立 TCP 连接,能连上就算成功。适合 Redis、MySQL 这类非 HTTP 协议的组件,或者在应用还没有健康检查端点时做兜底。
  • 执行命令(exec):kubelet 在容器内执行一条命令,以退出码 0 判定成功。适合无法通过端口表达状态、需要综合判断的场景,比如检查某个内部进程是否在跑、配置文件是否完整。

三种机制没有绝对的优劣,核心是匹配应用暴露状态的方式。HTTP 能拿到业务层状态,但前提是你的应用得真的实现了一个有意义的健康检查端点;TCP 最省事,但只能证明“端口能连”,证明不了“业务能用”;exec 最灵活,但要小心命令本身的开销和依赖。后面我会专门展开每种方式的具体坑。

2. 探针参数详解:K8s 是如何判定“成功”和“失败”的

2.1 五个关键参数,构成了一个“欠采样”的判断系统

先看一个典型的探针配置长什么样:

readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 successThreshold: 1

这五个参数的含义分别是:

参数默认值含义
initialDelaySeconds0容器启动后,等待多久才开始第一次探测,给应用留初始化时间
periodSeconds10每隔多少秒探测一次
timeoutSeconds1单次探测的超时时间,超时算本次失败
failureThreshold3连续失败多少次后才判定探针状态翻转(就绪变未就绪 / 存活变不存活)
successThreshold1连续成功多少次后才判定探针状态恢复(仅 readiness 和 startup 需要,liveness 固定为 1)

注意 K8s 的判定是“欠采样”的,不是一次失败就立刻反应。这其实是个很好的设计——它能过滤掉 GC 停顿、网络抖动、偶发超时等瞬时扰动。但反过来,这个设计也意味着探针从“第一次失败”到“真正触发动作”存在时间差。以大流量场景为例,如果一次失败就要立即摘流量,那很容易抖动;但如果失败判定阈值太大,异常流量已经打进来很久了,摘流量的动作才姗姗来迟。后面我会给出实战中的建议阈值。

2.2 一次失败的完整“生命周期”:探针状态机

理解探针判定的关键在于搞清楚“单次探测”和“探针状态”的关系。kubelet 每过一个 periodSeconds 就发起一次探测,得到结果后更新内部的探针状态。判定规则可以概括为:

  • 单次探测结果:成功、失败(含超时)。
  • 连续失败次数达到 failureThreshold 后,探针进入“失败”状态。
  • 连续成功次数达到 successThreshold 后,探针回到“成功”状态。

这里有个容易忽略的点:探针状态是有“记忆”的。比如 readinessProbe 的 failureThreshold 是 3,如果前两次已经失败了,第三次探测成功,那么连续失败计数会清零,探针状态继续维持“就绪”。这意味着,即便应用已经持续异常了两轮探测,只要在触发阈值前恢复,就不会发生摘流量。反过来,一旦连续失败达到阈值,探针进入失败态,之后再恢复,需要连续成功 successThreshold 次才能回到就绪态,而在这期间 Pod 一直处于“未就绪”状态,流量不会进来。

2.3 参数怎么配?核心是理解你的应用“启动曲线”

很多人配置探针参数全凭感觉,我给一个更系统的思路:先画出应用从进程启动到完全可服务的时间曲线。

假设某服务进程启动只要 1 秒,但加载一个 500MB 的缓存文件需要 15 秒。这期间 HTTP 端口已经监听了,但请求进来会因为没有缓存而报错。如果把 readinessProbe 的 initialDelaySeconds 设为 10,periodSeconds 设为 5,那第一次探测发生在 10 秒,恰好命中缓存加载的“临界区”,Pod 会被标记未就绪。这本身没问题,但很可能在 15 秒缓存加载完后,接下来第一次成功的探测才把它重新标记为就绪。如果 periodSeconds 是 2,10 秒时未就绪,12 秒时可能缓存还没好,又失败一次,连续失败超过 threshold,Pod 从 Service 摘除。等到缓存加载完毕再重新就绪,中间会出现几秒到几十秒的可用性空窗。

更麻烦的是 livenessProbe。如果把它也配置在启动早期,initialDelaySeconds 太短,就会在应用还在初始化时被判定失败,然后杀掉容器。启动要 15 秒,但 initialDelay 只留了 5 秒,结果容器每 5 秒被杀一次,永远起不来,这就是典型的 CrashLoopBackOff 陷阱。

正确的做法是区分两种启动场景:

  • 启动很快(3~5 秒内):直接用 livenessProbe + readinessProbe,initialDelaySeconds 给一个略大于启动时间的值即可。
  • 启动很慢(几十秒甚至几分钟):用 startupProbe 保驾护航。startupProbe 成功之前,livenessProbe 和 readinessProbe 不参与判定。startupProbe 的 failureThreshold 要足够大,periodSeconds 可以短一些,确保启动期间不被误杀。启动完成后,liveness 接管“运行时存活管理”,readiness 接管“流量接入管理”,各司其职。

举个我实践过的配置:

startupProbe: httpGet: path: /health/startup port: 8080 periodSeconds: 2 failureThreshold: 30 # 60 秒内允许最多 30 次失败,给慢启动充分余量 timeoutSeconds: 2 livenessProbe: httpGet: path: /health/live port: 8080 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 readinessProbe: httpGet: path: /health/ready port: 8080 periodSeconds: 5 timeoutSeconds: 2 failureThreshold: 1 successThreshold: 1

注意我把三个探针指向了三个不同端点。后面会解释为什么不能共用一个端点。

3. 检测方式选型:HTTP、TCP、exec 各自的适用场景与深坑

3.1 HTTP 探针:最常用,也最容易“假健康”

httpGet 是目前生产环境里最主流的探针方式,核心原因是它最能反映业务层的真实状态。一个设计良好的 /healthz 端点,通常会去检查关键依赖——数据库连接是否可用、缓存是否就绪、磁盘空间是否充足——然后返回 200 或 500。

但这里深坑非常多。

第一,不要只依赖“端口能通”。如果健康检查端点只返回 200 但不做任何内部检查,那它和 TCP 探针没什么区别。很多应用框架的默认 health 端点只检查进程活着,不会检查线程池、队列积压、依赖组件状态。你得自己确认 /healthz 到底检查了什么。

第二,注意返回码范围。K8s 把 200~399 都算成功。也就是说,你的 /healthz 返回 301、302 重定向也会被判定为“健康”。如果应用把根路径 / 重定向到某个登录页,而健康检查路径配置成了 /,那探针会一直成功,但这个应用可能根本没法处理业务请求。我遇到过类似案例,某团队配置 httpGet path: /,Nginx 把它 302 跳转到强制登录页,探针永远成功,直到用户把链路测挂。

第三,不要忽略 scheme 和 httpHeaders。如果你的健康检查端点只允许内网访问,那没问题;但如果端点放在 HTTPS 后面,探针默认走 HTTP,你需要显式配置 scheme: HTTPS。另外很多应用要求自定义 Header(比如 Authorization),可以在 httpHeaders 里加。经常有人忘了这步,导致探针永远收到 401,容器反复重启。

第四,检查路径本身不能太重。有些团队把健康检查做成“全量检查”,把所有依赖都探一遍,结果每次探测耗时好几秒,而 timeoutSeconds 只配了 1 秒。这个后面会展开说。

3.2 TCP 探针:简单粗暴,但只能证明“端口在听”

tcpSocket 是三种方式里最“低水平”的。kubelet 只是去 connect 一下,TCP 握手成功了就算健康。它适合以下场景:

  • 应用不暴露 HTTP 接口,比如 Redis、MySQL、Kafka,没法用 httpGet。
  • 应用处于早期阶段,还没有实现健康检查端点,先拿 TCP 兜底。
  • 探针目标是一个独立组件,端口连通性基本等价于可用性。

但它的问题也很明显:只要端口还在 listen,探针就认为健康,哪怕这个进程已经陷入死锁、无法响应任何请求。TCP 层面的连接成功只能说明内核协议栈还在工作,到不了业务层。

我自己的习惯是:TCP 探针只作为最低限度兜底,但凡应用能提供 HTTP 健康检查端点,一律用 httpGet。如果你现在还在用 tcpSocket 探测一个 Web 应用,强烈建议尽早改造。

3.3 exec 探针:灵活性强,但坑比想象中多

exec 探针给了你最大的自由度:在容器内执行任意命令,退出码为 0 就成功。它的典型场景有两个:

  • 没有网络端口的进程,或者端口本身不能代表可用性。比如一个批处理进程、一个 Agent 类程序,你要检查它内部的某个状态文件是否更新、某个工作协程是否还在跑。
  • 需要在容器内做多级判断,比如检查磁盘使用率、检查子进程健康、检查配置文件是否完整,用一个命令组合表达。

但 exec 探针的坑也最隐蔽。

第一,依赖容器内环境。你的命令可能依赖 shell、依赖某个二进制、依赖容器镜像里的特定路径。镜像一旦做了精简,比如用了 distroless 镜像,里面连 /bin/sh 都没有,exec 探针直接失败。这是很多人从 CentOS 镜像换成 distroless 后容器莫名重启的主要原因。

第二,命令本身会成为额外开销。如果探针命令很重,比如写了一个脚本去扫描日志、查数据库,而 periodSeconds 又很短,这个探针本身就会拖垮容器。我在某个故障现场见过探针脚本里有个 grep 全目录日志的操作,直接把 CPU 打满。

第三,exec 探针有一个特殊用法值得留意:命令可以在容器内主动“杀掉自己”。比如:

livenessProbe: exec: command: - sh - -c - "pgrep -f my-worker-process || exit 1"

如果检测到进程假死或关键线程消失,就返回非 0,让 kubelet 杀掉容器重启。但这里要非常小心:如果命令本身退出码写反了,或者 pgrep 匹配到了探针命令自己,就会造成误杀。我建议 exec 探针的命令越简单越好,不要指望在探针里写复杂的业务逻辑。

3.4 三种机制选型对照表

检测方式判定条件优点缺点推荐场景
httpGet返回码 200~399反映业务层真实状态,可自定义 Header依赖健康检查端点的实现质量所有暴露 HTTP 接口的业务应用
tcpSocketTCP 握手成功简单,不依赖应用侧实现只能证明端口在听,无法反映业务可用性Redis、MySQL、Kafka 等非 HTTP 组件
exec命令退出码为 0自由度最高,可做综合判断依赖容器内环境,额外开销,易误杀无端口进程、需多条件判断的场景

4. 真实故障案例:探针配置引发的连环事故

4.1 慢启动应用被 livenessProbe 反复杀死

我接手过一个模拟系统,应用是个 Java 服务,启动时要加载模型文件,耗时大约 40 秒。运维同事一开始没配 startupProbe,只配了 livenessProbe,initialDelaySeconds 设为 10,periodSeconds 为 5,failureThreshold 为 3。算一下时间线:第 10 秒开始探测,每 5 秒一次,14 秒左右加载还没完成,连续失败 3 次后——大约第 24 秒——kubelet 杀掉容器。这还没完,重启后又一次循环,开始时间更长,永远起不来。K8s 控制台 Event 里全是“Liveness probe failed: HTTP probe failed with statuscode: 503”和“Back-off restarting failed container”。

修复方式就是加 startupProbe,failureThreshold 给足 30 次,periodSeconds 设 2,这样 60 秒的启动窗口足够覆盖 40 秒的加载时间。startupProbe 成功的瞬间,livenessProbe 接管,之后运行期出问题才会重启。

这里我想强调一个细节:很多人以为把 initialDelaySeconds 调大就能解决慢启动。比如给 liveness 配 initialDelaySeconds: 45,确实也能绕过启动窗口。但这样做的代价是:如果应用启动过程中卡死了(比如等待某个远程依赖超时挂起),K8s 要等到 45 秒后才会开始探测,再等几轮失败才会杀容器。而用 startupProbe,最短 2 秒就能探一次,一旦发现启动卡住就能快速失败。一个是“干等”,一个是“主动观察”,效果完全不同。

4.2 就绪探针和存活探针共用端点,导致故障雪崩

这个案例在线上出现过不止一次。某团队图省事,把所有探针都指向同一个 /health 端点。平时流量低没问题,但有一次大促流量暴涨,数据库连接池被占满,所有请求都慢,/health 端点在检查数据库连接时也超时返回 500。readinessProbe 连续失败,Pod 被从 Service 摘除,流量被分摊到其他 Pod;其他 Pod 也没扛住,同样未就绪、同样摘除。按说这是一个正常的缩流量保护机制,问题在于 livenessProbe 也指向同一个 /health,它也失败了。liveness 失败不是摘流量,而是直接杀容器。于是 Pod 被杀、重启、继续打数据库、继续失败、再被杀,形成更大范围的雪崩。

正确的设计必须把“存活”和“就绪”拆成两个端点:

  • /health/live:只做最轻量的检查,比如进程是否活着、关键 goroutine 是否正常。不碰数据库,不碰外部依赖,避免在资源紧张时因为依赖慢而误杀容器。
  • /health/ready:检查一切影响服务可用性的事项,包括依赖状态、线程池水位、缓存是否就绪等。它失败时可以接受,因为后果只是摘流量,不会重启容器。

这个拆分逻辑的本质是:liveness 检查的应该是“不可恢复的致命问题”,readiness 检查的才是“当前可用的业务状态”。牵一发而动全身的因素,不应该放在 liveness 里。

4.3 超时时间太短,探针成了应用性能瓶颈

有个 Web 服务的健康检查端点在正常负载下响应约 600ms,偶尔在 GC 时到 1.5 秒。运维同事把 livenessProbe 配成了 timeoutSeconds: 1,结果 GC 一到就超时失败,连续 3 次失败后容器被杀。一天发生好几次,全是非必要的重启。

这类问题本质上是“探针超时时间与应用响应分布”不匹配。我一般会先看应用的 TP99 响应延迟。如果 TP99 是 800ms,timeoutSeconds 至少给 2 秒,不能拿 TP50 或者平均值去定。另外,探针会把自身的超时请求打到应用上,有一个常被忽略的点:如果探针超时了,kubelet 会直接判定失败,但那个请求可能还在应用侧继续执行,占用连接池。在高并发时,探针自身的流量也会成为压垮应用的最后一根稻草。所以我建议生产环境的探针响应本身要控制在 100ms 以内——健康检查端点里不要做重操作,更不要去查完整数据库,最好只做一个简单的依赖状态缓存查询。

4.4 滚动更新时,就绪探针没来得及摘流量

升级发布的时候,老版本 Pod 被终止,新版本 Pod 还没就绪,但 Service 的 Endpoints 里仍然保留了还没来得及更新的老 Pod。如果就绪探针的 periodSeconds 配得太大,比如 30 秒,那么在一个周期内,某个已终止的 Pod 还会继续接收流量;如果配得太小,又可能因为瞬时抖动频繁摘流量。核心矛盾在于:K8s 的滚动更新策略最大程度的“快速”,而就绪探针的更新频率决定了流量切换的“精确度”。

我见过的问题是:maxSurge 和 maxUnavailable 默认配成 25%,新版本 Pod 的 readiness 一直不通过,老版本又被不断缩容,集群里既没有足够的可用副本,新副本又进不来,造成可用性下降。排查时发现新版本应用启动要 30 秒,而 readiness 的 initialDelaySeconds 还是默认的 0,periodSeconds 5,第一次探测发生在启动后 5 秒,连续失败超过阈值,Pod 长时间处于未就绪状态。调完配置:initialDelaySeconds: 20,failureThreshold: 3,滚动更新立刻顺畅了。

所以发布新版本前,一定要先在测试环境跑一遍探针在真实启动时间下的行为,不要默认“配置一样就不用改”。

4.5 优雅停机与就绪探针的“时间差”

还有一个容易忽略的细节:Pod 进入 Terminating 状态时,readiness 探针会继续运行,直到所有容器退出。如果你的应用在收到 SIGTERM 后立刻停止响应健康检查,但容器真正退出前还有一段优雅关闭期,那么在这段时间里,K8s 认为 Pod 还是就绪的,但实际已经无法处理请求。流量在优雅停机窗口内被打进来,用户报错。

行业里的通行解法是 preStop hook 里加 sleep:

lifecycle: preStop: exec: command: - sh - -c - "sleep 5"

这样 Pod 收到 SIGTERM 后先等 5 秒,让 Endpoints 控制器来得及把 Pod 摘掉,再真正开始关闭进程。但 sleep 时长要配合 readinessProbe 的 periodSeconds 和 terminationGracePeriodSeconds 一起调。如果 sleep 设太长,而 terminationGracePeriodSeconds 太短,容器会被强制杀死,优雅停机反而失败。

4.6 常见问题速查表

现象典型原因排查方向解决思路
容器反复重启,无明显业务报错liveness 超时阈值过小/启动期失败看 Pod Event、容器启动日志、探针历史加 startupProbe,调大 timeoutSeconds
Pod 一直未就绪,Service 不转发流量readiness 端点返回非 2xx/3xx手动 curl /health/ready 看返回码检查应用依赖,修健康检查端点逻辑
流量高峰时大量 Pod 被摘除readiness 端点依赖重数据库查询看探针响应耗时轻量化健康检查端点,引入依赖状态缓存
发布时新版本始终不可用initialDelay 小于启动时间看新 Pod 的 Event 时间线用 startupProbe 覆盖启动窗口
Pod 被误杀,一天重启很多次timeoutSeconds 小于 TP99看应用延迟分布按 TP99 弹性设置 timeout
流量打到 Terminating Pod优雅停机前未摘流量看接入层日志时间戳配置 preStop sleep,配合 readiness 周期

5. 从经验里提炼的最佳实践:探针配置的“道”与“术”

5.1 探针是应用的一部分,不是 YAML 的一部分

这句话是我一直想强调的。很多团队把探针配置当成“基础设施的活”,应用开发者不关注,运维同学只看 YAML 里的参数,结果两边对不上。实际上,健康的探针设计必须由应用开发者主导:健康检查端点暴露什么信息、依赖哪些组件、返回什么状态码,这些都需要应用侧代码配合。如果应用不提供 /health/live 和 /health/ready,运维在 YAML 里再怎么调参也是“隔靴搔痒”。

我见过最好的实践是:应用框架层面统一提供三个端点,各自语义清晰。liveness 端点只查进程健康;readiness 端点查依赖可用性和负载水位;startup 端点用于初始化状态。然后运维在平台侧统一封装探针配置模板,开发者只需声明“我的应用启动需要 30 秒”或“我的应用依赖 MySQL和Redis”,平台根据这些声明自动生成探针参数。这套协作模式下,探针的语义在代码里被固化,参数在平台上被标准化,线上事故率明显下降。

5.2 参数调优的“两套公式”

根据个人经验,探针参数里最值得花心思的是 failureThreshold、periodSeconds 和 timeoutSeconds。这里分享两个经验公式。

一是“探测周期 × 失败阈值”要大于“单次可能出现的异常时长”。比如应用偶尔会发生一次 5 秒的 GC 停顿导致健康检查端点无响应,那么 periodSeconds=5,failureThreshold=3,总时间窗口是 15 秒,大于 5 秒,不会被误杀。如果 periodSeconds=10,failureThreshold=2,总窗口是 20 秒,也还行。关键是这个乘积要能覆盖最坏情况。

二是“超时时间”要大于应用健康检查端点的 TP99 响应时间,我一般取 2~3 倍甚至更大。比如 TP99=800ms,timeout=2s;TP99=2s,timeout=5s。同时健康检查端点自身响应要足够轻,不能让探针流量本身影响应用性能。

5.3 探针与发布流程的联动

探针不只是运行时的健康管理工具,它和发布流程是深度绑定的。滚动更新是否顺利,很大程度上取决于探针能否正确反映新版本的就绪状态。这里有几个容易被忽略的点:

  • maxSurge 和 maxUnavailable 的配置要和探针就绪时间匹配。如果新版本启动慢,而 maxSurge 设得很小,滚动更新会在“等就绪”阶段卡住,整体发布变慢。
  • 如果采用蓝绿发布或金丝雀发布,readiness 探针的状态直接影响流量切分。可以结合 ReadinessGate 做更精细的灰度控制,但基础前提仍然是探针本身准确。
  • 每次发版前,检查新版应用的启动时间是否发生变化。依赖组件变多、数据量变大、初始化逻辑变复杂,都会让启动变慢。如果 startupProbe 的 failureThreshold 不够,发布当天才会发现容器一直重启。

5.4 生产环境建议的探针参数参考值

下面这套配置是我在多数场景下实践后认为比较稳妥的基线值,可以直接复制改端口后用,但务必根据你自己的应用响应特性再调:

startupProbe: httpGet: path: /health/startup port: 8080 periodSeconds: 3 timeoutSeconds: 2 failureThreshold: 40 successThreshold: 1 livenessProbe: httpGet: path: /health/live port: 8080 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 # liveness 的 successThreshold 固定为 1 且不建议调整 readinessProbe: httpGet: path: /health/ready port: 8080 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 successThreshold: 1

实际项目中,一些极端场景的参数差异很大。比如一个本身就要求秒级自动摘除的海量流量服务,failureThreshold 可以缩到 1,让第一次失败就摘流量;而一个只做离线计算、流量抖动容忍度高的任务型容器,periodSeconds 可以拉到 30 秒,减少探针开销。参数本身没有“最佳”,只有“适配”。

5.5 探针运维的日常:日志、Event 和监控

最后聊聊探针的日常运维。排障时第一手信息来自 K8s 的 Event,比如 kubectl describe pod 里能看到Liveness probe failed或Readiness probe failed,这会告诉你失败的是哪种探针、失败了几次。但 Event 里看不到单次探测延迟,想看这个得在 kubelet 侧采集探针耗时指标。

我建议在监控体系里纳入这几项:

  • 探针失败次数和失败率,按探针类型分维度监控。
  • 探针单次请求耗时分布。如果耗时逐步上涨,说明健康检查端点在变慢,往往能提前预警依赖问题。
  • 容器重启次数和重启原因分类。频繁重启但业务日志无异常,往往就是探针误杀。
  • Pod 就绪状态变化次数。发布期间频繁变化,说明就绪探针不稳定,要检查依赖抖动。

另外有一个使用习惯:不要只在出问题时才看探针,平时发完版本后,花两分钟看一眼探针的成功率和耗时曲线,能省下后续大量排查成本。探针不仅是 K8s 的“眼睛”,也是你的“预警雷达”,把它用好了,很多线上事故都可以在用户感知前被自动消化掉。

这套探针配置能力和故障排查思路,我在多个模拟项目和真实生产环境里反复验证过,核心结论始终一致:探针配置没有银弹,但一定有方法论。把语义拆对,把参数调准,把监控补上,探针就是 K8s 里最值得信任的自动运维机制。现在回过头去检查你的 Deployment,看看三个探针是不是各司其职、端点有没有拆开、超时时间够不够——这几分钟的检查,很可能帮你避开一次凌晨三点的线上事故。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进