原文:https://i4t.com/4424.html首先我们从操作系统上简单分析一下“优雅关闭Pod”这个名词,比如我们的windows关闭系统会先退出软件,然后逐步达到关机。与之相反的是硬关机(Hardshutdown)。简单理解就是直接给微服务拔掉电源,由网关将流量分发到各个Pod节点。比如我们上线更新的时候如果在Pod的时候直接杀掉Pod,那么这部分流量就不能被正确的处理,会影响一些用户。一般来说网关或者注册中心会一直保持我们的服务在心跳,心跳超时后会自动移除我们的服务,但是有一个问题就是超时时间可能是30秒或者60秒。虽然不会影响我们的系统,但是会给用户带来轻微的抖动。如果我们在停止前执行命令通知网关或者注册中心主机下线,那么注册中心会标记主机下线,不做流量转发,对用户没有影响。这是elegantStop,最小化滚动更新的影响PodHookPodHook由kubelet发起,在容器内进程启动前或容器内进程终止前运行,包含在容器的生命周期中。我们可以在k8s中同时为pod中的所有容器配置hooks。理想状态是pod被优雅地释放,新的pod产生。然而,并不是每个Pod都会这么顺利。Pod卡死,无法处理优雅退出命令或优雅退出操作逻辑有bug,陷入死循环代码问题,导致执行命令无效。针对以上问题,k8sPod终止进程也有一个“最大可容忍时间”,即graceperiod(定义在pod的.spec.terminationGracePeriodSeconds字段中)。该值默认为30秒。当我们执行kubectldelete时,也可以通过--grace-period参数指定一个优雅退出时间来覆盖Pod中的配置。如果我们配置的graceperiod超过时间,k8s只能选择强行killPodKubernetes为我们提供了两个钩子函数:PostStart:这个钩子在容器创建后立即执行。但是,不能保证挂钩会在容器ENTRYPOINT之前运行,因为没有参数传递给处理程序。主要用于资源部署和环境准备。但是需要注意的是,如果hook时间过长导致运行失败或者挂起,容器将无法进入Running状态。PreStop:在容器终止之前立即调用挂钩。它是阻塞的,意味着它是同步的,所以它必须在删除容器的调用可以继续之前完成。主要用于优雅关闭应用,通知其他系统等。如果hook在执行过程中挂了,Pod阶段会一直停留在Running状态,不会到达failed状态。如果PostStart或PreStop挂钩失败,它将终止容器。所以我们应该让钩子函数尽可能的轻量级。当然,在某些情况下,长时间运行的命令是合理的,例如在停止容器之前预先保存状态。这里简单介绍一下Pod的终止过程。用户发送命令删除Pod,Pod进入Terminating状态。该服务删除Pod节点。当kubelet看到Pod已被标记为终止时,它开始执行preStop钩子。如果preStop钩子的运行时间超过了宽限期,kubelet就会发送SIGTERM并等待2秒。官方文档介绍PodHook钩子函数中有Exec和HTTP两种方法。-对容器上的特定端点执行HTTP请求基于PostStart命令演示首先,我们将首先演示PostStart的两种方式。第一个Exec我们回显一段追加到/tmp/message,在Pod启动前运行cat>>exec_test。yaml</tmp/message'EOF使用kubectlapply-fexec_test.yaml创建它。您可以通过以下方式查看结果。pod目录下已经有我们写在yaml文件中的测试文件[root@abcdockeryaml]#kubectlgetpodNAMEREADYSTATUSRESTARTSAGEabcdocker1/1Running037s[root@abcdockeryaml]#kubectlexec-it-ndefaultabcdocker/bin/bashroot@abcdocker:/#cat/tmp/messagehttps://i4t.comroot@abcdocker:/#root@abcdocker:/#exit容器创建后,Kubernetes立即发送postStart事件,但不保证postStart一定会在容器的入口点被称为处理程序之前被调用。postStart处理程序相对于容器的代码异步运行,但Kubernetes对容器的管理会阻塞,直到postStart处理程序完成。在postStart处理程序完成之前,容器的状态不会设置为RUNNING。第二种HTTP方法使用HttpGet配置Host、Path和PortapiVersion:v1kind:Podmetadata:name:abcdockerlabels:name:abcdockerspec:containers:-name:abcdockerimage:nginxports:-containerPort:80lifecycle:postStart:httpGet:host:i4t.com路径:index.html端口:80这里不做演示,因为log中看不到这个请求。该演示基于PreStop环境。充值,部分请求仍然分发到终止的容器(没有配置hook,熟悉默认环境),导致服务500错误,这部分错误请求数据占用较少,因为Pod滚动更新是一对一。因为部分用户会出现服务器错误,考虑使用优雅的终止方式,尽量减少错误请求,直到滚动更新不影响用户执行中间层服务器的负载均衡和故障转移。每个服务启动时,都会注册自己的信息(IP、端口、服务信息等),更新自己的租约,可以从eureka获取其他微服务的地址信息,并执行相关逻辑。由于Eureka默认心跳检测是30秒,K8S下线Pod时Eureka会出现30秒的异常问题,所以我们需要在Pod停止前发送请求通知Eureka下线,这样优雅停止对用户的影响最小。具体yaml如下:apiVersion:v1kind:Podmetadata:name:abcdockerlabels:name:abcdockerspec:containers:-name:abcdockerimage:nginxports:-containerPort:80lifecycle:preStop:exec:command:-bash--c-'curl-XPOST--dataDOWNhttp://127.0.0.1:8080/service-registry/instance-status-H"Content-Type:application/vnd.spring-boot.actuator.v2+json;charset=UTF-8";sleep30'#######参数说明127.0.0.1:8080#代表eureka地址service-registry#代表注册中心DOWN#执行down请求sleep#等待30秒当我们删除Pod,我们将执行上面的命令操作并等待30秒[root@yzsjhl82-135yaml]#kubectlgetpodNAMEREADYSTATUSRESTARTSAGEabcdocker1/1Running02m16s[root@yzsjhl82-135yaml]#kubectldeletepodabcdockerpod"abcdocker"deleted#不会立即删除Pod,而是在Exec中执行命令,等待30秒。配置中增加了一个sleep时间,主要作为一个servicestopbuffertime总结:Hook调用日志不暴露给Pod事件,所以只能通过describe命令获取。如果是正常操作,是不会有事件的。如果有错误,可以看到FailedPostStartHook和FailedPreStopHook等事件,如果Hook调用有错误,Pod状态不会是Running