k8s hook钩子
k8s hook钩子
简介
在介绍优雅停机之前,我们先来了解下k8s的容器都有哪些生命周期钩子?作用是什么?要怎么使用?Kubernetes的容器有两种生命周期钩子(Lifecycle Hooks):
PostStart这个钩子会在容器被创建后立即执行,但无法保证会在容器的起始点 ENTRYPOINT之前执行,如果执行时间太长,将会阻止Pod状态进入running,可用于数据初始化、容器启动回调等场景。如果需要保证在应用程序启动前就要执行完的任务,可以考虑放在初始化容器( Init Containers)中去实现。
PreStop这个钩子会在容器被结束前执行,执行期间Pod状态为 Terminating,运行时间受终止宽限期( terminationGracePeriodSeconds)约束,超出宽限期Pod将被强制杀死,可用于容器回收前的数据清理、优雅停机等场景。
上述的两个钩子(PostStart 和 PreStop)都有四种类型,
分别为:exec、httpGet、tcpSocket 和 sleep。
由于这四种钩子类型在 PostStart 和 PreStop 中的使用方法一致,下面以 PreStop 为例介绍这四种钩子类型的使用方法:
exec
(执行shell指令,可以是指令或shell脚本, 退出状态码为 0则为成功)
yaml
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "echo 'Container is stopping'"]
shell脚本模式
yaml
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "/data/scripts/preStop.sh"]
httpGet
(执行http get请求,响应状态码在[200,400)区间则为成功)
yaml
lifecycle:
preStop:
httpGet:
path: /shutdown # 请求的uri
port: 8080 # 端口
host: api.yilingyi.com # 主机域名,不加该字段将请求Pod本身
scheme: HTTP # http协议,默认值HTTP,支持HTTP、HTTPS
tcpSocket
(执行tcp socket请求,TCP连接成功建立则为成功)
yaml
lifecycle:
preStop:
tcpSocket:
port: 8080
sleep(将容器暂停5秒,Kubernetes 1.30的新特性 PodLifecycleSleepAction,待验证)
yaml
lifecycle:
preStop:
sleep:
seconds: 5
注意事项
请注意,如果 PostStart 或 PreStop 回调失败,容器将被杀死,所以回调处理的程序应尽量轻量级及把控好执行的时间。
微服务优雅停机实现
本文将以 k8s + SpringBoot + Nacos 作为案例,介绍在实际业务场景中如何实现微服务的优雅停机,从而实现代码发布时的零宕机。首先,先看看 pod 的默认删除过程:
Kube-apiserver 接收到 pod 的删除请求,在 Etcd 上更新 pod 的状态为 Terminating;
Kubelet 清理节点上容器相关的资源,如存储、网络;
Kubelet 向容器发送 SIGTERM,如果容器内进程没有任何配置,则容器立即退出;
如果容器在默认的 30 秒内没有退出,Kubelet 将发送 SIGKILL 并强制其退出。
可以看出,在没有配置优雅停机之前,pod 的删除相当暴力,所以为了更加优雅,我们加入了 preStop hook,并且将终止宽限期延长,具体实现如下:
preStop hook 做了两件事情:
nacos 反注册(也称 实例注销),确保在实例关闭期间不会再有新的请求被路由到该实例;
sleep 35s,nacos 客户端的实例缓存为 30s,30s 后会重新拉取实例信息,超时为 10s,一般不用 10s 这么长,所以我们设置为 35s;
springboot 开启优雅停机后,最大等待时间为 30s;
terminationGracePeriodSeconds 默认为 30s,远小于 preStop 和 springboot 的时间之和,所以我们需要将其调大,我这里设置的是 60s;
其实在 terminationGracePeriodSeconds 耗尽后,k8s 还给了一个 2s 的额外宽限期,最后才执行 SIGKILL。
04 操作步骤
在 SpringBoot > 2.3.0 的版本后支持应用程序优雅停机,需要在 java 微服务的配置中设置如下两个属性,这一步很重要!!!
yaml
server:
# 默认值 immediate: 即立即关闭,graceful: 即优雅停机
shutdown: graceful
spring:
lifecycle:
# 优雅停机最大等待时间,默认30s
timeout-per-shutdown-phase: 30s
接着是在微服务的 yaml 文件加上优雅停机的配置:
yaml
深色版本
apiVersion: v1
kind: Pod
metadata:
name: sre-yilingyi
spec:
containers:
- name: sre-yilingyi
image: 'sre/yilingyi:1.0.0'
env:
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
lifecycle:
preStop:
exec:
command:
- /bin/sh
- '-c'
- >-
curl -s --connect-timeout 10 -m 20 -X POST "http://nacos.yilingyi.com:8848/nacos/v1/ns/instance?port=8080&healthy=true&ip=${POD_IP}&weight=1&enabled=false&serviceName=sre-yilingyi&encoding=GBK&namespaceId=production" &&
sleep 35
terminationGracePeriodSeconds: 60
至此,完成微服务的优雅停机配置。
http是一个双向请求
启动、退出动作
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: harbor.hongfu.com/library/myapp:v1
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/bin/sh", "-c", "echo Hello from the poststop handler > /usr/share/message"]