容器健康检查机制通过liveness、readiness和startup三类探针的协同配合,在应用实例不可用的第一时间完成摘流、重启或屏蔽流量,它是识别异常应用实例的第一道防线。
这套机制不靠猜测,不靠人工盯监控,而是由kubelet按固定周期主动探测每个容器的真实状态,探测结果直接决定容器的生死去留,也决定了用户请求是否会被转发到一个注定报错的实例上。
为什么健康检查能识别不可用的容器
容器本身是一个隔离的进程环境,”进程还在运行”和”业务能正常服务”是两回事,一个Java应用可能进程没有退出,但线程池已经耗尽;一个Nginx容器可能还没重启,但上游后端已全部失联,如果没有健康检查机制,调度器会把流量源源不断送到这些”活着但已丧失服务能力”的实例上,用户感知就是超时、报错、白屏。
健康检查机制的核心思路是用主动探测代替被动等待,我们不再等用户投诉,而是让系统每隔几秒就去问容器”你还能干活吗”,回答不上来的,先摘流量,再决定是否重启。
liveness探针:业务进程活着不等于服务可用
liveness探针解决的是”容器要不要杀掉重启”的问题,它探测的是最基本的存活状态进程还在不在、端口还听不监听、某个特定的命令能不能正常返回。
比如一个Node.js服务因为内存泄漏进入假死状态,HTTP端口还在监听,但每次请求都卡住,此时如果只靠进程检测,系统会认为一切正常,配置了liveness探针之后,kubelet定期请求/healthz接口,连续几次拿不到响应,就会杀掉容器并按重启策略拉起新实例。
关键点在于:liveness探针判断的是”要不要重启”,而不是”能不能接流量”。 这两个问题经常被混淆,也是不少线上事故的根源。
readiness探针:流量分发前的最后一道岗
readiness探针判断的是”这个实例有没有资格接收请求”,探测失败的实例不会被杀掉,而是从Service的Endpoint列表里移除,流量不再分发到它身上。
典型场景是应用启动时需要加载大量配置或预热缓存,直接切流量会导致大量超时请求,readiness探针这个阶段,我们可以用readiness探针控制实例对外暴露服务的时间点,配置了readiness的容器,在探针通过之前,Pod的IP不会出现在Endpoints列表中,也就不会收到任何业务请求。
readiness失败只摘流量,不重启进程。 这一点和liveness有本质区别,一个数据库连接池暂时耗尽的服务,readiness探针会连续失败,但进程本身是健康的,杀掉重启反而会让问题恶化。
startup探针:给慢启动应用留出缓冲
启动慢的应用容易在启动阶段被liveness探针误杀,很多老旧的Java服务启动耗时超过两分钟,如果liveness探针的initialDelaySeconds设置得太短,容器还没启动完就被杀掉,陷入反复重启的循环。
startup探针专门解决这个问题,它只在容器启动阶段运行,一旦探测成功就停止工作,把探针控制权交给liveness和readiness,在startup探测期间,liveness探针不会执行,这相当于给了慢启动应用一个明确的”免死金牌”。
行业共识认为,startup探针的failureThreshold应设置为足以覆盖应用最长启动时间,如果应用需要三分钟完成启动,探针周期设为10秒,failureThreshold至少要设到18次以上。
容器健康检查liveness和readiness区别在哪里
这两个探针名称相似,职责完全不同,但配置错误引发的故障却极为常见。
| 对比维度 | liveness探针 | readiness探针 |
|---|---|---|
| 核心问题 | 容器要不要重启 | 容器能不能接流量 |
| 失败动作 | 杀容器,按策略重启 | 摘除Endpoints,不移除Pod |
| 适用场景 | 死锁、内存泄漏、进程假死 | 启动加载、依赖服务不可用、过载保护 |
| 恢复方式 | 杀掉重建 | 探针恢复后自动加回Endpoints |
两者判定逻辑的本质差异
liveness探针的判定逻辑是”非黑即白”失败就杀,没有中间态,这种粗暴处理方式在多数情况下是高效的,但遇到依赖外部服务的场景时会误伤,比如应用依赖的Redis临时抖动,readiness探针会失败,但liveness探针不应该同时失败,否则Pod会频繁重启。
通常的做法是:liveness探针只探测进程自身状态,readiness探针才探测外部依赖的可用性。 例如liveness用TCP检查端口存活,readiness去请求一个会查询数据库的接口,这样分工,外部依赖抖动时实例被摘流但不被杀掉,Redis恢复后实例自动恢复服务。
探针误判引发的事故场景
误判比不判更危险,一个典型的配置错误是把业务接口的响应时间作为liveness探针的判定依据,接口偶发慢查询,响应时间超过阈值,liveness连续失败后重启容器,此时容器内存中的缓存全部丢失,启动后又要重新预热,导致雪崩。
另一个常见场景是readiness探针和liveness探针使用了同一个接口,这个接口一旦因高并发出现延迟,两把刀同时落下一边摘流量,一边杀容器,系统状态比你想象中混乱得多,配置时建议让两个探针指向不同路径,liveness走静态页面或专用探活端口,readiness走真实的业务依赖检查。
docker容器健康检查怎么配置才算规范
配置路径分为Dockerfile层面和Kubernetes清单层面,两者都掌握才能应对不同部署环境。
Dockerfile中的HEALTHCHECK指令
原生Docker支持用HEALTHCHECK指令定义健康检查逻辑:
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD curl -f http://localhost:8080/health || exit 1
这里的参数含义:每30秒执行一次,超过5秒无响应判定失败,连续失败3次标记为unhealthy。docker ps会显示容器状态为unhealthy,编排工具可以据此对容器进行替换。
注意一点:Dockerfile中每一层只能有一条HEALTHCHECK指令,后写的会覆盖先写的。 更隐蔽的是,HEALTHCHECK只能通过Docker命令查看状态,它不参与Kubernetes的调度决策,Kubernetes只认自己配置的探针,不会读取Dockerfile里的HEALTHCHECK,这个兼容性关系需要搞清楚。
Kubernetes清单中的探针配置
在Kubernetes中,探针配置在Pod的spec.containers下:
containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
参数调优的关键是先定initialDelaySeconds,再定periodSeconds,最后才是不和你业务脱节的timeoutSeconds。initialDelaySeconds应该大于应用平均启动时间;periodSeconds意味着探针请求对业务造成的额外负载,尽量控制在每10秒一次,不要盯着最小化数值去看。
探针请求路径的代码单独实现,不要复用复杂的业务逻辑,不要带数据库查询,不要带外部依赖调用。 否则探针本身就是定时炸弹。
探针配置的常见错误排查方法
- 探针路径返回状态码不是200-399,例如返回302重定向
- 探针请求头和真实用户请求不一致,触发WAF拦截
- TLS证书校验失败,httpGet探针无法访问HTTPS接口
- 端口写错,容器监听的是8080而探针探测的是8081
- 探针的超时时间比应用自身响应慢更短,被误判成了失败
容器健康检查失败怎么办:从定位到恢复的完整路径
探针连续失败后,容器状态会进入CrashLoopBackOff或Unhealthy,此时从容器层面反推问题原因,按下面几层逐个排查,大多数情况能定位到根因。
第一步:确认探针命令在容器内真正可用
很多探针失败的原因是命令不存在,或者路径不对,先进入容器手动执行探针命令:
kubectl exec -it <pod-name> -- sh # 在容器内执行你的探针命令 curl -I http://localhost:8080/healthz
如果容器内没有curl也没有wget,那配置httpGet探针就注定失败,推荐用/bin/sh -c方式配合内置命令探测,或者在镜像中预装最小体积的探活工具。
第二步:查看kubelet事件与探针日志
kubectl describe pod <pod-name>
输出的Events区域会清晰地记录探针失败的原因。Liveness probe failed和Readiness probe failed对应两类不同的失败语义,后者不用害怕,前者需要重视,Events区域内会显示探针的超时时间、失败次数、恢复时间,这些信息用来定位哪个阶段的哪次探测出了问题。
第三步:检查业务日志与容器退出码
如果探针事件显示HTTP状态码异常,去查应用日志中对应的请求记录,探针请求会在访问日志里留下痕迹,过滤出探针路径的日志,能看到HTTP错误状态码对应的业务异常堆栈。
容器退出码也能提供线索:
| 退出码 | 含义 | 后续动作 |
|---|---|---|
| 0 | 正常退出 | 检查为何触发重启 |
| 137 | SIGKILL,内存超限被杀 | 调大内存限额或排查泄漏 |
| 143 | SIGTERM,优雅终止 | 检查滚动更新策略 |
| 1 | 业务进程主动退出 | 看业务日志 |
| 255 | 启动脚本异常 | 检查entrypoint脚本 |
进程被反复杀死的实例,在kubectl logs --previous里能看到上一次容器的日志记录,这个参数在排查崩溃问题时比你想的更有价值。
容器健康检查机制相关的常见问题
健康检查探针会影响容器性能吗?
会,但影响可以控制,探针每执行一次都是一次额外的HTTP请求或命令执行,高频探测会产生日志噪音和连接开销,推荐将periodSeconds设置为10秒以上,探针的请求路径不要做耗时操作,向探针路径注入独立于业务线程的轻量逻辑,平衡可靠性和开销,多数时候比性能优化更值得。
用了k8s还需要Dockerfile里配HEALTHCHECK吗?
需要区分场景,Kubernetes只认自己的探针配置,不会使用Dockerfile中的HEALTHCHECK,但是镜像被用于单独运行(docker run)或者配合其他编排工具时,HEALTHCHECK仍是唯一的安全网,两条配置路径都保留,互不冲突也不会产生双倍探测。
探针连续失败触发了重启,但这个容器一直是正常响应请求的,误判可能出在哪里?
可能是探针访问的端口和业务实际监听端口不一致,也可能是探针路径对来源IP做了限制,kubelet发起探测的IP被防火墙拦截,处理方法是先看Pod的Events中探针失败的具体原因,从超时还是连接拒绝分辨问题方向,连接拒绝大概率是端口问题,超时则可能是探针接口本身处理慢,把探针的超时时间调大后继续观察,容器应用接管网络配置会让探针走代理导致预检失效,但现代集群中这一现象正在减少。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/640795.html





