首页 > 其他分享 >K8S Pod 优雅停机

K8S Pod 优雅停机

时间:2023-05-19 11:03:34浏览次数:50  
标签:容器 停机 Envoy 优雅 最小值 preStop Pod K8S

优雅停止(Gracful Shutdown)与 502/504 报错

如果 Pod 正在处理大量请求(比如 1000 QPS+)时,因为节点故障或「竞价节点」被回收等原因被重新调度, 你可能会观察到在容器被 terminate 的一段时间内出现少量 502/504。

为了搞清楚这个问题,需要先理解清楚 terminate 一个 Pod 的流程:

1.Pod 的状态被设为 Terminating,(几乎)同时该 Pod 被从所有关联的 Service Endpoints 中移除
2.preStop 钩子被执行
a.它的执行阶段很好理解:在容器被 stop 之前执行
b.它可以是一个命令,或者一个对 Pod 中容器的 http 调用
c.如果在收到 SIGTERM 信号时,无法优雅退出,要支持优雅退出比较麻烦的话,用 preStop 实现优雅退出是一个非常好的方式
d.preStop 的定义位置:https://github.com/kubernetes/api/blob/master/core/v1/types.go#L2515
3.preStop 执行完毕后,SIGTERM 信号被发送给 Pod 中的所有容器
4.继续等待,直到容器停止,或者超时 spec.terminationGracePeriodSeconds,这个值默认为 30s
a.需要注意的是,这个优雅退出的等待计时是与 preStop 同步开始的!而且它也不会等待 preStop 结束!
5.如果超过了 spec.terminationGracePeriodSeconds 容器仍然没有停止,k8s 将会发送 SIGKILL 信号给容器
6.进程全部终止后,整个 Pod 完全被清理掉

注意:1 跟 2 两个工作是异步发生的,所以在未设置 preStop 时,可能会出现「Pod 还在 Service Endpoints 中,但是 SIGTERM 已经被发送给 Pod 导致容器都挂掉」的情况,我们需要考虑到这种状况的发生。

了解了上面的流程后,我们就能分析出两种错误码出现的原因:

  • 502:应用程序在收到 SIGTERM 信号后直接终止了运行,导致部分还没有被处理完的请求直接中断,代理层返回 502 表示这种情况
  • 504:Service Endpoints 移除不够及时,在 Pod 已经被终止后,仍然有个别请求被路由到了该 Pod,得不到响应导致 504

通常的解决方案是,在 Pod 的 preStop 步骤加一个 15s 的等待时间。其原理是:在 Pod 处理 terminating 状态的时候,就会被从 Service Endpoints 中移除,也就不会再有新的请求过来了。在 preStop 等待 15s,基本就能保证所有的请求都在容器死掉之前被处理完成(一般来说,绝大部分请求的处理时间都在 300ms 以内吧)。

一个简单的示例如下,它使 Pod 被 Terminate 时,总是在 stop 前先等待 15s,再发送 SIGTERM 信号给容器:

    containers:
    - name: my-app
      # 添加下面这部分
      lifecycle:
        preStop:
          exec:
            command:
            - /bin/sleep
            - "15"

 

更好的解决办法,是直接等待所有 tcp 连接都关闭(需要镜像中有 netstat):

    containers:
    - name: my-app
      # 添加下面这部分
      lifecycle:
      preStop:
          exec:
            command:
            - /bin/sh
            - -c
            - "while [ $(netstat -plunt | grep tcp | wc -l | xargs) -ne 0 ]; do sleep 1; done"

 

如果我的服务还使用了 Sidecar 代理网络请求,该怎么处理?

