Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Kubernetes 中 Pod 的优雅退出机制

Kubernetes 中 Pod 的优雅退出机制

作者头像
CS实验室
发布于 2022-06-14 06:19:17
发布于 2022-06-14 06:19:17
3.3K00
代码可运行
举报
文章被收录于专栏:CS实验室CS实验室
运行总次数:0
代码可运行

本文基于对 Kubernetes v1.23.1 的源码阅读

Kubernetes 提供了一种 Pod 优雅退出机制,使 Pod 在退出前可以完成一些清理工作。但若执行清理工作时出错了,Pod 能正常退出吗?多久能退出?退出时间可以指定吗?系统有默认参数吗?这其中有若干细节值得我们去注意,本文就从这些细节出发,梳理清楚每种情况下 Kubernetes 的组件的各项行为及其参数设定。

Pod 正常退出

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

整个过程在函数 killContainer 中,我们在 pod 优雅退出时,需要明确的是,kubelet 的等待时间由那几个因素决定,用户可以设置的字段和系统组件的参数是如何共同作用的。

gracePeriod

kubelet 计算 gracePeriod 的过程为:

  1. 如果 pod 的 DeletionGracePeriodSeconds 不为 nil,表示是 ApiServer 删除的,gracePeriod 直接取值;
  2. 如果 pod 的 Spec.TerminationGracePeriodSeconds 不为 nil,再看 pod 删除的原因是什么;
    1. 若删除原因为执行 startupProbe 失败,gracePeriod 取值为 startupProbe 中设置的 TerminationGracePeriodSeconds
    2. 若删除原因为执行 livenessProbe 失败,gracePeriod 取值为 livenessProbe 中设置的 TerminationGracePeriodSeconds

获得到 gracePeriod 之后,kubelet 执行 pod 的 preStop,函数 executePreStopHook 中会起一个 goroutine ,并计算其执行的时间,gracePeriod 再减去该时间,就是最终传给 runtime 的删除容器的 timeout 时间。所以,若我们设置了 pod preStop,需要同时考虑到 preStop 的执行时间以及容器退出的时间,可以给 TerminationGracePeriodSeconds 设置一个大于 preStop + 容器退出的时间。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (m *kubeGenericRuntimeManager) killContainer(pod *v1.Pod, containerID kubecontainer.ContainerID, containerName string, message string, reason containerKillReason, gracePeriodOverride *int64) error {
   ...
 // From this point, pod and container must be non-nil.
 gracePeriod := int64(minimumGracePeriodInSeconds)
 switch {
 case pod.DeletionGracePeriodSeconds != nil:
  gracePeriod = *pod.DeletionGracePeriodSeconds
 case pod.Spec.TerminationGracePeriodSeconds != nil:
  gracePeriod = *pod.Spec.TerminationGracePeriodSeconds

  switch reason {
  case reasonStartupProbe:
   if containerSpec.StartupProbe != nil && containerSpec.StartupProbe.TerminationGracePeriodSeconds != nil {
    gracePeriod = *containerSpec.StartupProbe.TerminationGracePeriodSeconds
   }
  case reasonLivenessProbe:
   if containerSpec.LivenessProbe != nil && containerSpec.LivenessProbe.TerminationGracePeriodSeconds != nil {
    gracePeriod = *containerSpec.LivenessProbe.TerminationGracePeriodSeconds
   }
  }
 }

 // Run internal pre-stop lifecycle hook
 if err := m.internalLifecycle.PreStopContainer(containerID.ID); err != nil {
  return err
 }

 // Run the pre-stop lifecycle hooks if applicable and if there is enough time to run it
 if containerSpec.Lifecycle != nil && containerSpec.Lifecycle.PreStop != nil && gracePeriod > 0 {
  gracePeriod = gracePeriod - m.executePreStopHook(pod, containerID, containerSpec, gracePeriod)
 }
 // always give containers a minimal shutdown window to avoid unnecessary SIGKILLs
 if gracePeriod < minimumGracePeriodInSeconds {
  gracePeriod = minimumGracePeriodInSeconds
 }
 if gracePeriodOverride != nil {
  gracePeriod = *gracePeriodOverride
 }

 err := m.runtimeService.StopContainer(containerID.ID, gracePeriod)
   ...
 return nil
}

