参考文档
掌握SpringBoot-2.3的容器探针:基础篇
Spring Boot 2.3+ Liveness 和 Readness 接口使用
1. 存活探针
-
kubernetes 的探针涉及的内容是很多的,这里只提和 SpringBoot 相关的部分;
-
kubelet 使用存活探针 livenessProbe 来知道什么时候要重启容器;
-
下图是 kubernetes 官网的存活探针示例,几个关键参数已经做了详细说明:
- 可见如果我们的 SpringBoot 应用发布到 kubernetes 环境,只要应用还健康,livenessProbe 对应的地址就要能响应 200-400 的返回码;
2. 就绪探针
- 有时候,应用程序会暂时性的不能提供通信服务。
例如,应用程序在启动时可能需要加载很大的数据或配置文件,或是启动后要依赖等待外部服务。在这种情况下,既不想杀死应用程序,也不想给它发送请求。Kubernetes 提供了就绪探测器来发现并缓解这些情况。容器所在 Pod 上报还未就绪的信息,并且不接受通过 Kubernetes Service 的流量。 - 就绪探测器的配置和存活探测器的配置相似,唯一区别就是要使用 readinessProbe字段,而不是 livenessProbe 字段;
- 简单的说,就绪探针正常的容器,k8s 就认为是可以对外提供服务的,相应的请求也会被调度到该容器上来;
3. Actuator
简单来说,actuator 是用来帮助用户监控和操作 SprinBoot 应用的,这些监控和操作都可以通过 http 请求实现,如下图,http://localhost:8080/actuator/health 地址返回的是应用的健康状态:
3.1 所需依赖
plugins {
id 'org.springframework.boot' version '2.3.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-web'
}
3.2 分组
在 SpringBoot-2.3 版本中,actuator 新增了分组的概念,默认分为 liveness 和 readness 两个组,需显式指定后才能使用,两个地址:/actuator/health/liveness
和 /actuator/health/readiness
,前者用作kubernetes 的存活探针,后者用作 kubernetes 的就绪探针;/actuator/health
接口包含所有的指标
查看当前应用支持的指标,可以设置 management.endpoint.health.show-details=always
后从 /actuator/health
接口获取。
先指定 readness 指标,之后可以将剩余的所有指标设置为 liveness 的指标
management.endpoint.health.show-details=always
management.endpoint.health.group.readiness.include=ping
management.endpoint.health.group.liveness.include=*
management.endpoint.health.group.liveness.exclude=${management.endpoint.health.group.readiness.include}
3.3 测试
3.3.1 health
curl http://localhost:8080/actuator/health
{
"status": "UP",
"components": {
"diskSpace": {
"status": "UP",
"details": {
"total": 499963174912,
"free": 380364800000,
"threshold": 10485760,
"exists": true
}
},
"livenessStateProbeIndicator": {
"status": "UP"
},
"ping": {
"status": "UP"
},
"readinessStateProbeIndicator": {
"status": "UP"
}
},
"groups": [
"liveness",
"readiness"
]
}
3.3.2 readiness
curl http://localhost:8080/actuator/health/readiness
{
"components": {
"ping": {
"status": "UP"
}
},
"status": "UP"
}
3.3.3 liveness
curl http://localhost:8080/actuator/health/liveness
{
"components": {
"custom": {
"details": {
"Status": "Health"
},
"status": "UP"
},
"diskSpace": {
"details": {
"exists": true,
"free": 380286464000,
"threshold": 10485760,
"total": 499963174912
},
"status": "UP"
}
},
"status": "UP"
}
3.4 自定义检查项
自定义一个新的 HealthIndicator
,添加到相应的分组即可, 默认名为类名前缀(第一个字母小写)
@Component
public class CustomHealthIndicator implements HealthIndicator {
private boolean health = true;
@Override
public Health health() {
if (health) {
return Health.up().withDetail("Status", "Health").build();
}
return Health.down().withDetail("Status", "Not Health").build();
}
public void setHealth(boolean health) {
this.health = health;
}
}
测试
添加到分组
management.endpoint.health.group.readiness.include=ping,custom
curl http://localhost:8080/actuator/health/readiness
{
"components": {
"custom": {
"details": {
"Status": "Health"
},
"status": "UP"
},
"ping": {
"status": "UP"
}
},
"status": "UP"
}
4. SpringBoot 对容器环境的判断
SpringBoot 为 kubernetes 提供了两个 actuator 项,但是那些并未部署在 kubernetes 的 SringBoot 应用呢?用不上这两项也要对外暴露这两个服务地址吗?
SpringBoot 判断是否是 kubernetes 环境的逻辑很简单:是否有*_SERVICE_HOST
和 *_SERVICE_PORT
这两个环境变量。
熟悉kubernetes的读者,应该会想起 KUBERNETES_SERVICE_HOST 和 KUBERNETES_SERVICE_PORT,这是 k8s 给 pod 中配置的环境变量,看来 SpringBoot 也是针对 k8s 的这个规则来判定是否是容器环境的。
那非 kubernetes 环境怎么开启探针呢。
- 加上上述两个环境变量。
- 添加配置
management.health.probes.enabled=true
5. 探针的状态
存活探针有两个状态
- CORRECT 表示应用运行中并且内部状态正常
- BROKEN 表示应用运行中并且内部是 BROKEN 状态
就绪探针状态
- ACCEPTING_TRAFFIC 表示应用可以对外提供服务
- REFUSING_TRAFFIC 表示应用无法对外提供服务;
在 SpringBoot 启动过程中,应用、存活探针、就绪探针三者状态对应关系如下图:
在 SpringBoot 停止过程中,应用、存活探针、就绪探针三者状态对应关系如下图:
获取状态
@Resource
ApplicationAvailability applicationAvailability;
public void getStatus(){
LivenessState livenessState = applicationAvailability.getLivenessState();
ReadinessState readinessState = applicationAvailability.getReadinessState();
}
监听状态
@Component
public class StateListener {
@EventListener
public void onStateChange(AvailabilityChangeEvent<?> event) {
event.getState();
}
}
修改状态
@Resource
ApplicationEventPublisher eventPublisher;
public void changeState() {
AvailabilityChangeEvent.publish(eventPublisher, this, LivenessState.BROKEN);
AvailabilityChangeEvent.publish(eventPublisher, this, ReadinessState.REFUSING_TRAFFIC);
}