目录

k8s健康检查失败问题,如何解决

问题概述:

在更新或者创建工作负载时,经查会遇到,健康检查失败的错误,导致容器一直无法正常启动。类似如下:

/images/k8s%E5%81%A5%E5%BA%B7%E6%A3%80%E6%9F%A5%E5%A4%B1%E8%B4%A5%E9%97%AE%E9%A2%98%EF%BC%8C%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3/image-20210902125916164.png

问题原因:

  1. 容器内应用原因:

    1. 健康检查所配置规则对应的端口或者脚本,无法成功探测,如容器内应用没正常启动等
  2. 用户使用不当:

    1. 设置的阈值过小,详见“基础概念”章节中的示例
    2. 配置有误,如写错的检查的端口等
  3. 系统层问题:

    1. 节点负载非常高:节点负载高导致的健康检查失败,通常出现在容器已经正常运行,然后突然挂掉,事件有健康检查失败的错误。k8s的调度是预选+优选,一般会优选低负载的节点,所以初始调度,不太会直接落到极高负载的节点。(但因k8s默认调度器规则是基于request权重,所以不绝对,具体可了解k8s的调度器策略,不在本文讨论范围)

    2. 其他bug,或系统组件问题

      几乎所有案例都因为1&2两项导致,故本文针对于1&2两项,进行排查说明,这也是大多用户遇到此问题的原因。系统层问题遇到较少,不在此文章讨论。


基础概念:

首先,需要明确,liveness(存活检查)和readiness(就绪检查)可一起使用,也可以只使用其中一种,具体取决于用户。

  1. 容器健康检查分两种,liveness(存活检查)和readiness(就绪检查),统称为健康检查。

  2. 官方概念,liveness(存活检查)和readiness(就绪检查)都代表什么?

    参考文档:https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/

    livenessProbe:指示容器是否正在运行。如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来。

    readinessProbe:指示容器是否准备好为请求提供服务。如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址。

  3. 举例对上述文字概念进行说明。

    注意:

    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


解决方案:

通过如上概念和示例,可得知,一般出现健康检查失败报错的两种情形:容器自身应用问题&&用户使用不当问题。关于配置有误,如写错的检查的端口或者脚本等行为,还请优先自行排除。(系统层问题和写错配置的端口和脚本不在讨论范围,假设系统都健康且配置的端口正确)

那么遇到此类报错该如何解决,可按如下场景对号入座:

  1. 同时配置了liveness(存活检查)和readiness(就绪检查)

    如上文所说,readiness(就绪检查)会在探测规则就绪后,便检查通过。所以此处应优先考虑如下几点

    • liveness是否阈值设置过小,导致死循环
    • 容器进程是否真的有问题

    处理方法:可重新更新工作负载,去除liveness(存活检查)后观察,如果去除liveness(存活检查)后,事件有几次“Readiness probe failed”后即正常,那么说明是阈值过小导致,调大阈值即可。如果去除后“Readiness probe failed”一直持续不断,请检查镜像是否有问题。

  2. 只配置了liveness(存活检查)

    • liveness是否阈值设置过小,导致死循环
    • 容器进程是否真的有问题

    处理方法:可重新更新工作负载,尽可能适当调大liveness(存活检查)阈值(如启动延迟)后观察,如果调大liveness(存活检查)阈值后即正常,那么说明是阈值过小导致,如果仍然事件一直报错“Liveness probe failed”,请检查镜像是否有问题。

    注意:这里并没有让用户去掉liveness(存活检查),因为去除后,如果容器内有常驻进程,容器会直接Running,不利于问题判断,当然用户也可去除liveness(存活检查),手动进入容器查看。

  3. 只配置了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. 创建工作负载,在容器配置中如下位置填入以下两行内容

    /images/k8s%E5%81%A5%E5%BA%B7%E6%A3%80%E6%9F%A5%E5%A4%B1%E8%B4%A5%E9%97%AE%E9%A2%98%EF%BC%8C%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3/image-20210902130011295.png

  2. 步骤1会执行sleep命令,并会覆盖掉容器原本的启动命令,如果您的容器有sh环境和sleep命令,上述命令就会执行成功并让容器running

  3. 进入容器,然后手工启动下容器本该执行的启动命令或脚本,然后观察相关日志输出,看看问题究竟在哪

  4. 解决问题后重新构建镜像,然后使用新镜像并去掉2中的参数尝试启动

Q&A

  1. 为什么容器liveness检查失败,反复重启后,还落在原来的节点,pod重启不是应该要重调度的吗?

    首先,需要清楚个概念

    重启 Pod 中的容器不应与重启 Pod 混淆。 Pod 不是进程,而是容器运行的环境。 在被删除之前,Pod 会一直存在。

    可参考: https://kubernetes.io/zh/docs/concepts/workloads/pods/#working-with-pods

    健康检查针对的是容器,重启是容器重启,而调度,是pod调度,但是pod并不会因容器健康检查失败重启而重建,故pod不会变换节点。