就绪探针是Kubernetes保障服务可用性的第一道防线,配置不当会直接导致Pod被摘除流量,进而引发大量请求超时或错误。在一次线上故障中,我们将某个微服务的就绪探针超时时间从3秒调整为1秒,结果在高峰期触发大规模Pod就绪状态翻转,服务出现间歇性不可用。本文结合这次实战经历,拆解就绪探针的工作原理、配置参数取舍以及问题排查路径。
就绪探针决定Pod是否接收流量,存活探针决定Pod是否被重启,两者的职责完全独立。Kubelet周期性执行就绪探针,当探测失败时,Endpoints控制器会将该Pod的IP从Service后端列表中移除,但Pod本身不会被杀死;存活探针失败则触发容器重启。很多配置事故源于对这两个探针的混淆,误把就绪探针当存活探针来调参。
Kubernetes官方文档给出的建议是:就绪探针适用于“容器已启动但无法立即处理流量”的场景,比如加载缓存或等待依赖服务;存活探针适用于“容器死锁或内存泄漏导致无法响应”的场景。实践中,如果服务启动时间较长,只配置存活探针而忽略就绪探针,会导致流量在Pod启动瞬间打向尚未就绪的进程,出现大量连接拒绝。
故障发生时,业务监控显示一个在线交易服务的成功率从99.9%骤降到95%,持续约20分钟后自动恢复。排查发现,该服务的就绪探针配置如下:HTTP GET请求路径为/healthz,periodSeconds为5,timeoutSeconds为1,failureThreshold为2。
readinessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 2
在高峰期,服务线程池处于高负载状态,/healthz接口的响应时间从正常的30毫秒飙升至800毫秒以上。部分请求的响应时间超过1秒,触发了超时探针判定失败。连续两次失败后,Pod被标记为未就绪,从Service负载均衡池中摘除。摘除后,剩余Pod承接的流量进一步增大,形成雪崩效应:更多Pod的探针开始超时,最终集群中三分之二的Pod被摘除,只剩少数Pod承受全部流量。
恢复过程同样值得复盘。当流量回落,剩余Pod的负载降低,/healthz响应恢复,探针连续成功两次后Pod重新加入Endpoint列表。从故障发生到自动恢复,整个过程约20分钟,期间业务侧看到的现象就是时而超时时而正常,这正好印证了就绪探针状态翻转的特征。
periodSeconds控制探测频率,timeoutSeconds控制单次探测的等待上限,failureThreshold控制连续失败多少次后才标记未就绪。这三个参数的组合决定了Pod对瞬时抖动的敏感度。据CNCF《Kubernetes 应用部署实践报告》数据显示:在生产环境中,约38%的探针故障与超时时间设置过短有关,而非应用真正宕机。
以本次故障为例,timeoutSeconds=1意味着只要接口响应超过1秒就判定为失败。对于健康检查接口而言,1秒的阈值在多数场景下都过于严苛。合理的做法是将timeoutSeconds放宽到与接口P99响应时间匹配,比如设置为3秒,同时保持periodSeconds为5秒,这样既可以快速发现真正卡死的进程,也能容忍短时负载波动。
failureThreshold则是一个延迟策略。将failureThreshold设为2,意味着Pod需要连续两次失败才会被摘除,这为瞬时抖动提供了一道缓冲;如果设为1,任何一次超时都会触发摘除,在高并发场景下等于主动放大故障面。
initialDelaySeconds参数是许多配置事故的隐形杀手。它定义容器启动后多少秒才开始执行第一次探测,如果设置过短,应用尚未完成初始化,探针会连续失败;如果设置过长,Pod虽然处于Running状态但不会接收流量,造成资源闲置。业界普遍推荐采用动态就绪策略:结合startupProbe来保护慢启动应用,让就绪探针专注于稳态时的健康判断。
startupProbe在Kubernetes 1.18版本后进入稳定阶段,它的作用是给容器提供一个“启动宽限期”,期间不进行就绪和存活探测。配置示例中,我们可以让startupProbe使用更长的超时和更高的失败阈值,保障应用有足够时间初始化。
startupProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 2
failureThreshold: 30
上述配置允许应用最长60秒(2秒×30次)完成启动,期间不会触发就绪探针。启动完成后,就绪探针才开始介入,这样既避免启动阶段的误判,也保证了稳态下的快速响应。
遇到就绪探针引发的流量异常,建议按以下顺序排查,每一步都能缩小问题范围。
这套流程在本次故障中帮助我们快速定位了超时参数与线程池负载的关系。如果跳过前两步直接看应用日志,很容易误判为代码性能问题,浪费数小时。
基础设施差异会直接改变就绪探针的最优参数。下面结合实际经验给出四类常见服务的建议配置,所有数值都基于稳态应用与正常网络环境的假设。
| 服务类型 | periodSeconds | timeoutSeconds | failureThreshold |
|---|---|---|---|
| 无状态API服务 | 5 | 3 | 2 |
| 依赖外部存储的服务 | 10 | 5 | 3 |
| 批处理任务一次性Pod | 15 | 5 | 1 |
| 对外网关或代理 | 3 | 2 | 2 |
无状态API服务通常响应较快,3秒超时能容忍常见GC暂停或网络抖动,2次失败阈值提供缓冲。依赖外部存储的服务健康检查往往需要访问数据库或Redis,这类探测的响应时间天然偏高,timeoutSeconds应放宽到5秒以上。批处理任务短暂运行,就绪探针只用于判断是否具备执行条件,不需要太高频率,失败一次即可摘除以减少等待。对外网关要尽快感知后端故障,探测频率可以加快,但超时仍应保持2秒以上,避免误伤。
一个常见误用是把就绪探针的探测路径指向一个不做任何依赖检查的静态页面,比如返回200的纯静态资源。这样的探针无法反映应用真实可用性,即使数据库连接池已耗尽,探针依然显示成功,流量继续涌入故障实例。合理的健康接口应至少检查核心依赖的可用状态,但注意不要引入过重的检查逻辑,比如每次探针都执行完整的业务数据库查询,会增加不必要的压力。
另一个误用是让探针依赖的端口与应用监听端口不一致,导致探针永远失败。配置时务必确认containerPort与httpGet中的port一致。如果应用使用gRPC等非HTTP协议,应改用tcpSocket探针或为健康检查单独开启HTTP端口。
据Google SRE团队在《Site Reliability Engineering》中总结的经验:Health check的设计目标是“快速失败、快速恢复”,探针检测的是“能否处理请求”的二值状态,而不是性能评判工具。把性能指标作为探针逻辑的输入,会触发本文将中描述的雪崩效应,这已经超越了探针的设计边界。性能问题应该交给指标监控和弹性伸缩来处理,而不是用摘除流量来惩罚Pod。
回溯整个事件,最核心的原则是:就绪探针的参数必须基于真实流量特征来定义,而不是照搬模板。每个服务的延迟分布、依赖复杂度、启动时长都不同,探针参数应当通过压测和监控数据校准。建议在架构评审中将对探针参数的讨论与负载测试结果绑定,让就绪探针成为可量化的SLO指标之一,而非一串拍脑袋填的数字。
当探针配置被修改后,伴随发布流程进行灰度观察,监控就绪状态翻转次数和成功率变化。如果发现翻转频繁,先回滚参数配置,再分析根因。将这次故障的完整时间线和决策过程记录进团队事故库,后续其他服务调整探针参数时即可直接对照,避免同一类问题在不同服务上重复出现。
评论
加载中...