• 【云原生】K8s pod优雅退出(postStart、terminationGracePeriodSeconds、preStop)


    一、概述

    • 更新部署服务时,旧的 Pod 会终止,新 Pod 上位。
    • 如果在这个部署过程中老 Pod 有一个很长的操作,我们想在这个操作成功完成后杀死这个 pod(优雅关闭),如果无法做到的话,被杀死的 pod 可能会丢失一定的流量,或者外界无法感知到该 Pod 被杀死。
    • 特别是,如果我们有一个接收大量流量的 API,错误率在部署过程中会显著增加。这时就必须要设置一些预处理操作让pod的优雅退出,先来了解一下pod的生命周期。

    二、Pod 的 生命周期

    在这里插入图片描述
    我们一般将pod对象从创建至终这段时间范围成为pod的生命周期,它主要包含以下的过程:

    • pod创建;
    • 运行初始化容器(init container);
    • 运行主容器(main container);
    • pod终止过程,pod完全退出之前执行pre stop操作,也就是收尾工作。

    三、pod的终止过程

    • 在 Pod 退出时,kubelet 删除容器之前会先执行 pod 的 preStop,允许 pod 在退出前执行一段脚本用以清除必要的资源等。
    • 然而 preStop 也有执行失败或者直接 hang 住的情况,这个时候 preStop 并不会阻止 pod 的退出,kubelet 也不会重复执行,而是会等一段时间,超过这个时间会直接删除容器,保证整个系统的稳定。

    终止过程主要分为如下几个步骤:

    • 【第一步】用户发出删除 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从而完成删除操作。

    四、钩子函数(postStart 和 preStop)

    钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码,k8s在主容器的启动之后停止之前提供了两个钩子函数。

    • post start:容器创建之后执行,如果失败了会重启容器。
    • pre stop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作。

    钩子处理器支持使用下面三种方式定义动作:

    • exec命令(自定义):在容器内执行一次命令
    ......
      lifecycle:
        postStart:
          exec:
            command:
            - cat
            - /tmp/healthy
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • tcpSocket:在当前容器尝试访问指定的socket,监听端口,属于四层。
    ......
      lifecycle:
        postStart:
          tcpSocket:
            port: 8080
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • httpGet:在当前容器中向某url发起http请求,监听接口,属于七层。
    ......
      lifecycle:
        postStart:
          httpGet:
            path: #uri地址
            port:
            host: 
            scheme: HTTP  #支持的协议,http或者https
    ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    五、示例演示

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: pod-hook-exec
    spec:
      replicas: 1
      selector:
        matchLabels:
         app: pod-hook-exec
      template:
        metadata:
          labels:
            app: pod-hook-exec
        spec:
          terminationGracePeriodSeconds: 5 # 设置5秒宽限时间,默认是30s
          nodeName: local-168-182-110 # 为了测试方便,指定调度机器
          initContainers:
          - name: init-containers
            image: busybox
            command: ["sh","-c","echo init-containers...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
            volumeMounts:
            - name: logs
              mountPath: /tmp/pod-hook-exec.log
              subPath: pod-hook-exec.log
          containers:
          - name: main-container
            image: busybox
            command: ["sh","-c","echo main-container...|tee -a /tmp/pod-hook-exec.log;sleep 3600s"] # 只有这个才会输出到屏幕,也就是通过logs只能查看主容器日志
            volumeMounts:
            - name: logs
              mountPath: /tmp/pod-hook-exec.log
              subPath: pod-hook-exec.log
            startupProbe:
              exec:
                command: ["sh","-c","echo startupProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
              timeoutSeconds: 10
            livenessProbe:
              exec:
                command: ["sh","-c","echo livenessProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
              timeoutSeconds: 10
            readinessProbe:
              exec:
                command: ["sh","-c","echo readinessProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
              timeoutSeconds: 10
            lifecycle:
              postStart:
                exec: #在容器启动的时候执行一个命令
                  command: ["sh","-c","echo postStart...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
              preStop: # 在pod停止之前执行
                exec:
                  command: ["sh","-c","echo preStop...|tee -a /tmp/pod-hook-exec.log"]
          volumes:
          - name: logs #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
            hostPath:
              path: /opt/k8s/test/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    kubectl apply -f test.yaml ;kubectl get pods -w |grep pod-hook-exec
    
    • 1

    在这里插入图片描述
    在这里插入图片描述

    从上图的日志就可看出,被分为6个执行阶段,执行的先后顺序:initContainers-》【main-container、postStart】-》startupProbe-》readinessProbe-》livenessProbe-》preStop

    【温馨提示】main-containerpostStart 是同时执行,虽然readinessProbelivenessProbe 也是同时执行,但是他们不是真正的并行执行,也有先后顺序的。

    关于K8s pod优雅退出优雅退出,执行先后顺序就先到这里了,有疑问的小伙伴欢迎给我留言哦,后续会持续分享关于【大数据与云原生】相关的文章,请小伙伴耐心等待哦~

  • 相关阅读:
    单片机论文参考:1、基于单片机的电子琴
    搜索增长超80%,小红书多元内容发力!这些行业迎好机遇…
    最短路(spfa)hdu 2544
    Leetcode2937. 使三个字符串相等
    前端性能精进之优化方法论(二)——分析
    【SpringBoot】一文了解SpringBoot热部署
    这些比较前沿的设计网站,你知道吗?
    UE4 动画资源曲线导出以及导入
    Python基础内容训练2(常用的数据类型-----字符串)
    STL(标准模板库)入门
  • 原文地址:https://blog.csdn.net/qq_35745940/article/details/126673660