云上健康检查与自动恢复实战:让你的系统学会自愈
本内容发表于:2026-05-08 11:42:46
浏览量
1011

云上健康检查与自动恢复实战:让你的系统学会自愈

微信图片_2026-05-08_114151_619.png

去年一个客户,凌晨被拉起来处理故障。应用假死,进程还在,但不处理请求。监控显示CPU、内存都正常,负载均衡也把流量正常转发过来。但用户收到全是超时。

查了半天,发现是应用内部死锁,线程池全堵住了。进程活着,但什么事也干不了。负载均衡的健康检查用的是TCP连接检测,能连上就认为健康。进程没挂,端口开着,TCP检查通过了。但应用已经废了。

这是健康检查最常见的误区:能连上不代表能服务。

今天聊聊健康检查与自动恢复。不是那种“检查很重要”的废话,而是帮你理清楚:怎么检查才能真正反映服务健康?阈值怎么设?怎么让系统自己恢复,不用半夜爬起来?

01 TCP通不代表服务能用

很多负载均衡和K8s的默认健康检查是TCP探针。只检查端口能不能连上。能连上就标记为“健康”。

但实际场景中,很多故障和端口无关:

  • 应用死锁,端口还开着

  • 数据库连接池耗尽,但端口正常

  • 内部依赖挂掉,服务对外端口还通

  • 内存泄漏,GC频繁,端口仍可连接

TCP通,不代表服务能处理请求。

反常识观点:健康检查要检查的是“能不能正常工作”,不是“端口能不能连上”。

那家客户后来把TCP检查改成了HTTP检查,调用/health接口。接口内部检查:数据库连接、依赖服务、线程池状态。任何一个不正常,返回500。负载均衡收到500就把节点摘掉。

改了之后,假死节点自动摘流,不用半夜人工排查。

02 三种探针:存活、就绪、启动

K8s里有三种探针,分别解决不同阶段的问题。

存活探针(livenessProbe):判断容器是否还活着。挂了就重启。

  • 检测失败,Kubelet杀掉容器,按重启策略重建

  • 适合检测死锁、无限循环等让进程卡死的问题

  • 不适合检测依赖故障(比如数据库连不上重启也没用)

就绪探针(readinessProbe):判断容器是否准备好接收流量。失败就从Service摘掉,不接收请求,但不重启。

  • 用于启动预热、依赖加载等场景

  • 启动慢的服务,readinessProbe初始延迟设大点

  • 检测失败只摘流,不重启,避免反复重启治不好

启动探针(startupProbe):保护启动慢的服务。

  • 启动阶段用startupProbe,成功后才切换成livenessProbe

  • 适合启动需要几十秒甚至几分钟的服务

  • 避免启动期间被livenessProbe误杀

03 阈值调优:太敏感和太迟钝都不行

健康检查的参数设置,直接影响系统稳定性。

参数说明

  • initialDelaySeconds(初始延迟):容器启动后等多久才开始检查。设太短,服务还没起来就被判死亡。

  • periodSeconds(检查间隔):每隔几秒检查一次。设太短,增加负载;设太长,故障发现慢。

  • timeoutSeconds(超时):检查请求最大等待时间。设太短,慢启动服务被误判。

  • failureThreshold(失败阈值):连续失败几次,才认为不健康。设太小,临时抖动就踢掉;设太大,故障反应慢。

推荐配置(以K8s为例):

探针类型initialDelayperiodtimeoutfailureThreshold
liveness30-60秒10秒5秒3次
readiness0-10秒5秒5秒3次
startupProbe0秒5秒5秒30次(按启动时长调)

核心原则:readiness可以快一点,及时摘掉坏节点;liveness放松一点,避免误杀。

04 自动恢复不是只有重启

很多人理解的自动恢复就是“挂了重启”。但重启不是万能的。

重启能解决的问题

  • 内存泄漏(重启后内存释放)

  • 死锁(重启打破僵局)

  • 临时状态错乱

重启解决不了的问题

  • 配置错误(重启完还是错的)

  • 依赖故障(下游挂了,重启没用)

  • 数据损坏(重启也恢复不了)

自动恢复的几种手段

  • 摘流量:最温和。readiness失败,不重启,只是不接流量。适合依赖故障,等依赖恢复自动就好。

  • 重启:中等。liveness失败,杀掉重建。适合进程挂掉、死锁。

  • 替换实例:K8s自动重建,可能漂移到其他节点。

  • 告警+人工:有些问题不适合自动恢复,比如数据损坏、配置错误。自动恢复搞不定,需要人工介入。

那家客户后来设计了分层恢复策略:

  • 数据库连接失败 → 只摘流,不重启(重启没用)

  • 应用死锁 → 重启(liveness检测失败)

  • 连续重启3次 → 告警人工介入

05 常见坑与解法

坑一:探针调用依赖了其他服务

/health接口里调用了数据库、Redis、下游API。数据库重启时短暂不可用,/health返回500,所有Pod被摘流。依赖恢复后,readiness恢复,流量回来。但数据库重启期间服务全挂。

解法:/health只检查本进程。依赖的健康由调用方处理,不在健康检查里做。

坑二:initialDelaySeconds设太短

Java应用启动要30-60秒,initialDelaySeconds只设了10秒。livenessProbe开始检查,服务还没起来,连续失败3次,Pod被杀死,反复重启,永远起不来。

解法:用startupProbe保护启动阶段,或把initialDelaySeconds设大。

坑三:failureThreshold设太小,抖动就重启

网络闪断2秒,livenessProbe超时一次,failureThreshold=1,直接重启。生产环境网络不可能零抖动。

解法:failureThreshold设3次,容忍短暂抖动。

06 一个真实案例:readiness探针错误配置的惨案

某公司K8s集群,readinessProbe配置了initialDelaySeconds=5failureThreshold=3。听起来正常。

问题出在应用启动就30秒。启动期间,readinessProbe开始检查,连续失败3次,Pod被标记NotReady,摘流。摘流后过了几秒,应用终于启动完成,readiness恢复。但被摘流的那段窗口,大量请求失败。

用户投诉,排查发现是readiness探针太早开始检查。

修复方案:

  • 增加startupProbe,设failureThreshold=30periodSeconds=5,给150秒启动时间

  • startupProbe成功后,readinessProbe和livenessProbe才开始工作

  • 启动期间不再被误判

写在最后

健康检查和自动恢复,是系统自愈的基石。但用错了,可能比不用还糟。

那位客户的运维负责人后来总结:“健康检查不是点一下开关就行,要根据自己的服务特性配置。太严了,动不动就重启;太松了,挂了也不知道。”

你的健康检查,配对了吗?