从springBoot健康检查到k8s探针的问题定位

环境:

k8s、springboot、mysql、flyway、Spring boot 2.0 Actuator健康检查组件等

问题:

项目中集成了flyway,当项目第一次启动时,都会初始化flyway文件,再加上各种组件的初始化,导致主服务启动的时间达到7分钟;

另外pod有时候会自动重启,如果在初始化flyway时中断重启,那么下次启动时程序会报flyway的问题,导致服务永远起不来;

查看pod状态,一直显示健康检查失败;

现象:
健康检查失败:

在这里插入图片描述

pod重启了3次,显示running状态,但是并没有ready;

程序日志:报flyway初始化异常;

排查过程:

1、服务启动慢,启动过程中突然中断,以为服务器内存不够或者cpu不够

给服务加内存:

resources:
  requests:
    memory: 6Gi
    cpu: 0.5
  limits:
    memory: 8Gi
    cpu: 1

在服务启动时,一直使用命令查看pod占用内存情况:

kubectl top pod

发现启动时,内存就只吃了几百兆,cpu也只用的500m左右;

2、经过步骤一的步骤排查,确定不是服务器环境问题;但是启动过程中老是中断,而且查看pod状态时,显示健康检查拒绝;服务就算不中断,启动起来也需要个六七分钟:

仔细看了看服务yaml的健康检查参数:

# 存活探针
livenessProbe:
  httpGet:
    path: /iot/sacp/actuator/health		# 健康检查接口
    port: sacp
    scheme: HTTP
  timeoutSeconds: 30			# 访问接口的超时时间
  initialDelaySeconds: 20		# 初始化延迟时间  即20s开始访问健康检查接口
  periodSeconds: 10				# 20s后 每10s访问一次健康检查接口 探测频率
  successThreshold: 1			# 探测至少成功1次,就认为pod是健康的
  failureThreshold: 20			# 探测连续失败20次,任务此pod是不健康的; 此时kubectl会重启pod
# 就绪探针
readinessProbe:
  failureThreshold: 20
  initialDelaySeconds: 20
  periodSeconds: 10
  successThreshold: 1
  tcpSocket:
    port: sacp
  timeoutSeconds: 30

总结起来就是:由于程序启动过慢,健康检查探测了20 + 20*10 = 220s;程序还没有起来;此时kubectl会自动重启容器;如果处于flyway文件初始化阶段,那么中断了初始化,程序就会一直起不来;如果重启之后能够正常起来,则pod状态仍然是健康检查失败,拒绝健康检查;

健康检查一直被拒绝、失败的原因是,因为pod一直起不来,健康检查接口访问不通,所以拒绝、失败;pod中断则是因为健康检查失败次数太多,kubectl依照重启策略进行了中断重启;

修复:

使用启动探针startupProbe:k8s在 v1.16 中添加了 startup 探针作为 Alpha 功能,并在 v1.18 中升级为 Beta;

当程序启动时,就开启动启动探针,然后开始探测,只要探测成功一次,就不在探测(说明程序启动成功),此时存活探针和就绪探针才会开始工作;通过将 failureThreshold * periodSeconds 参数设置为足够长的时间来应对糟糕情况下的启动时间;

startupProbe:
  httpGet:
    path: /iot/sacp/actuator/health
    port: sacp
    scheme: HTTP
  failureThreshold: 37		
  periodSeconds: 10

大概估一下程序启动的时间六七分钟;因此设置 37 * 10 = 370s; 这意味着当程序启动,有370s的启动时间,启动探针在这个过程中一直探测,只要程序探测成功一次,存活探针才会工作;那么我们的pod就不会再发生中断重启了;

环境组件的简单使用:
springBoot Actuator:
项目引入依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

只要加上了这個 maven dependency,SpringBoot 在运行时就会开放/actuator/health和/actuator/info这两个endpoint,我們就可以通过这两个 endpoint 查看当前 SpringBoot 运行的情況;

暴露和关闭端点简单配置:
# 开放所有端点endpoints(不包含shutdown端点)
management.endpoints.web.exposure.include=*
 
# 指定开放某些端点
management.endpoints.web.exposure.include=beans,mappings
 
# exclude用来关闭端点
# exclude通常会跟include一起用,就是先include了全部,然后再exclude /actuator/beans这个endpoint
management.endpoints.web.exposure.exclude=beans
management.endpoints.web.exposure.include=*
 
# 如果要开放/actuator/shutdown,要在额外加上这一行
management.endpoint.shutdown.enabled=true
k8s 探针:
启动探针:startupProbe

使用启动探针保护慢启动容器;有时候,会有一些现有的应用在启动时需要较长的初始化时间,在这种情况下,如果没有配置启动探针,而直接配置存活探针,可能容器在启动过程中,健康检查长时间没有响应,达到存活探针的失败次数,会中断启动而重新启动容器;

在一些情况下,往往只是新的Pod完成自身初始化,系统尚未完成EndPoint、负载均衡器等外部可达的访问信息刷新,老得Pod就立即被删除,最终造成服务短暂的额不可用,这对于生产来说是不可接受的;

存活探针:livenessProbe

readiness 探针可以让 kubelet 知道应用程序何时准备接受新流量,如果应用程序在进程启动后需要一些时间来初始化状态,要配置 readiness 探针让 Kubernetes 在发送新流量之前进行等待;

就绪探针:readinessProbe

Liveness 探针liveness 探针用于重新启动不健康的容器。Kubelet 会定期地 ping liveness 探针,以确定健康状况,并在 liveness 检查不通过的情况下杀死 Pod。liveness 检查可以帮助应用程序从死锁中恢复。如果不进行 liveness 检查,Kubernetes 会认为死锁中的 Pod 处于健康状态,因为从 Kubernetes 的角度来看,Pod 的子进程仍在运行,是健康的。通过配置 liveness 探针,kubelet 可以检测到应用程序处于不健康状态,并重新启动 Pod 以恢复可用性;

三种探针共有配置:
  • initialDelaySeconds:启动 liveness、readiness 探针前要等待的秒数。
  • periodSeconds:检查探针的频率。
  • timeoutSeconds:将探针标记为超时(未通过运行状况检查)之前的秒数。
  • successThreshold:探针需要通过的最小连续成功检查数量。
  • failureThreshold:将探针标记为失败之前的重试次数。对于 liveness 探针,这将导致 Pod重新启动。对于 readiness 探针,将标记 Pod 为未就绪(unready);
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