问题概述:
在更新或者创建工作负载时,经查会遇到,健康检查失败的错误,导致容器一直无法正常启动。类似如下:
问题原因:
- 容器内应用原因:
- 健康检查所配置规则对应的端口或者脚本,无法成功探测,如容器内应用没正常启动等
- 用户使用不当:
- 设置的阈值过小,详见“基础概念”章节中的示例
- 配置有误,如写错的检查的端口等
- 系统层问题:
- 节点负载非常高:节点负载高导致的健康检查失败,通常出现在容器已经正常运行,然后突然挂掉,事件有健康检查失败的错误。k8s的调度是预选 优选,一般会优选低负载的节点,所以初始调度,不太会直接落到极高负载的节点。(但因k8s默认调度器规则是基于request权重,所以不绝对,具体可了解k8s的调度器策略,不在本文讨论范围)
- 其他bug,或系统组件问题
几乎所有案例都因为1&2两项导致,故本文针对于1&2两项,进行排查说明,这也是大多用户遇到此问题的原因。系统层问题遇到较少,不在此文章讨论。
基础概念:
首先,需要明确,liveness(存活检查)和readiness(就绪检查)可一起使用,也可以只使用其中一种,具体取决于用户。
- 容器健康检查分两种,liveness(存活检查)和readiness(就绪检查),统称为健康检查。
- 官方概念,liveness(存活检查)和readiness(就绪检查)都代表什么? 参考文档:https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/ livenessProbe:指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。 readinessProbe:指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。
- 举例对上述文字概念进行说明。 注意: 1. 健康检查对检测间隔,失败阈值等,有多种配置可定义,本例只是对概念进行说明,具体配置请自行参考文档了解。 2. 本例只对容器初次启动时,遇到的现象进行说明,但是容器在正常Running的时候,也可能因为容器内进程crash,或者容器夯死,也会触发检查失败的报错。
例1:
配置了liveness(存活检查)规则:检测80端口,容器启动后10s开始检查,每次检查间隔1s,一次不通过即失败
容器实际80端口应用启动时间:15s
结果:死循环,容器不断重启,事件有“Liveness probe failed”字样报错。为典型用户配置不合理导致,直接影响使用。
例2:
配置了liveness(存活检查)规则:检测80端口,容器启动后20s开始检查,每次检查间隔1s,一次不通过即失败
容器实际80端口应用启动时间:15s
结果:检查成功,不会打印Liveness相关日志,容器正常Running
例3:
配置了readiness(就绪检查)规则:检测80端口,容器启动后10s开始检查,每次检查间隔1s,一次不通过即失败
容器实际80端口应用启动时间:15s
结果:事件会报5次“Readiness probe failed”,然后停止报错,容器正常Running,待报错停止后,k8s会将此pod加入endpoint,也就是可以被service后端负载上,去提供服务。为用户配置不合理导致报错,但不会影响使用,检测通过后即正常。
例4:
配置了readiness(就绪检查)规则:检测80端口,容器启动后20s开始检查,每次检查间隔1s,一次不通过即失败
容器实际80端口应用启动时间:15s
结果:检查成功,不会打印Readiness相关日志,容器正常Running
解决方案:
通过如上概念和示例,可得知,一般出现健康检查失败报错的两种情形:容器自身应用问题&&用户使用不当问题。关于配置有误,如写错的检查的端口或者脚本等行为,还请优先自行排除。(系统层问题和写错配置的端口和脚本不在讨论范围,假设系统都健康且配置的端口正确)
那么遇到此类报错该如何解决,可按如下场景对号入座:
同时配置了liveness(存活检查)和readiness(就绪检查)
如上文所说,readiness(就绪检查)会在探测规则就绪后,便检查通过。所以此处应优先考虑如下几点
- liveness是否阈值设置过小,导致死循环
- 容器进程是否真的有问题
处理方法:可重新更新工作负载,**去除**liveness(存活检查)后观察,如果去除liveness(存活检查)后,事件有几次“Readiness probe failed”后即正常,那么说明是阈值过小导致,调大阈值即可。如果去除后“Readiness probe failed”一直持续不断,请检查镜像是否有问题。
只配置了liveness(存活检查)
- liveness是否阈值设置过小,导致死循环
- 容器进程是否真的有问题
处理方法:可重新更新工作负载,尽可能适当调大liveness(存活检查)阈值(如启动延迟)后观察,如果调大liveness(存活检查)阈值后即正常,那么说明是阈值过小导致,如果仍然事件一直报错“Liveness probe failed”,请检查镜像是否有问题。
注意:这里并没有让用户去掉liveness(存活检查),因为去除后,如果容器内有常驻进程,容器会直接Running,不利于问题判断,当然用户也可去除liveness(存活检查),手动进入容器查看。
只配置了readiness(就绪检查)
请检查镜像。
我该如何检查镜像哪里有问题?
如上所述,对于异常情况,多数都提到要去检查镜像,该如何进行检查?方法如下:
场景一:
容器已经正常running,只是健康检查未通过。这种一般情况下在事件只会有“Liveness probe failed”和“Readiness probe failed”的错误。在确认没有liveness(存活检查)的情况下,直接进入容器,排查即可,如查看应用启动失败的日志,尝试手工拉起应用看看卡点在哪等。
场景二:
除“Liveness probe failed”和“Readiness probe failed”的错误,还有“Back-off restarting failed container”的错误。
“Back-off restarting failed container”一般是因为容器内没有常驻进程,或者进程意外退出导致。
可通过如下方法,让容器先行启动,然后进入容器排查。(确认没有liveness(存活检查)的情况,否则会导致容器反复重启,不利于排查)
1、创建工作负载,在容器配置中如下位置填入以下两行内容
有的平台页面需要用[],类似这样,具体取决于页面要求
yaml中写法参考如下
2、步骤1会执行sleep命令,并会覆盖掉容器原本的启动命令,如果您的容器有sh环境和sleep命令,上述命令就会执行成功并让容器running
3、进入容器,然后手工启动下容器本该执行的启动命令或脚本,然后观察相关日志输出,看看问题究竟在哪
4、解决问题后重新构建镜像,然后使用新镜像并去掉1中的参数尝试启动
Q&A
- 为什么容器liveness检查失败,反复重启后,还落在原来的节点,pod重启不是应该要重调度的吗? 首先,需要清楚个概念
重启 Pod 中的容器不应与重启 Pod 混淆。 Pod 不是进程,而是容器运行的环境。 在被删除之前,Pod 会一直存在。
参考文档:https://kubernetes.io/zh/docs/concepts/workloads/pods/
健康检查针对的是容器,重启是容器重启,而调度,是pod调度,但是pod并不会因容器健康检查失败重启而重建,故pod不会变换节点。
2. 想到再说