SIGKILL 是硬终止的信号,而 SIGTERM 是通知进程优雅退出的信号
terminationGracePeriodSeconds是pod最多可以容忍的时间,超过这个时间pod会被强制kill
Pod的优雅关闭流程
1.新pod启动——>等待新pod进入Ready状态
2.k8s创建endpoint,将新服务纳入负载均衡
3.旧Pod设置为Terminating状态,并从所有服务的Endpoints列表中删除。
此时旧Pod不再接收新的流量,但pod中的容器不受影响’
4.preStop Hook被执行
preStop Hook是一个发送到Pod中的容器特殊命令或Http请求。
如果应用程序在接收SIGTERM时没有正常关闭,可以使用preStop Hook来触发正常关闭。 接收SIGTERM时大多数程序都会正常关闭,但如果您使用的是第三方代码或管理的系统无法控制,则preStop Hook是在不修改应用程序的情况下触发正常关闭的好方法。
5.SIGTERM信号被发送到Pod
此时,Kubernetes将向pod中的容器发送SIGTERM信号。这个信号让容器知道它们很快就会关闭
6.Kubernetes等待优雅的终止
此时,Kubernetes等待指定的时间称为优雅终止宽限期。默认情况下,这是30秒。值得注意的是,这与preStop Hook和SIGTERM信号并行发生。Kubernetes不会等待preStop Hook完成。
如果你的应用程序完成关闭并在terminationGracePeriod完成之前退出,Kubernetes会立即进入下一步。
如果您的Pod通常需要超过30秒才能关闭,请增加优雅终止宽限期。通过在Pod YAML中设置terminationGracePeriodSeconds选项来实现。
spec:
terminationGracePeriodSeconds: 30
7.SIGKILL信号被发送到Pod,并删除Pod
如果容器在优雅终止宽限期后仍在运行,则会发送SIGKILL信号并强制删除。与此同时,所有的Kubernetes对象也会被清除
有些步骤是同时执行的。因此有可能会导致该Pod仍然列在服务的Endpoints中并仍然接收流量,而它已经收到SIGTERM并且已经停止,因此负载均衡器上可能会有一些Http 504。目前解决这个问题可以使用preStop Hook 在容器收到SIGTERM时sleep一段时间,以确终止期间的流量可以正确处理
spec:
containers:
- name: nginx
image: nginx
lifecycle:
preStop:
exec:
command:
- sleep
- 30
terminationGracePeriodSeconds: 60
preStop Hook并不会影响SIGTERM的处理,因此有可能preStopHook还没执行完就收到SIGKILL导致容器强制退出。如果preStop Hook设置了n秒,需要设置terminationGracePeriodSeconds+n秒
容器生命周期钩子
容器钩子分为两类触发点:容器创建后PostStart和容器终止前PreStop
PostStart
在容器创建之后立即执行,如果钩子花费太长时间以至于容器不能运行或者挂起, 容器将不能达到running状态
PreStop
这个钩子在容器终止之前立即被调用,它是同步的, 必须在删除容器的调用发出之前完成,如果钩子在执行期间挂起, Pod阶段将停留在running状态并且永不会达到failed状态,如果PostStart或者PreStop钩子失败, 容器将会被kill
容器可以通过实现和注册钩子的处理程序来访问钩子,可以为容器实现两种类型的钩子处理程序:
Exec - 在容器的cgroups和命名空间内执行一个特定的命令,比如pre-stop.sh。
该命令消耗的资源被计入容器
lifecycle:
postStart:
exec:
command: ["bash", "/data/sed.sh"]
preStop:
exec:
command: ["/bin/sh","-c","/pre-stop.sh"]
HTTP - 对容器上的特定的端点执行HTTP请求。
spring框架服务优雅停止
对于spring框架使用 spring cloud 的服务发现组件Eureka ,它是一个服务注册中心,用于定位服务来进行中间层服务器的负载均衡和故障转移。服务启动时,会向Eureka Server注册自己的信息(IP,端口,服务信息等),Eureka Server存储这些信息
微服务启动后,会周期性(默认30秒)的向Eureka Server发送心跳以续约自己的”租期”,并可以从eureka中获取其他微服务的地址信息,执行相关的逻辑
禁用某个服务
curl -X PUT \
"http://admin:admin@ip:port/eureka/apps/{appName}/{instanceId}/status?value=OUT_OF_SERVICE"
我们可以把这样停止服务的命令写到脚本中,在pod关闭前执行该脚本