1-背景
1.1-概述
- Spring项目健康检查地址应使用
/actuator/health/readiness
代替/actuator/health
,以保证项目不因中间件的暂时不可用而重启; - 为保证k8s和非k8s环境的一致性,应通过Spring配置显示开启健康检查探针:
management.endpoint.health.probes.enabled=true
。
1.2-问题
近期经常发生服务因健康检查失败而被重启的问题。
经排查,因nacos不稳定导致服务健康检查失败的情况比较常见,也偶有redis和数据库连接不稳定导致的健康检查失败导致的服务被重启。
1.3-根因
Spring框架的/actuator/health
健康检查的整体状态,会因为任一健康检查子项不健康而不健康;
而nacos、redis、jdbc等子项的健康检查,会发起网络请求,会因为网络的不稳定等因素,出现健康检查失败的情况;
健康检查的本意,是保障服务的持续稳定性,而因为nacos这类非业务关键组件的健康检查失败导致服务被重启,反而得不偿失。
2-带病方案
2.1-修改内容
最直接的解决方案,是通过Spring配置
关闭健康检查时的各子项,包括多种方法:
方法一:关闭指定项的健康检查项
yaml
# bootstrap.yaml|application.yaml
management:
health:
# 关闭nacos配置中心健康检查
nacos-config:
enabled: false
# 关闭nacos注册中心健康检查
nacos-discovery:
enabled: false
# 关闭redis健康检查
redis:
enabled: false
# 关闭jdbc数据源健康检查
db:
enabled: false
优点:
- 细粒度控制;
缺点:
- 每项依赖组件的健康检查都要明确指定,容易遗漏;
方法二:排除指定健康检查项的AutoConfiguration
yaml
# bootstrap.yaml|application.yaml
spring:
autoconfigure:
exclude:
# 排除nacos配置中心健康检查的自动配置
- com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration
# 排除nacos注册中心健康检查的自动配置
- com.alibaba.cloud.nacos.endpoint.NacosDiscoveryEndpointAutoConfiguration
# 排除redis健康检查的自动配置
- org.springframework.boot.actuate.autoconfigure.data.redis.RedisHealthContributorAutoConfiguration
- org.springframework.boot.actuate.autoconfigure.data.redis.RedisReactiveHealthContributorAutoConfiguration
# 排除jdbc数据源健康检查的自动配置
- org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration
优点:
- 细粒度控制;
- 更彻底、更明确地排除自动装配类;
缺点:
- 每项依赖组件的健康检查都要明确指定,容易遗漏;
方法三:默认关闭所有Spring健康检查子项
yaml
# bootstrap.yaml|application.yaml
management:
health:
# 所有健康检查子项默认关闭,可单项打开
defaults:
enabled: false
优点:
- 全局开关,减少配置量、避免遗漏;
- 兼顾细粒度配置,可有选择性打开部分健康检查子项;
缺点:
- 失去了对中间件是否可用状态的可观测性。
2.2-总结
所有方案都是关闭/actuator/health
子项的健康检查,共同缺点是:
- 中间件是否可用的可观测性与服务可用性不可兼得;
- 需要细粒度配置。
3-进阶方案
查询Spring官方文档,发现Spring早就提供了对k8s
专用的健康检查API,可以兼顾中间件的可观测性与服务可用性:
参见:《Kubernetes Probes》
3.1-修改内容
要使用Spring的这种能力,只需两步:
步骤1:打开k8s专用健康检查端点
Spring会自动检查是否处于k8s环境中、并按需启动存活和可读健康检查端点。为了本地、开发、生产的一致性,我们可以通过开关显示打开:
yaml
# bootstrap.yaml|application.yaml
management:
endpoint:
health:
# 显示开启存活和可读探针
probes:
enabled: true
步骤2:替换健康k8s健康检查地址
修改部署模板中健康检查的配置:
yaml
# deployment.yaml
livenessProbe:
httpGet:
path: "/actuator/health/liveness"
port: 8080
failureThreshold: ...
periodSeconds: ...
readinessProbe:
httpGet:
path: "/actuator/health/readiness"
port: 8080
failureThreshold: ...
periodSeconds: ...
3.2-检查效果
步骤1:检查存活和可读端点是否启用
本地启动服务之后,可以通过/actuator/health
是否包含了存活和可读健康检查子项:
请求
shell
curl http://localhost:8080/actuator/health
响应内容
json
{
"status": "UP",
"groups": [
"liveness",
"readiness"
],
"components": {
...
"livenessState": {
"status": "UP"
},
"readinessState": {
"status": "UP"
}
}
...
}
步骤2:检查k8s的健康检查配置是否生效
应用部署后,可以k8s的yaml
配置检查健康检查是否生效。
健康检查地址应该由/actuator/health
被修改为了/actuator/health/readiness
:
yaml
livenessProbe:
httpGet:
path: "/actuator/health/liveness"
port: 8080
failureThreshold: ...
periodSeconds: ...
readinessProbe:
httpGet:
path: "/actuator/health/readiness"
port: 8080
failureThreshold: ...
periodSeconds: ...
步骤3:验证中间件不可用时服务的健康状态
通过配置错误的地址等方法,让某中间件(如redis)不可用:
yaml
# bootstrap.yaml|application.yaml
spring:
data:
redis:
host: error
port: 1234
...
重启服务使配置生效。
1.健康检查根路径为失败状态
发送请求:
shell
curl http://localhost:8080/actuator/health
响应结果:
json
{
"status": "DOWN",
"groups": [
"liveness",
"readiness"
],
"components": {
"livenessState": {
"status": "UP"
},
"nacosConfig": {
"status": "UP"
},
"ping": {
"status": "UP"
},
"readinessState": {
"status": "UP"
},
"redis": {
"status": "DOWN",
"details": {
"error": "org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis"
}
}
}
}
2.存活探针应为成功状态
发送请求:
shell
curl http://localhost:8080/actuator/health/liveness
响应结果:
json
{
"status": "UP"
}
3.可读探针应为成功状态
发送请求:
shell
curl http://localhost:8080/actuator/health/readiness
响应结果:
json
{
"status": "UP"
}
小结
此时k8s中应用应是正常存活且可用的状态,不会重启,证明服务稳定性不受中间件影响。