以服务网格 Istio 为例,在 Envoy 代理了 Pod 流量的情况下,502/504 的问题会变得更复杂一点——还需要考虑 Sidecar 与主容器的关闭顺序:

  • 如果在 Envoy 已关闭后,有新的请求再进来,将会导致 504(没人响应这个请求了)
    • 所以 Envoy 最好在 Terminating 至少 3s 后才能关,确保 Istio 网格配置已完全更新
  • 如果在 Envoy 还没停止时,主容器先关闭,然后又有新的请求再进来,Envoy 将因为无法连接到 upstream 导致 503
    • 所以主容器也最好在 Terminating 至少 3s 后,才能关闭。
  • 如果主容器处理还未处理完遗留请求时,Envoy 或者主容器的其中一个停止了,会因为 tcp 连接直接断开连接导致 502
    • 因此 Envoy 必须在主容器处理完遗留请求后(即没有 tcp 连接时),才能关闭

所以总结下:Envoy 及主容器的 preStop 都至少得设成 3s,并且在「没有 tcp 连接」时,才能关闭,避免出现 502/503/504.

主容器的修改方法在前文中已经写过了,下面介绍下 Envoy 的修改方法。

和主容器一样,Envoy 也能直接加 preStop,修改 istio-sidecar-injector 这个 configmap,在 sidecar 里添加 preStop sleep 命令:

    containers:
    - name: istio-proxy
      # 添加下面这部分
      lifecycle:
      preStop:
          exec:
            command:
            - /bin/sh
            - -c
            - "while [ $(netstat -plunt | grep tcp | grep -v envoy | wc -l | xargs) -ne 0 ]; do sleep 1; done"

 

实践中java springcloud 微服务优雅停机设置

pod:   http:     port: 8080   probe:     enabled: true     probes:       livenessProbe:         tcpSocket:           port: http         initialDelaySeconds: 150 #启动容器后,启动活动或就绪探针之前的秒数。默认为0秒。最小值为0。         periodSeconds: 10 #执行探测的频率(以秒为单位)。默认为10秒。最小值为1。         timeoutSeconds: 1 #探测超时的秒数。默认为1秒。最小值为1。         successThreshold: 1 #探测失败后,连续最小成功探测为成功。默认值为1。为保持活力,必须为1。最小值为1。         failureThreshold: 3 #如果探测失败,Kubernetes将尝试尝试failureThreshold放弃。放弃活动探针意味着重新启动容器。如果准备就绪,则将Pod标记为“未就绪”。默认值为3。最小值为1。       readinessProbe:         tcpSocket:           port: http         initialDelaySeconds: 150 #启动容器后,启动活动或就绪探针之前的秒数。默认为0秒。最小值为0。         periodSeconds: 10 #执行探测的频率(以秒为单位)。默认为10秒。最小值为1。         timeoutSeconds: 1 #探测超时的秒数。默认为1秒。最小值为1。         successThreshold: 1 #探测失败后,连续最小成功探测为成功。默认值为1。为保持活力,必须为1。最小值为1。         failureThreshold: 3 #如果探测失败,Kubernetes将尝试尝试failureThreshold放弃。放弃活动探针意味着重新启动容器。如果准备就绪,则将Pod标记为“未就绪”。默认值为3。最小值为1。       lifecycle:  #优雅统计使用的k8s钩子函数         preStop:           exec:             command: #k8s 钩子函数需要执行的命令               - sh               - -c          #以下为服务线下流程,正常k8s 滚动更新是会发送SIGTERM信号给pod内PID为1的进程(正常状态下java进程pod内PID为1,故不用使用此钩子函数),特殊业务架构下,PID为1的进程可能不是java进程,此时必须使用此钩子函数才能优雅停机               - curl -X "POST" "http://localhost:9090/sys/actuator/service-registry?status=DOWN" -H "Content-Type:application/vnd.spring-boot.actuator.v2+json;charset=UTF-8" && sleep 35 && PID=`pidof java` && kill -SIGTERM $PID && while ps -p $PID > /dev/null; do sleep 1; done


 

 

今日语录:

  才华是刀刃,辛苦是刀石,很锋利的刀刃,若日久不用磨,也会生锈,成为废物

