POD健康检查的合理配置与问题总结

POD健康检查的合理配置与问题总结

Deng YongJie's blog 2,602 2023-01-15

Kubernetes 健康检查介绍

K8S 支持三种健康检查:

  1. 就绪检查(readinessProbe): Pod启动后,如果配了就绪检查,要等就绪检查探测成功,Pod Ready 状态变为 True,允许放流量进来;在运行期间如果突然探测失败,Ready 状态变为 False,摘除流量。
  2. 存活检查(livenessProbe): Pod 在运行时,如果存活检查探测失败,会自动重启容器;值得注意的是,存活探测的结果不影响 Pod 的 Ready 状态,这也是很多人可能误解的地方。
  3. 启动检查(startupProbe): 如果提供了启动探针(startup probe),则禁用所有其他探针,直到它成功为止。如果启动探针失败,kubelet 将杀死容器,容器服从其重启策略进行重启。如果容器没有提供启动探针,则默认状态为成功Success。

三种健康检查配置格式都是一样的,以 readinessProbe 为例:

readinessProbe:
  successThreshold: 3 # 1 次探测成功就认为健康
  failureThreshold: 5 # 连续 2 次探测失败认为不健康
  periodSeconds: 10 # 10s 探测一次
  initialDelaySeconds: 30 #容器启动后等待 30 秒后开始探测
  timeoutSeconds: 5 # 5s 超时还没返回成功就认为不健康
  httpGet: # 使用 http 接口方式探测,GET 请求 8848 端口的 "/nacos" 这个 http 接口,响应状态码在200~399之间视为健康,否则不健康。
    port: 8848
    path: "/nacos"

探测结果一定要真实反应业务健康状态

首选 HTTP 探测

通常是推荐业务自身提供 http 探测接口,如果业务层面健康就返回 200 状态码;否则,就返回 500。

备选脚本探测

如果业务还不支持 http 探测接口,或者有探测接口但不是 http 协议,也可以将探测逻辑写到脚本文件里,然后配置脚本方式探测。

尽量避免 TCP 探测

因为 TCP 探测实际就是 kubelet 向指定端口发送 TCP SYN 握手包,当端口被监听内核就会直接响应 ACK,探测就会成功:

当程序 hang 死,这些并不影响端口监听,所以探测结果还是健康,流量打到表面健康但实际不健康的 Pod 上,就无法处理请求,从而引发业务故障。

所有提供服务的容器都应当加上 ReadinessProbe(就绪探针)

如果你的容器对外提供了服务,监听了端口,那么都应该配上 ReadinessProbe,ReadinessProbe 不通过就视为 Pod 不健康,然后会自动将不健康的 Pod 踢出去,避免将业务流量转发给异常 Pod。

谨慎使用 LivenessProbe(存活探测)

LivenessProbe 失败会反复重启 Pod,不要轻易使用,除非把阈值设置足够大

探测条件要更宽松

如果使用 LivenessProbe,不要和 ReadinessProbe 设置成一样,需要更宽松一点,避免因抖动导致 Pod 频繁被重启。

通常是失败阈值 (failureThreshold) 设置得更大一点,避免因探测太敏感导致 Pod 很容易被重启。

超时时间 (timeoutSeconds) 和探测间隔 (periodSeconds) 也可以根据情况适当延长。

保护慢启动容器

有些应用本身可能启动慢(比如 Java),或者用的副容器,需要起一大堆依赖,导致容器启动需要的较长,如果配置了存活检查,可能会造成启动过程中达到失败阈值被重启,如此循环,无限重启。

startupProbe:
  httpGet:
    path: /nacos/health
    port: 8848
  failureThreshold: 30
  periodSeconds: 10
  initialDelaySeconds: 60

该容器启动60秒后,startupProbe首先检测,应用程序将会有最多 5 分钟(30 * 10 = 300s)的时间来完成启动。 一旦启动探测成功一次,存活或者就绪探测就会接管对容器的探测,对容器死锁作出快速响应。 如果启动探测一直没有成功,容器会在 300 秒后被杀死,并且根据 restartPolicy 来执行进一步处置。 300秒范围内,允许30次探测失败,失败则会重启。

对于这类启动慢的容器,我们需要保护下,等待应用完全启动后才开始探测:

  1. 如果 K8S 版本低于 1.18,可以设置 LivenessProbe 的初始探测延时 (initialDelaySeconds)。
  2. 如果 K8S 版本在 1.18 及其以上,可以配置启动探针,保证等应用完全启动后才开始探测。https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

避免依赖导致级联故障

LivenessProbe 探测逻辑里不要有外部依赖 (db, 其它 pod 等),避免抖动导致级联故障。

Pod B 探测逻辑里查 DB,Pod A 探测逻辑里调用 Pod B,如果 DB 抖动,Pod B 存活探测变为不健康,Pod A 调用 Pod B 也失败,也变为不健康,从而引发级联故障。

POD无限重启且流量异常

故障现象: Pod 突然不断重启,期间有流量进入,这部分流量异常。

原因:

  1. Pod 之前所在节点异常,重建漂移到了其它节点去启动。
  2. Pod 重建后由于基础镜像中依赖的一个服务有问题导致启动较慢,因为同时配置了 ReadinessProbe 与 LivenessProbe,大概率是启动时所有健康检查都失败,达到 LivenessProbe 失败次数阈值,又被重启。
  3. Pod 配置了 preStop 实现优雅终止,被重启前会先执行 preStop,优雅终止的时长较长,preStop 期间 ReadinessProbe 还会继续探测。
  4. 探测方式使用的 TCP 探测,进程优雅终止过程中 TCP 探测仍然会成功(没完全退出前端口监听仍然存在),但实际此时进程已不会处理新请求了,而流量仍然转发进去。
  5. LivenessProbe 结果不会影响 Pod Ready 状态,是否 Ready 主要取决于 ReadinessProbe 结果,由于 preStop 期间 ReadinessProbe 是成功的,Pod 就变 Ready 了,也就是说允许流量进入POD。
  6. Pod Ready 但实际无法处理请求,最终业务就会异常。

总结:

  1. Pod 慢启动 + 存活探测 会导致被无限重启。需要延长 initialDelaySeconds 或 启动探针来保护慢启动容器。
  2. TCP 探测方式不能完全真实反应业务健康状态,导致在优雅终止过程中,ReadinessProbe 探测成功让流量放进来而业务却不会处理,导致流量异常。需要使用更好的探测方式,建议业务提供 HTTP 探活接口,使用 HTTP 探测业务真实健康状态。
  3. 建议使用HTTP探测方式,Pod 慢启动 +就绪探测。有效避免POD Ready状态,实际上容器进程不会处理请求了,而导致的异常问题。