Kubernetes 集群零停机更新-使用preStop优雅关停POD

标签: | 发表时间:2021-07-25 18:17 | 作者:
出处:https://mp.weixin.qq.com

原文标题:Gracefully Shutting Down Pods in a Kubernetes Cluster

发布时间:Jan 26, 2019

原文链接:https://blog.gruntwork.io/zero-downtime-server-updates-for-your-kubernetes-cluster-902009df5b33

文章作者:yorinasub17

这是我们实现 Kubernetes 集群零停机时间更新的第二部分。在本系列的 第一部分中,我们列举出了简单粗暴地使用 kubectl drain命令清除集群节点上的 Pod 的问题和挑战。在这篇文章中,我们将介绍解决这些问题和挑战的手段之一:优雅地关闭 Pod。

Pod驱逐的生命周期

默认情况下, kubectl drain命令驱逐节点上的 Pod 时会遵循 Pod 的生命周期,这意味着整个过程会遵守以下规则:

  • kubectl drain将向控制中心发出删除目标节点上的 Pod 的请求。随后,请求将通知目标节点上的 kubelet开始关闭 Pod。
  • 节点上的 kubelet将会调用 Pod 里的 preStop钩子。
  • preStop钩子执行完成后,节点上的 kubelet会向Pod容器中运行的程序发送 TERM信号 (SIGTERM)。
  • 节点上的 kubelet将最多等待指定的宽限期(在pod上指定,或从命令行传入;默认为30秒)然后关闭容器,然后强行终止进程(使用SIGKILL)。注意,这个宽限期包括执行 preStop钩子的时间。

译注:Kubelet 终止Pod前的等待宽限期有两种方式指定

  1. 在Pod定义里通过Pod模板的spec.terminationGracePeriodSeconds 设定
  2. kubectl delete pod {podName} --grace-period=60

基于此流程,我们可以利用应用程序 Pod 中的 preStop钩子和信号处理来正常关闭应用程序,以便在最终终止应用程序之前对其进行“清理”。例如,假如有一个工作进程从队列中读取信息然后处理任务,我们可以让应用程序捕获 TERM 系统信号,以指示该应用程序应停止接受新任务,并在所有当前任务完成后停止运行。或者,如果运行的应用程序无法修改以捕获 TERM 信号(例如第三方应用程序),则可以使用 preStop钩子来实现该服务提供的自定义API,来正常关闭应用。

在我们的示例中,Nginx 默认情况下不能处理 TERM 信号,因此,我们将改为依靠 Pod 的 preStop钩子实现正常停止Nginx。我们将修改资源定义,将生命周期钩子添加到容器的 spec定义中,如下所示:

    lifecycle:      
  preStop:
    exec:
      command: [
        # Gracefully shutdown nginx
        "/usr/sbin/nginx", "-s", "quit"
      ]

应用此配置后,在将 TERM 信号发送给容器中的Nginx进程之前, kebulet调用 Pod 的生命周期钩子发出命令 / usr / sbin / nginx -s quit。请注意,由于该命令将会正常停止 Nginx 进程和 Pod,因此 TERM 信号实际上在这个例子中是一个空操作。

在定义文件添加了生命周期钩子后,整个 Deployment 资源的定义变成了下面这样

    ---      
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.15
        ports:
        - containerPort: 80
        lifecycle:
          preStop:
            exec:
              command: [
                # Gracefully shutdown nginx
                "/usr/sbin/nginx", "-s", "quit"
              ]

停机后的后续流量

使用上面的 preStop钩子正常关闭 Pod 可以确保 Nginx 在处理完现存流量有才会停止。但是,你可能会发现,Nginx 容器在关闭后仍会继续接收到流量,从而导致服务出现停机时间。

为了了解造成这个问题的原因,让我们来看一个示例图。假定该节点已接收到来自客户端的流量。应用程序会产生一个工作线程来处理请求。我们用在 Nginx Pod 示例图内的圆圈表示该工作线程。

正在处理请求的Nginx

假设在工作线程处理请求的同时,集群的运维人员决定对 Node1进行维护。运维运行了 kubectl drain node-1后,节点上的 kubelet会执行 Pod 设置的 preStop钩子,开始进入Nginx进程正常关闭的流程。

对节点进行维护,清出节点上的Pod时会先执行preStop钩子

由于 Nginx 仍要处理已存流量的请求,所以进入正常关闭流程后 Nginx 不会马上终止进程,但是会拒绝处理后续到达的流量,向新请求返回错误。

在这个时间点,假设一个新的服务请求到达了 Pod 上层的 Service,因为此时 Pod 仍然是上层 Service 的Endpoint,所以这个即将关闭的 Pod 仍然可能会接收到 Service 分发过来的请求。如果 Pod 真的接收到了分发过来的新请求 Nginx 就会拒绝处理并返回错误。

译注:推荐阅读 学练结合快速掌握K8s Service控制器

Nginx处于关闭流程时会拒绝新来的请求

最终 Nginx 将完成对原始已存请求的处理,随后 kubelet会删除 Pod,节点完成排空。

Nginx 处理完已存请求后终止进程 Pod停止运行,kubelet删除Pod