标签:容器,停机,Envoy,优雅,最小值,preStop,Pod,K8S
From: https://www.cnblogs.com/muyi-yang/p/17414254.html

相关文章

  • K8s 资源缩写
    点击查看代码[root@nginx~]#kubectlapi-resourcesNAMESHORTNAMESAPIVERSIONNAMESPACEDKINDbindingsv1trueBindingcompon......
  • k8s集群环境搭建
    注:如下文档不是一次性搭建成功的,可能有一些地方ip地址和实际成功的那次地址不一样。首先,我们准备3台虚拟机,配置都是是2核心2.2G内存192.168.3.121k8s-master192.168.3.133k8s-node1192.168.3.119k8s-node21修改主机名经过实践,发现,如果不修改主机名的话,安装可能......
  • Podman基本命令
      Podman是一个功能齐全的容器引擎,它是一个简单的无需守护的用来管理镜像、容器的工具。Podman提供了一个与DockerCLI兼容的操作方式,简单地说:aliasdocker=podman。大多数Podman命令都可以普通用户运行,而无需其他额外的权限。仓库=》镜像=》容器的关系:仓库:用来提供/存放镜像......
  • k8s删除命名空间namespace一直显示Terminating问题处理
    转载自:https://huaweicloud.csdn.net/638db195dacf622b8df8c5f7.html============= 1、问题现象假设我们的现在要删除的namespace是dev,执行如下命令进行删除:执行如下命令查看namespace删除情况:可以看到删除状态显示Terminating,而且会一直持续这个状态。并且用:kubectldeleten......
  • 使用k8s configmap保存nginx.conf配置文件
    创建一个包含Nginx配置的文件(例如nginx.conf)。创建一个ConfigMap对象,将Nginx配置文件添加为其中的数据。kubectlcreateconfigmapnginx-config--from-file=nginx.conf这将创建一个名为"nginx-config"的ConfigMap,并将nginx.conf文件的内容作为其中的数据存......
  • 一、搭建k8s集群前置准备工作
    1、VM虚拟机配置网卡2、准备机器操作系统IP角色CPU核心数运行内存HostnameCentOS7.6192.168.1.2Master122Gk8s-master1CentOS7.6192.168.1.3Master222Gk8s-master2CentOS7.6192.168.1.4Master322Gk8s-master3CentOS7.6192.168.1.......
  • k8s主备Master安装(Containerd)
    原创文档编写不易,未经许可请勿转载。文档中有疑问的可以邮件联系我。邮箱:[email protected]文章基于CentOS7.8系统使用Containerdr作为容器运行时通过kubeadm指导搭建k8s多master节点集群。必备条件:需要服务器可以联网。环境节点说明主机名IP地址操作系统作用ma......
  • k8s 之statefulset有状态应用
    StatefulSet由以下几个部分组成:1.HeadlessService:用来定义pod网路标识,生成可解析的DNS记录2.volumeClaimTemplates:存储卷申请模板,创建pvc,指定pvc名称大小,自动创建pvc,且pvc由存储类供应。3.StatefulSet:管理pod的Headlessservice不分配clusterIP,headlessse......
  • k8s 之storageclass存储类
    1.storageclass(存储类)概念storageclass是一个存储类,k8s集群管理员通过创建storageclass可以动态生成一个存储卷供k8s用户使用。2.storageclass资源定义每个StorageClass都包含字段provisioner,parameters和reclaimPolicy,当需要动态配置属于该类的PersistentVolume时使用这些字段......
  • K8S 常用资源 YAML 详解
    Pod资源对象yaml详解apiVersion:v1#必选,版本号,例如v1kind:Pod#必选,指定创建资源的角色/类型metadata:#必选,资源的元数据/属性name:string#必选,资源的名字,在同一个namespace中必须唯一namespace:string#必选,Pod所属的命名空间......