spring boot 应用在 k8s 中的健康检查(一)

一、概述

在 spring boot 2.3 中引入了容器探针,也就是增加了 /actuator/health/liveness/actuator/health/readiness 这两个健康检查路径,对于部署在 k8s 中的应用,spring-boot-actuator 将通过这两个路径自动进行健康检查。本文主要根据官方文档的描述实践并记录使用流程,从如下几个方面进行介绍:

  1. k8s 中的健康检查
  2. spring-boot-actuator 中的 k8s 探针
  3. spring boot 健康检查在 k8s 中的实践

二、K8s 容器健康检查

1、k8s 中的探针

kubernetes 提供了三种探针(支持exec、tcp和http方式)来探测容器的状态:

  • LivenessProbe:容器存活性检查,用于判断容器是否健康,告诉 kubelet 一个容器什么时候处于不健康的状态。如果 LivenessProbe 探针探测到容器不健康,则 kubelet 将删除该容器,并根据容器的重启策略做相应的处理。如果一个容器不包含 LivenessProbe 探针,那么 kubelet 认为该容器的 LivenessProbe 探针返回的值永远是 Success
  • ReadinessProbe:容器就绪性检查,用于判断容器是否启动完成且准备接收请求。如果ReadinessProbe探针探测到失败,Endpoint Controller 将从 ServiceEndpoint 中删除包含该容器所在 Pod 的 IP 地址的Endpoint条目。如果容器不提供就绪态探针,则默认状态为 Success
  • startupProbe: 容器启动检查,指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success

startupProbe 是在k8s v1.16加入了alpha版,官方对其作用的解释是:
Indicates whether the application within the Container is started. All other probes are disabled if a startup probe is provided, until it succeeds. If the startup probe fails, the kubelet kills the Container, and the Container is subjected to its restart policy. If a Container does not provide a startup probe, the default state is Success

2、k8s 的探针处理程序

Probe 探针检查是由 kubelet 对容器执行的定期诊断,kubelet 调用由容器实现的 Handler (处理程序),每一个探针都可以配置如下三种类型的处理程序:

  • ExecAction: 在容器内执行指定命令,如果命令退出时返回码为 0 则认为诊断成功。

通过执行命令的方式来检查服务是否正常,比如使用 cat 命令查看 pod 中的某个重要配置文件是否存在,若存在,则表示pod健康,反之异常。Exec 探测方式的 yaml 文件语法如下:

spec:  
  containers:  
  - name: liveness  
    image: k8s.gcr.io/busybox  
    args:  
    - /bin/sh  
    - -c  
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600  
    livenessProbe:               #选择livenessProbe的探测机制  
      exec:                      #执行以下命令,如果文件存在则探测成功
        command:  
        - cat  
        - /tmp/healthy  
      initialDelaySeconds: 5          #在容器运行五秒后开始探测  
      periodSeconds: 5                #检查时间间隔为 5s 检查一次

  • TCPSocketAction: 对容器的 IP 地址上的指定端口执行 TCP 检查,如果端口打开,则诊断被认为是成功的。
    这种方式与HTTPget的探测机制有些类似,tcpsocket健康检查适用于TCP业务,tcpSocket探测方式的yaml文件语法如下:
spec:  
  containers:  
  - name: goproxy  
    image: k8s.gcr.io/goproxy:0.1  
    ports:  
      - containerPort: 8080  
    #这里两种探测机制都用上了,都是为了和容器的8080端口建立TCP连接  
    readinessProbe:  
      tcpSocket:  
        port: 8080  
      initialDelaySeconds: 5  # 容器启动 5s 后开始第一次就绪性探测
      periodSeconds: 10       # 每 10s 进行一次探测
    livenessProbe:  
      tcpSocket:  
        port: 8080  
      initialDelaySeconds: 15  # 容器启动 15s 后开始第一次就存活性探测
      periodSeconds: 20  # 每 20s 进行一次探测

在上述的yaml配置文件中,两类探针都使用了,在容器启动5秒后,kubelet将发送第一个readinessProbe探针,这将连接容器的8080端口,如果探测成功,则该pod为健康,十秒后,kubelet将进行第二次连接。