为什么会这样呢?如何避免在Pod执行关闭期间接受到来自客户端的请求呢?在本系列的下一部分中,我们会更详细地介绍 Pod 的生命周期,并给出如何在 preStop钩子中引入延迟为 Pod 进行摘流,以减轻来自 Service 的后续流量的影响。

相关 [kubernetes 集群 更新] 推荐:

Kubernetes 集群零停机更新-使用preStop优雅关停POD

- -
原文标题:Gracefully Shutting Down Pods in a Kubernetes Cluster. 发布时间:Jan 26, 2019. 原文链接:https://blog.gruntwork.io/zero-downtime-server-updates-for-your-kubernetes-cluster-902009df5b33.

Kubernetes 集群零停机更新-使用PDB保证最少可用POD数

- -
原文标题:Avoiding Outages in your Kubernetes Cluster using PodDisruptionBudgets. 发布时间:Jan 26, 2019. 原文链接:https://blog.gruntwork.io/avoiding-outages-in-your-kubernetes-cluster-using-poddisruptionbudgets-ef6a4baa5085.

Kubernetes 集群零停机更新-增加preStop延迟实现POD摘流

- -
原文标题:Gracefully Shutting Down Pods in a Kubernetes Cluster. 发布时间:Jan 26, 2019. 原文链接:https://blog.gruntwork.io/delaying-shutdown-to-wait-for-pod-deletion-propagation-445f779a8304.

Kubernetes 集群日志基础

- - Linux 中国◆开源社区
探索 Kubernetes 中不同容器日志记录模式的工作原理. 服务器和应用程序日志记录是开发人员、运维人员和安全团队了解应用程序在其生产环境中运行状态的重要工具. 日志记录使运维人员能够确定应用程序和所需组件是否运行平稳,并检测是否发生了异常情况,以便他们能够对这种情况做出反应. 对于开发人员,日志记录提供了在开发期间和之后对代码进行故障排除的可见性.

使用Prometheus、Thanos监控Kubernetes集群

- - DockOne.io
当你阅读这篇文章的时候,我相信你一定已经说服了你的经理,或者是公司CTO,选择容器和Kubernetes作为微服务治理平台,去转型升级你们公司的软件产品. 你非常非常的happy,一切都貌似按照计划进行,你创建了你的第一个Kubernetes集群(三大主流云服务提供商,微软云Azure,亚马逊云AWS和谷歌云GCP都提供了非常方便的方式部署Kubernetes平台),你开发了你的第一个容器化应用,然后把它部署到了你的Kubernetes集群上.

Ubuntu 20.04 部署kubernetes 1.22 集群

- - 鹿先森
由于众所周知的原因(F**K 红X),CentOS8的生命周期就快结束了,系统要转入Ubuntu的怀抱了,不过还好所有的应用都扔到kubernetes上了,迁移的难度大大降低. 近期在做Ubuntu的测试,正好把在ubuntu 20.04 LTS 上部署最新的kubernetes记录下来. 系统:ubuntu 20.04 LTS.

Kubernetes - 集群内容器访问集群外服务

- - 掘金后端
GitHub地址: github.com/QingyaFan/c…. 企业内部一般存在很多的微服务,在逐步容器化的过程中,会有部分服务在集群外部,未完成容器化,比如数据库,而部分已经完成容器化的依赖于这些服务的服务,过渡过程中,需要集群内部的容器访问集群外部的服务. 为了在容器化过程中,让服务不中断,就需要让Kubernetes集群内部的容器能访问集群外部的服务,怎么做到呢,在每个应用的配置文件中使用外部IP或者外部rds名字吗.

Kubernetes:玩转 Pod 滚动更新

- - IT瘾-dev
今天推荐一篇关于Kubernetes上服务滚动更新相关的配置选项的文章,文章列出了最常用的几个配置项,解释了他们是怎么影响调度器对服务进行滚动更新的,同时还带出了 Kubernetes项目中 Pod这个逻辑单元的 Ready状态是怎么确定的,并不是容器运行起来后 Pod就进入 Ready状态的.

构建生产就绪的Kubernetes集群的16点清单

- - DockOne.io
Kubernetes是用于构建高度可扩展系统的强大工具. 结果,许多公司已经开始或正在计划使用它来协调生产服务. 不幸的是,像大多数强大的技术一样,Kubernetes也很复杂. 我们整理了以下清单,以帮助你生产环境最佳实践Kubernetes. Kubernetes提供了一种编排容器化服务的方法,因此,如果您没有按顺序实践你的容器,那么集群一开始就不会处于良好状态.

利用Kubeadm部署 Kubernetes 1.13.1集群实践录 | CodeSheep · 程序羊

- -
Kubernetes集群的搭建方法其实有多种,比如我在之前的文章 《利用K8S技术栈打造个人私有云(连载之:K8S集群搭建)》中使用的就是二进制的安装方法. 虽然这种方法有利于我们理解 k8s集群,但却过于繁琐. 而 kubeadm是 Kubernetes官方提供的用于快速部署Kubernetes集群的工具,其历经发展如今已经比较成熟了,利用其来部署 Kubernetes集群可以说是非常好上手,操作起来也简便了许多,因此本文详细叙述之.