gracePeriodOverride

在上面分析的过程中,kubelet 调用 runtime 接口之前,会再判断一步 gracePeriodOverride,若传进来的值不为空,直接用该值覆盖前面的 gracePeriod

kubelet 计算 gracePeriodOverride 的主要过程如下:

  1. 取值 pod 的 DeletionGracePeriodSeconds
  2. 若 kubelet 是在驱逐 pod,则用驱逐的设置 pod 退出时间覆盖;
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func calculateEffectiveGracePeriod(status *podSyncStatus, pod *v1.Pod, options *KillPodOptions) (int64, bool) {
 gracePeriod := status.gracePeriod
 // this value is bedrock truth - the apiserver owns telling us this value calculated by apiserver
 if override := pod.DeletionGracePeriodSeconds; override != nil {
  if gracePeriod == 0 || *override < gracePeriod {
   gracePeriod = *override
  }
 }
 // we allow other parts of the kubelet (namely eviction) to request this pod be terminated faster
 if options != nil {
  if override := options.PodTerminationGracePeriodSecondsOverride; override != nil {
   if gracePeriod == 0 || *override < gracePeriod {
    gracePeriod = *override
   }
  }
 }
 // make a best effort to default this value to the pod's desired intent, in the event
 // the kubelet provided no requested value (graceful termination?)
 if gracePeriod == 0 && pod.Spec.TerminationGracePeriodSeconds != nil {
  gracePeriod = *pod.Spec.TerminationGracePeriodSeconds
 }
 // no matter what, we always supply a grace period of 1
 if gracePeriod < 1 {
  gracePeriod = 1
 }
 return gracePeriod, status.gracePeriod != 0 && status.gracePeriod != gracePeriod
}

ApiServer 的行为

在上面分析 kubelet 处理 pod 的退出时间时,我们会发现 kubelet 会首先用 pod 的 DeletionGracePeriodSeconds,而该值正是 ApiServer 删除 pod 时写入的。本节我们来分析 ApiServer 删除 pod 时的行为。

ApiServer 中计算 pod 的 GracePeriodSeconds 过程为:

  1. options.GracePeriodSeconds 不为空,则设置为该值;否则设置为 spec 中用户指定的 Spec.TerminationGracePeriodSeconds(默认为 30s);
  2. 若 pod 未被调度或已经退出,则设置为 0,即立即删除;

其中,options.GracePeriodSeconds 为 kubectl 删除 pod 时,可以指定的参数 --grace-period;或者程序里调用 ApiServer 接口时指定的参数,如 client-go 中的 DeleteOptions.GracePeriodSeconds

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (podStrategy) CheckGracefulDelete(ctx context.Context, obj runtime.Object, options *metav1.DeleteOptions) bool {
 if options == nil {
  return false
 }
 pod := obj.(*api.Pod)
 period := int64(0)
 // user has specified a value
 if options.GracePeriodSeconds != nil {
  period = *options.GracePeriodSeconds
 } else {
  // use the default value if set, or deletes the pod immediately (0)
  if pod.Spec.TerminationGracePeriodSeconds != nil {
   period = *pod.Spec.TerminationGracePeriodSeconds
  }
 }
 // if the pod is not scheduled, delete immediately
 if len(pod.Spec.NodeName) == 0 {
  period = 0
 }
 // if the pod is already terminated, delete immediately
 if pod.Status.Phase == api.PodFailed || pod.Status.Phase == api.PodSucceeded {
  period = 0
 }

 if period < 0 {
  period = 1
 }

 // ensure the options and the pod are in sync
 options.GracePeriodSeconds = &period
 return true
}

kubelet 驱逐 pod