除了readinessProbe探针外,在容器启动15秒后,kubelet将发送第一个livenessProbe探针,仍然尝试连接容器的8080端口,如果连接失败,则重启容器。

  • HTTPGetAction: 对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求,如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。

Httpget探测方式的yaml文件语法如下:

spec:  
  containers:  
  - name: liveness  
    image: k8s.gcr.io/liveness  
    livenessProbe:              #采用livenessProbe机制探测  
      httpGet:                  #采用httpget的方式  
        scheme: HTTP         #指定协议,也支持https  
        path: /healthz          #检测是否可以访问到网页根目录下的healthz网页文件  
        port: 8080              #监听端口是8080  
      initialDelaySeconds: 3     #容器运行3秒后开始探测  
      periodSeconds: 3                #探测频率为3秒  

上述配置文件中,探测方式为项容器发送HTTP GET请求,请求的是 8080 端口下的 healthz 文件,返回任何大于或等于200且小于400的状态码表示成功,任何其他代码表示异常。

每次探测都将获得以下三种结果之一:

  • Success(成功):容器通过了诊断。
  • Failure(失败):容器未通过诊断。
  • Unknown(未知):诊断失败,因此不会采取任何行动。

3、k8s 探针的配置

Probe 有如下配置字段,可以使用这些字段精确的控制存活和就绪检测的行为:

  • initialDelaySeconds:容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。
  • periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。
  • timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
  • successThreshold:探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
  • failureThreshold:当探测失败时,Kubernetes 的重试次数。 存活探测情况下的放弃就意味着重新启动容器。 就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。

说明:在 Kubernetes 1.20 版本之前,exec 探针会忽略 timeoutSeconds:探针会无限期地 持续运行,甚至可能超过所配置的限期,直到返回结果为止。这一缺陷在 Kubernetes v1.20 版本中得到修复。

LivenessProbe配置例子:

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 30        // 容器启动30s后启动第一次探测
  periodSeconds: 10              //  每隔10s启动一次探测
  timeoutSeconds: 3               // 超时时间3s
  successThreshold: 1             // 成功1次即表示容器健康
  failureThreshold: 5             // 连续5次失败,则判定容器不健康,默认3次

4、何时使用

何时使用启动探针

kubelet 使用启动探测器可以知道应用程序容器什么时候启动了。 如果配置了这类探测器,就可以控制容器在启动成功后再进行存活性和就绪检查, 确保这些存活、就绪探测器不会影响应用程序的启动。 这可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。

何时使用存活探针

kubelet 使用存活探测器来知道什么时候要重启容器。 例如,存活探测器可以捕捉到死锁(应用程序在运行,但是无法继续执行后面的步骤)。 这样的情况下重启容器有助于让应用程序在有问题的情况下更可用。

何时使用就绪探针

kubelet 使用就绪探测器可以知道容器什么时候准备好了并可以开始接受请求流量, 当一个 Pod 内的所有容器都准备好了,才能把这个 Pod 看作就绪了。 这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。 在 Pod 还没有准备好的时候,会从 Service 的负载均衡器中被剔除的。

5、使用启动探测器保护慢启动容器

有时候,会有一些现有的应用程序在启动时需要较多的初始化时间。 要不影响对引起探测死锁的快速响应,这种情况下,设置存活探测参数是要技巧的。 技巧就是使用一个命令来设置启动探测,针对HTTP 或者 TCP 检测,可以通过设置 failureThreshold * periodSeconds 参数来保证有足够长的时间应对糟糕情况下的启动时间。
所以,前面的例子就变成了:

ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

幸亏有启动探测,应用程序将会有最多 5 分钟(30 * 10 = 300s) 的时间来完成它的启动。 一旦启动探测成功一次,存活探测任务就会接管对容器的探测,对容器死锁可以快速响应。 如果启动探测一直没有成功,容器会在 300 秒后被杀死,并且根据 restartPolicy 来设置 Pod 状态。

参考文章:

官网 k8s 容器探针

你可能感兴趣的:(spring boot 应用在 k8s 中的健康检查(一))