Kubernetes 设计模式笔记 —— Health Probe

Health Probe 模式主要关注 Kubernetes 如何获取某个应用的健康状态。为了实现完全自动化,一个云原生应用必须是高度可观测的,从而 Kubernetes 能够推断应用的状态,检测应用是否已经启动,是否已经准备好接收请求。
这些观测结果会影响 Pod 的生命周期管理,以及网络流量被路由到应用的具体路径。

Kubernetes 会定期检测容器中进程的状态,如果有错误发生,就立即重启该容器。然而在实践中,通过检查进程状态来确定应用是否健康,并不总是有效。
很多情况下应用提供的服务中断了,但进程仍旧在运行。比如 Java 应用有可能抛出 OutOfMemoryError 同时 JVM 进程仍在运行。此外,应用还有可能因为无限循环、死锁或者缓存异常等原因冻结。
因此 Kubernetes 需要一种可靠的方式来检查应用的健康状态,不关注应用的内部工作流程,而是通过某些指标,衡量应用能否对外提供服务。

软件行业已经接受了这样一个事实,即写出完全没有 bug 的软件是不现实的。因此当面对 failures 时,可以把关注点从避免 bug 转移到如何快速检测到失效并自动恢复。
但错误检测并不是一个简单的对所有应用通用的任务,存在很多不同的对于错误的定义,并且不同类型的错误也需要不同的应对方式。

Process Health Checks

process health check 是最简单的一种 health check 方式,由 Kubelet 持续对容器进程进行检测。若容器进程没有处于运行状态,即对其进行重启。
如果应用本身能够检测到任意类型的错误并自行终止,凭借 process health check 就足够完成健康检查任务。

Liveness Probes

假如应用会进入某种死锁状态,进程并未停止运行,因而从 process health check 的角度看应用仍然是健康的。Kubernetes 可以通过 liveness probes 来检测此类错误。
能够从应用外部执行健康检测,而不是仅仅依靠应用本身,这一点是非常重要的。因为有些错误有可能会阻止应用本身的 watchdog 对外报告异常。
liveness probes 看上去和 process health check 非常相似,它们都会在检测到异常时重启容器。但前者提供了更多的灵活性:

  • HTTP probe:向容器的 IP 地址发起 HTTP GET 请求,期待获取一个成功的 HTTP 响应码(200 - 399)
  • TCP Socket probe:测试是否能完成完整的 TCP 连接
  • Exec probe:在容器内部执行任意的命令,期待获取一个成功的退出码(0)

基于 HTTP 的 liveness probe 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Pod
metadata:
name: pod-with-liveness-check
spec:
containers:
- image: k8spatterns/random-generator:1.0
name: random-generator
env:
- name: DELAY_STARTUP
value: "20"
ports:
- containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30

其中 httpGet 中的 path 项用于配置 HTTP probe 执行健康检测时请求的端点;initialDelaySeconds 用于配置执行第一次检测前等待的时间,以等待应用启动后完成 warm up。

需要注意的是,未通过 liveness probe 检查的后果就是容器被重启,若容器重启对于解决问题没有任何效果,则 liveness probe 本身也不会再有任何其他作用。

Readiness Probes

Liveness 检查通过杀掉不健康的容器并将它们替换为新的容器实例,来确保应用处于健康状态。但有些时候容器遇到问题,重启它们并不会令其恢复健康。最常见的情况就是容器正处于启动过程中,还没有准备好处理任何请求。或者有可能容器负载过高导致延迟极度增长。

在上述场景下,Kubernetes 提供了 readiness probe 特性。Readiness 检查和 Liveness 检查提供的检测方法是一样的(都是 HTTP、TCP 和 Exec),只有触发的操作不同。
失败的 Readiness 检查会将容器从 Service 端点移除,确保其不再对外提供任何服务。它关注的重点在于容器是否已经准备好,有些容器在启动时需要一定的 warm up 时间才能处理请求。
Readiness probe 在容器启动后依然会定期运行,从而将不能对外提供服务的容器屏蔽掉,保证未准备好的容器不会接收到外部的请求。

即 Liveness probe 触发的操作是重启容器,目的是不健康的容器尽可能恢复服务;Readiness probe 触发的操作是将容器从 Service 移除,目的是确保不健康的容器不会对外提供服务,用户的请求只会转发到健康的容器。
当然在容器重启时,Kubernetes 也会尽力确保该容器不会再收到用户请求,不管 Readiness probe 是否通过。

Readiness probe 示例:

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: pod-with-readiness-check
spec:
containers:
- image: k8spatterns/random-generator:1.0
name: random-generator
readinessProbe:
exec:
command: [ "stat", "/var/run/random-generator-ready" ]

Process health check 和 liveness probe 的目的都是通过重启容器来使应用能从错误中自动恢复。Readiness probe 则力求为处于恢复中的容器争取足够的时间。

总结

容器技术为打包和运行应用实现了一系列统一的接口,从而可以将应用作为黑盒(black box)看待。
然而任何致力于成为云原生应用的容器,必须为运行时环境提供一系列必要的 API,对容器的健康状态进行统一的观测,并执行对应的操作。这是容器能统一地实现自动化升级和生命周期管理的基础需求,从而提高系统的稳定性和用户体验。
这意味着容器化应用必须为多种不同的健康检测(liveness 和 readiness)提供需要的 API。
甚至更优异的应用还必须为管理平台提供其他手段,以方便更好地观测容器化应用的状态,比如与 Prometheus 进行整合。将应用视为一种黑盒,同时实现必须的 API 接口,方便平台对其进行监控和管理。

参考资料

Kubernetes Patterns