回调函数
Kubernetes 为容器提供了生命周期回调。 回调使容器能够了解其管理生命周期中的事件,并在执行相应的生命周期回调时运行在处理程序中实现的代码。Kubernetes 支持 PostStart 和 PreStop 事件。 当一个容器启动后,Kubernetes 将立即发送 PostStart 事件;在容器被终结之前, Kubernetes 将发送一个 PreStop 事件。
PostStart
PostStart回调在容器创建后立即执行,但是PostStart并不能保证在容器ENTRYPOINT之前运行,所以PostStart的执行相对于容器的代码执行是异步的。
PostStart主要用于资源部署、环境准备等。
只有 PostStart 处理函数执行完毕,容器的状态才会变成 RUNNING。
PreStop
PreStop在容器终止前立即被调用,它是阻塞的,所以它必须在删除容器的调用触发之前完成。
PreStop主要用于优雅关闭应用程序、通知其他系统等。
如果钩子在执行期间挂起,Pod阶段将停留在Running状态并且不会达到failed状态。
另外,PreStop的执行现对于SIGTERM信息,也是异步的,k8s也不会等待PreStop执行完成。
Pod生命周期
pod的生命周期,它主要包含以下的过程:
- pod创建;
- 运行初始化容器(init container);
- 运行主容器(main container);
- 容器启动后钩子(post start)、容器终止前钩子(pre stop),在启动后就执行
post start
; - 容器的存活性检测(liveness probe)、就绪性检测(readiness probe)。
- 容器启动后钩子(post start)、容器终止前钩子(pre stop),在启动后就执行
- pod终止过程,pod完全退出之前执行
pre stop
操作,也就是收尾工作。
优雅关闭
PreStop是实现Pod优雅关闭的重要工具。
进行Pod优雅关闭的重要性主要体现在以下几个方面:
- 避免服务中断:通过优雅关闭,Pod可以在终止前完成当前正在处理的请求,确保服务不会因为Pod的突然终止而中断。
- 确保数据一致性:优雅关闭允许Pod在终止前通过PreStop Hook完成必要的数据持久化或事务处理,从而确保数据的一致性。
- 最小化用户体验影响:通过优雅关闭,可以避免将流量路由到已经被删除的Pod,减少用户请求处理失败的可能性。在滚动更新或扩展Pod时,优雅关闭能够确保服务的平滑过渡,对用户来说几乎是无感知的。
- 合理利用资源:优雅关闭允许Pod在终止前释放占用的资源,避免资源浪费和泄露,提高资源的利用率。
Pod终止过程:
- 【第一步】用户发出删除 pod 命令
- 【第二步】K8S 会给旧POD发送SIGTERM信号;将 pod 标记为“Terminating”状态;pod 被视为“dead”状态,此时将不会有新的请求到达旧的pod;
- 【第三步】并且等待宽限期(
pod.spec.terminationGracePeriodSeconds
参数定义,默认情况下30秒
)这么长的时间 - 与第三步同时运行,监控到 pod 对象为“Terminating”状态的同时启动 pod 关闭过程
- 与第三步同时进行,endpoints 控制器监控到 pod 对象关闭,将pod与service匹配的 endpoints 列表中删除
- 如果 pod 中定义了
preStop
处理程序,则 pod 被标记为“Terminating
”状态时以同步的方式启动执行preStop
;若宽限期结束后,preStop 仍未执行结束,第二步会重新执行并额外获得一个2秒
的小宽限期(最后的宽限期,所以定义preStop 注意时间,和terminationGracePeriodSeconds 参数配合使用), - Pod 内对象的容器收到 TERM 信号
- 宽限期结束之后,若存在任何一个运行的进程,pod 会收到 SIGKILL 信号
- Kubelet 请求 API Server 将此 Pod 资源宽限期设置为0从而完成删除操作
[!NOTE]
如果应用程序完成关闭并在terminationGracePeriod完成之前退出,Kubernetes会立即进入下一步。
总的来说,Pod优雅关闭是Kubernetes中一个重要的功能,它结合了PreStop Hook和宽限期等机制,确保Pod在终止前能够优雅地完成必要的清理工作,从而保持服务的稳定性和可用性、确保数据一致性、提升用户体验和合理利用资源。在进行Pod管理时,应该充分了解和利用Pod优雅关闭的功能。
PostStart对比初始化容器
PostStart容器是在一个Pod中,所有容器启动之后启动的一个容器。与Init Container不同,PostStart容器不需要先于其他容器启动,它的主要作用是在所有容器启动之后,完成一些额外的任务。
可以使用PostStart容器来进行一些初始化或准备工作,例如配置一些环境变量或启动一个后台服务。PostStart容器的输出将被记录在Pod的日志中。
Init Container用于在启动其他容器之前,执行一些必要的任务。例如,加载一些配置文件或检查一些网络连接。Init Container是按顺序启动的,并且只有在所有的Init Container都成功完成之后,才会启动其他容器。
PostStart容器则用于在所有其他容器启动之后,执行一些附加的任务。例如,进行一些初始化或准备工作。PostStart容器在所有其他容器启动之后启动,且所有容器启动完毕之前,Pod将一直处于容器启动中的状态。
应用实战
PostStart做准备工作,PreStop优雅关闭Nginx
使用官网案例,PostStart 命令在容器的 /usr/share
目录下写入文件 message
。 命令 PreStop 负责优雅地终止 nginx 服务。
apiVersion: v1
kind: Pod
metadata:name: lifecycle-demo
spec:containers:- name: lifecycle-demo-containerimage: nginxlifecycle:postStart:exec:command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]preStop:exec:command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
使用 shell 连接到 Pod 里的容器:
kubectl exec -it lifecycle-demo -- /bin/bash
在 shell 中,验证 postStart
处理函数创建了 message
文件:
root@lifecycle-demo:/# cat /usr/share/message
命令行输出的是 postStart
处理函数所写入的文本:
Hello from the postStart handler
设置宽限期,优雅关闭Nginx
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deployment
spec:replicas: 3selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:latestports:- containerPort: 80lifecycle:preStop:exec:command: ["/usr/sbin/nginx", "-s", "quit"]terminationGracePeriodSeconds: 120 # 设置优雅终止的超时时间为 120 秒(2 分钟)