另外,在 kubelet 驱逐 pod 时,pod 的优雅退出时间是被覆盖的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc ActivePodsFunc) []*v1.Pod {
   ...
 // we kill at most a single pod during each eviction interval
 for i := range activePods {
  pod := activePods[i]
  gracePeriodOverride := int64(0)
  if !isHardEvictionThreshold(thresholdToReclaim) {
   gracePeriodOverride = m.config.MaxPodGracePeriodSeconds
  }
  message, annotations := evictionMessage(resourceToReclaim, pod, statsFunc)
  if m.evictPod(pod, gracePeriodOverride, message, annotations) {
   metrics.Evictions.WithLabelValues(string(thresholdToReclaim.Signal)).Inc()
   return []*v1.Pod{pod}
  }
 }
 ...
}

其 override 值为 EvictionMaxPodGracePeriod,且只有软驱逐时有效,该值为 kubelet 的驱逐相关的配置参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 // Map of signal names to quantities that defines hard eviction thresholds. For example: {"memory.available": "300Mi"}.
 EvictionHard map[string]string
 // Map of signal names to quantities that defines soft eviction thresholds.  For example: {"memory.available": "300Mi"}.
 EvictionSoft map[string]string
 // Map of signal names to quantities that defines grace periods for each soft eviction signal. For example: {"memory.available": "30s"}.
 EvictionSoftGracePeriod map[string]string
 // Duration for which the kubelet has to wait before transitioning out of an eviction pressure condition.
 EvictionPressureTransitionPeriod metav1.Duration
 // Maximum allowed grace period (in seconds) to use when terminating pods in response to a soft eviction threshold being met.
 EvictionMaxPodGracePeriod int32

kubelet 驱逐 pod 的函数是启动时注入的,函数如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func killPodNow(podWorkers PodWorkers, recorder record.EventRecorder) eviction.KillPodFunc {
 return func(pod *v1.Pod, isEvicted bool, gracePeriodOverride *int64, statusFn func(*v1.PodStatus)) error {
  // determine the grace period to use when killing the pod
  gracePeriod := int64(0)
  if gracePeriodOverride != nil {
   gracePeriod = *gracePeriodOverride
  } else if pod.Spec.TerminationGracePeriodSeconds != nil {
   gracePeriod = *pod.Spec.TerminationGracePeriodSeconds
  }

  // we timeout and return an error if we don't get a callback within a reasonable time.
  // the default timeout is relative to the grace period (we settle on 10s to wait for kubelet->runtime traffic to complete in sigkill)
  timeout := int64(gracePeriod + (gracePeriod / 2))
  minTimeout := int64(10)
  if timeout < minTimeout {
   timeout = minTimeout
  }
  timeoutDuration := time.Duration(timeout) * time.Second

  // open a channel we block against until we get a result
  ch := make(chan struct{}, 1)
  podWorkers.UpdatePod(UpdatePodOptions{
   Pod:        pod,
   UpdateType: kubetypes.SyncPodKill,
   KillPodOptions: &KillPodOptions{
    CompletedCh:                              ch,
    Evict:                                    isEvicted,
    PodStatusFunc:                            statusFn,
    PodTerminationGracePeriodSecondsOverride: gracePeriodOverride,
   },
  })

  // wait for either a response, or a timeout
  select {
  case <-ch:
   return nil
  case <-time.After(timeoutDuration):
   recorder.Eventf(pod, v1.EventTypeWarning, events.ExceededGracePeriod, "Container runtime did not kill the pod within specified grace period.")
   return fmt.Errorf("timeout waiting to kill pod")
  }
 }
}

killPodNow 函数是 kubelet 在驱逐 pod 时所调用的函数,gracePeriodOverride 为软驱逐时设置的参数,当其没有设置时,gracePeriod 依然取值 pod.Spec.TerminationGracePeriodSeconds。然后该函数会调用 podWorkers.UpdatePod,传入相应参数,并且设置一个跟 gracePeriod 相关的超时时间,等待其返回。

总结

