K8S环境下配置SpringBoot健康检查(活性探针)
- k8s 探针配置:
- 具体配置如下
- 后续使用问题
主要是通过
K8S
的健康检查探针,对SpringBoot
应用进行运行健康监控,检测pod
是否存活,从而对pod
进行重启之类的操作
k8s 探针配置:
kubernetes
提供了三种探针(支持exec、tcp和http方式)来探测容器的状态:
LivenessProbe
:容器存活性检查,用于判断容器是否健康,告诉 kubelet 一个容器什么时候处于不健康的状态。如果 LivenessProbe 探针探测到容器不健康,则 kubelet 将删除该容器,并根据容器的重启策略做相应的处理。如果一个容器不包含 LivenessProbe 探针,那么 kubelet 认为该容器的 LivenessProbe 探针返回的值永远是 Success;
ReadinessProbe
:容器就绪性检查,用于判断容器是否启动完成且准备接收请求。如果ReadinessProbe探针探测到失败,Endpoint Controller 将从 Service 的 Endpoint 中删除包含该容器所在 Pod 的 IP 地址的Endpoint条目。如果容器不提供就绪态探针,则默认状态为 Success。
StartupProbe
: 容器启动检查,指示容器中的应用是否已经启动。如果提供了启动探针,则所有其他探针都会被禁用,直到此探针成功为止。如果启动探测失败,kubelet 将杀死容器,而容器依其重启策略进行重启。 如果容器没有提供启动探测,则默认状态为 Success。
三种探针共有配置:
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 版本中得到修复。
具体配置如下
推荐使用SpringBoot
版本在2.3.2
版本以上,这样才好的支持开启应用探针功能,所以先检查项目的版本号
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/>
</parent>
再引入pom
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在yml
文件中加入以下配置
server:
port: 8080
shutdown: graceful # 默认为IMMEDIATE,表示立即关机;GRACEFUL表示优雅关机
servlet:
context-path: /test
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # 停机过程超时时长设置30s,超过30s,直接停机
application:
name: test
management: # 开启shutdown和health端点
endpoint:
health:
probes:
enabled: true
health:
livenessstate:
enabled: true
readynessstate:
enabled: true
在k8s
部署文件中加入以下配置
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ service_name }}--api
spec:
selector:
matchLabels:
app: {{ service_name }}--api
replicas: {{ replicas }} # Pod副本数
strategy:
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
template:
metadata:
name: {{ service_name }}--api
labels:
app: {{ service_name }}--api
spec:
containers:
- name: {{ service_name }}--api
# 启动探针(容器启动检查)
startupProbe:
httpGet:
path: /test/actuator/health
port: 8080
scheme: HTTP
failureThreshold: 60
periodSeconds: 10
# 存活探针(容器存活性检查)
livenessProbe:
httpGet:
path: /test/actuator/health # 健康检查接口
port: 8080
scheme: HTTP
timeoutSeconds: 30 # 访问接口的超时时间
initialDelaySeconds: 20 # 初始化延迟时间 即20s开始访问健康检查接口
periodSeconds: 10 # 20s后 每10s访问一次健康检查接口 探测频率
successThreshold: 1 # 探测至少成功1次,就认为pod是健康的
failureThreshold: 10 # 探测连续失败10次,任务此pod是不健康的; 此时kubectl会重启pod
# 就绪探针(容器就绪性检查)
readinessProbe:
httpGet:
path: /test/actuator/health
port: 8080
scheme: HTTP
timeoutSeconds: 30
initialDelaySeconds: 20
periodSeconds: 10
successThreshold: 1
failureThreshold: 10
image: "{{ image_name }}"
imagePullPolicy: Always
command:
- "/app/entrypoint.sh"
- "api"
volumeMounts:
- name: config-envs
mountPath: /etc/config/envs
readOnly: true
- name: secret-mysql-config
mountPath: /etc/secrets/mysql/config
readOnly: true
- name: secret-redis-config
mountPath: /etc/secrets/redis/config
readOnly: true
- name: secret-storage-sa
mountPath: /etc/secrets/storage/sa
readOnly: true
- name: secret-storage-config
mountPath: /etc/secrets/storage/config
readOnly: true
resources: # 容器资源管理
limits: # 资源限制(监控使用情况)
cpu: "{{ cpu_limits }}"
memory: "{{ memory_limits }}"
requests: # 最小可用资源(灵活调度)
cpu: "{{ cpu_requests }}"
memory: "{{ memory_requests }}"
nodeSelector:
{{ k8s_priority_node_pool }}
affinity: # 设置调度策略,采取多主机/多可用区部署
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- {{ service_name }}--api
topologyKey: "kubernetes.io/hostname"
volumes:
- name: config-envs
configMap:
name: {{ service_name }}--config--envs
- name: secret-mysql-config
secret:
secretName: {{ service_name }}--config--mysql
- name: secret-redis-config
secret:
secretName: {{ service_name }}--config--redis
- name: secret-storage-sa
secret:
secretName: {{ service_name }}--sa--storage
- name: secret-storage-config
secret:
secretName: {{ service_name }}--config--storage
后续使用问题
更新:
用了一段时间后,在某一次服务器资源不够后,导致服务全部停止,然后多个pod陷入无限重启状态。原因是多个pod一直在探测是否有成功启动的pod,然后导致先后批次的pod都在等待对方启动完成,后面发现是重启的时间设置的不合理导致的。后续有时间专门出一篇文章细说一下。