Pod 的优雅退出是由 preStop 实现的,本文就 Pod 正常退出和被驱逐时,Pod 的退出时间受哪些因素影响,各参数之间是如何相互作用的做了简要的分析。了解了这些细节后,我们对 Pod 的退出流程就有了一个更加全面的认知。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CS实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
云原生|k8s pod 的删除过程以及信号处理
m.killContainer --> m.internalLifecycle.PreStopContainer-->m.runtimeService.StopContainer
heidsoft
2022/06/09
9860
云原生|k8s pod 的删除过程以及信号处理
K8S Pod解析
上篇文章我们在解析K8S Container时,提到Pod的整个生命周期都是围绕“容器”这个核心进行运转,毕竟,Pod 是 Kubernetes 集群中能够被创建和管理的最小部署单元,只有弄清楚其底层原理以及实现细节,我们才能够对Kubernetes生态体系有所了解。
Luga Lee
2021/12/09
5910
K8S Pod解析
Kubernetes 如何优雅的重启Pod
在应用程序的整个生命周期中,正在运行的 pod 会由于多种原因而终止。在某些情况下,Kubernetes 会因用户输入(例如更新或删除 Deployment 时)而终止 pod。在其他情况下,Kubernetes 需要释放给定节点上的资源时会终止 pod。无论哪种情况,Kubernetes 都允许在 pod 中运行的容器在可配置的时间内正常关闭。
kubernetes中文社区
2022/10/27
4.5K0
Kubernetes Eviction Manager源码分析
Kubernetes Eviction Manager介绍及工作原理 这部分内容,请看我的前一篇博文:Kubernetes Eviction Manager工作机制分析 Kubernetes Eviction Manager源码分析 Kubernetes Eviction Manager在何处启动 Kubelet在实例化一个kubelet对象的时候,调用eviction.NewManager新建了一个evictionManager对象。 pkg/kubelet/kubelet.go:273 func New
Walton
2018/04/16
2.6K1
《一起读 kubernetes 源码》probe 监控 pod 状态
当我们知道了 pod 的生命周期,那么 k8s 如何知道一个 pod 的健康状态呢?就是通过今天要说的 Probe 也就是探针来检查 pod 的状态。一方面可以监控 pod 的健康状态,重启不健康的 pod;另一方面还可以监控 pod 的服务状态,当 pod 能提供服务时才会将流量打进来。
LinkinStar
2023/12/30
4640
Sidecar优雅退出
codis集群在接入弹性云测试时发现容器漂移失败,通过集群日志看,提示 调度超时,去界面查看,已经调度成功了(调度成功的标志就是已经有宿主机IP了),状态显示的pending并不一定就是调度失败。但这反应不出来问题出在哪里,接下来就需要到master机器上执行命名,查看日志来分析问题出在哪里
李鹤
2023/03/06
5390
《一起读 kubernetes 源码》pod 的资源限制和驱逐
资源在 k8s 中是一个非常重要的关键因素,一些运维事故往往也就是因为一些资源限制设置的不合理而导致的。而合理的设置资源也是一门学问和经验,最近不停地被提及的 “降本增效” 通常也伴随着资源设置的优化。对于一个应用应该设置多少内存和 CPU,我觉得这不是我们在这里应该学习的(这都是实战经验积累的)。而我们需要知道的是,这些限制条件何时会被检查,会被谁检查,超过限制条件会引发什么问题。 这对于我们来说很重要,一方面实际出现问题,我们可以迅速知道原因;另一方面,这些限制条件还会和之后的调度、自动扩容/缩容有关系。所以本章节我们来看看它。
LinkinStar
2023/12/30
4740
Node工作负载异常,一部分pod状态为Terminating
在节点处于“NotReady”状态时,deployment控制器会迁移节点上的容器实例,并将节点上运行的pod置为“Terminating”状态。待节点恢复后,处于“Terminating”状态的pod会自动删除。偶现部分pod(实例)一直处于“Terminating ”状态,发现这部分的pod没有得到重新调度,不能提供服务。
没有故事的陈师傅
2021/10/19
1.9K0
Node工作负载异常,一部分pod状态为Terminating
Kubernetes Eviction Manager工作机制分析
研究过Kubernetes Resource QoS的同学,肯定会有一个疑问:QoS中会通过Pod QoS和OOM Killer进行资源的回收,当发生资源紧缺的时候。那为什么Kubernetes会再搞一个Kubelet Eviction机制,来做几乎同样的事呢? 首先,我们来谈一下kubelet通过OOM Killer来回收资源的缺点: System OOM events本来就是对资源敏感的,它会stall这个Node直到完成了OOM Killing Process。 当OOM Killer干掉某些cont
Walton
2018/04/16
2K0
Kubernetes Eviction Manager工作机制分析
如何为你的Kubernetes保驾护航?
随着Kubernetes的不断发展,技术不断成熟,越来越多的公司选择把自家的应用部署到Kubernetes中。但是把应用部署到Kubernetes中就完事了吗?显然不是,应用容器化只是万里长征的第一步,如何让应用安心、稳定的运行才是后续的所有工作。
极客运维圈
2021/06/30
3100
Kubernetes Pod优雅停机分析
业务容器化上云之后,时常会有版本的动态变更,如何无损升级越发重要。结合底层技术原理,本文将对Pod优雅停机展开分析,供业务团队参考。
白鹏飞
2023/06/14
9260
kubelet 创建 pod 的流程
上篇文章介绍了 kubelet 的启动流程,本篇文章主要介绍 kubelet 创建 pod 的流程。
田飞雨
2019/12/15
1.2K0
kubernetes 组件之 kubelet
每个Node节点上都运行一个 Kubelet 服务进程,默认监听 10250 端口,接收并执行 Master 发来的指令,管理 Pod 及 Pod 中的容器。每个 Kubelet 进程会在 API Server 上注册所在Node节点的信息,定期向 Master 节点汇报该节点的资源使用情况,并通过 cAdvisor 监控节点和容器的资源。
看、未来
2022/06/05
7960
kubernetes 组件之 kubelet
如何优雅地关闭Kubernetes集群中的Pod
这是我们实现 Kubernetes 集群零停机时间更新的第二部分。在本系列的第一部分中,我们列举出了简单粗暴地使用kubectl drain 命令清除集群节点上的 Pod 的问题和挑战。在这篇文章中,我们将介绍解决这些问题和挑战的手段之一:优雅地关闭 Pod。
KevinYan
2021/03/16
3.2K0
浅析 Kubelet 驱逐机制
Kubelet 出于对节点的保护,允许在节点资源不足的情况下,开启对节点上 Pod 进行驱逐的功能。最近对 Kubelet 的驱逐机制有所研究,发现其中有很多值得学习的地方,总结下来和大家分享。
CS实验室
2021/09/24
1.6K0
Kubernetes之Kubelet组件解析
Kubelet组件运行在Node节点上,维持运行中的Pods以及提供kuberntes运行时环境,主要完成以下使命: 1.监视分配给该Node节点的pods 2.挂载pod所需要的volumes 3.下载pod的secret 4.通过docker/rkt来运行pod中的容器 5.周期的执行pod中为容器定义的liveness探针 6.上报pod的状态给系统的其他组件 7.上报Node的状态
菲宇
2019/06/12
2K0
Kubernetes之Kubelet组件解析
Kubernetes集群中,Node异常时Pod状态分析
一个节点上运行着pod前提下,这个时候把kubelet进程停掉。里面的pod会被干掉吗?会在其他节点recreate吗?
Walton
2019/02/28
5.7K0
8.深入k8s:资源控制Qos和eviction及其源码分析
又是一个周末,可以愉快的坐下来静静的品味一段源码,这一篇涉及到资源的回收,工作量是很大的,篇幅会比较长,我们可以看到k8s在资源不够时会怎么做的,k8s在回收资源的时候有哪些考虑,我们的pod为什么会无端端的被干掉等等。
luozhiyun
2020/09/01
1.2K0
8.深入k8s:资源控制Qos和eviction及其源码分析
Kubernetes Pod详解
Pod中通过共享Network Namespace的方式进行网络的共享,但是如果是以下方式进行Network Namespace共享会有问题:
shysh95
2022/05/24
8250
Kubernetes Pod详解
Kubernetes 中如何保证优雅地停止 Pod
一直以来我对优雅地停止 Pod 这件事理解得很单纯:不就利用是 PreStop hook 做优雅退出吗?但最近发现很多场景下 PreStop Hook 并不能很好地完成需求,这篇文章就简单分析一下“优雅地停止 Pod”这回事儿。
PingCAP
2019/04/02
8.8K0
相关推荐
云原生|k8s pod 的删除过程以及信号处理
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验