程序锅

  • 首页
  • 分类
  • 标签
  • 归档
  • 关于

  • 搜索
基础知识 Etcd LeetCode 计算机体系结构 Kubernetes Containerd Docker 容器 云原生 Serverless 项目开发维护 ELF 深入理解程序 Tmux Vim Linux Kernel Linux numpy matplotlib 机器学习 MQTT 网络基础 Thrift RPC OS 操作系统 Clang 研途 数据结构和算法 Java 编程语言 Golang Python 个人网站搭建 Nginx 计算机通用技术 Git

Kubernetes 核心流程-Pod 驱逐过程

发表于 2023-04-15 | 分类于 Kubernetes | 0 | 阅读次数 3432

基础

驱逐的方式

驱逐 Pod 的方式,主要有 2 大类:

  • 基于驱逐 API 主动发起的驱逐行为。

  • kubelet 基于节点压力(kubelet 会不断检查节点资源,比如内存、CPU、磁盘),主动发起的驱逐行为,又分为两种

    • 硬驱逐。

      硬驱逐策略定义了节点上资源消耗的绝对限制。一旦实际使用量超过了这些限制,Kubelet 会立即采取行动终止 Pod,以恢复资源的利用率到可接受的水平。例如,您可以对内存使用量设置硬限制,一旦超出,立即触发 Pod 驱逐。

    • 软驱逐。

      软驱逐策略提供了一种更灵活的驱逐机制,允许配置阈值和一个宽限期。实际资源使用量超过软阈值时,Kubelet 将等待定义的宽限期,如果在宽限期结束时资源使用量仍然超标,则开始终止 Pod。如果期间该资源又恢复到低于阈值时,则不进行 Pod 驱逐。

驱逐的顺序

Kubelet 在决定驱逐哪些 Pod 时,会遵循一定的顺序或者策略,比如:

  • 驱逐匿名 Pod(不属于任何 ReplicaSet、Deployment 或 Job 的 Pod)。
  • 根据 Pod 的 Quality of Service(QoS)类别进行驱逐,顺序通常是 BestEffort、Burstable 到 Guaranteed
    • Best-Effort(尽最大努力):Pod 中所有容器均未设置 requests 和 limits。
    • Burstable(不稳定的):Pod 中只要有一个容器的 requests 和 limits 的设置不相同(其实简单来说就是除 guaranteed 和 best-effort 之外)
    • Guaranteed(有保证的)
      • Pod 中的所有容器都且仅设置了 CPU 和 Mem 的 limits。
      • Pod 中的所有容器都设置了 CPU 和 Mem 的 requests 和 limits,且容器内的 requests==limits。
  • 考虑 Pod 的优先级。

驱逐流程

  1. Kubelet 启动 Eviction Manager 来管理 Pod 的驱逐。

  2. Eviction Manager 会定时检查 Node 资源消耗情况,如内存、磁盘空间和 CPU。

  3. 如果 Node 资源消耗超过 Kubelet 配置的驱逐阈值,则会根据配置的策略和 Pod 的优先级、QoS 等来对要被驱逐的 Pod 进行排序。

  4. 对排序的 Pod 进行遍历,并尝试进行驱逐。如果一个 Pod 被驱逐成功,则此次驱逐流程结束。驱逐流程如下,

    • 停止 Pod 中所有业务容器以及 Sandbox 容器。

    • 将 pod.status.phase 值更新为 Failed,并附上驱逐 reason。

    • 对于 Pod 由控制器控制的情况,如 ReplicationController、ReplicaSet、DaemonSet、StatefulSet 和 Job,它们将会作出响应,比如重新创建一个新的 Pod 来替代被驱逐的 Pod。

      而对于不由控制器控制的 Pod 的话,则将直接调用 API Server,执行 DELETE 请求。

以内存为例,简单介绍一下 Pod 排序的过程:

OOM_ADJ 参数设置的越大,计算出来的 OOM 分数越高,表明该 Pod 的优先级就越低。当出现资源竞争时会越早被kill 掉,对于 OOM_ADJ 参数是 -999 则表示永远不会因为 OOM 被 kill 掉。

对于 Best-Effort 级别的 Pod,OOM_ADJ 参数设置为 1000;对于 Burstable 级别的 Pod,OOM_ADJ 参数取值从2到999;对于 Guaranteed 级别的 Pod,OOM_ADJ参数设置成了 -998;对于 kubelet、docker/containerd,OOM_ADJ 参数设置为 -999,表示不会被 OOM kill 掉。

源码解析

Kubelet 中负责压力驱逐的是 evictionManager,evictionManager 的启动代码位于如下位置,

Kubelet.Run()->Kubelet.updateRuntimeUp()->Kubelet.initializeRuntimeDependentModules()->Kubelet.evictionManager.Start(),也就是 managerImpl.Start()

Start() 中的代码如下所示,

  • 拉起一个 goroutine,循环调用 m.synchronize() 方法执行驱逐逻辑。驱逐逻辑为:根据 kubelet 配置的驱逐策略,计算并判断是否符合驱逐条件,符合则根据一定的优先级来驱逐 Pod,然后返回被驱逐的pod。需要注意:每次调用 m.synchronize 方法最多只会驱逐一个pod。
  • 如果被驱逐的 Pod 不为空,则调用 m.waitForPodsCleanup() 方法等待被驱逐的 Pod 删除成功。
  • 如果没有 Pod 被驱逐,则 sleep monitoringInterval 之后再循环一次。monitoringInterval 默认为 10s。
// Start starts the control loop to observe and response to low compute resources.
func (m *managerImpl) Start(diskInfoProvider DiskInfoProvider, podFunc ActivePodsFunc, podCleanedUpFunc PodCleanedUpFunc, monitoringInterval time.Duration) {
	...
	// start the eviction manager monitoring
	go func() {
		for {
			if evictedPods := m.synchronize(diskInfoProvider, podFunc); evictedPods != nil {
				m.waitForPodsCleanup(podCleanedUpFunc, evictedPods)
			} else {
				time.Sleep(monitoringInterval)
			}
		}
	}()
}

其中,主要的是 synchronize() 方法,大概流程如下,

  1. 构建 Pod 的排序函数,返回软驱逐、硬驱逐中各个驱逐信号所对应的排序函数,排序函数用于后续计算被驱逐 Pod 的顺序。

    同时构建节点资源回收函数,在后续驱逐 Pod 之前,先调用节点资源回收函数来回收资源,如果回收的资源足够,则不用进行驱逐。

  2. 获取会被驱逐的 Pod 列表---activePods。

  3. 获取节点上各个资源的总量以及使用情况、容器的资源声明及使用情况。

  4. 判断是否有达到阈值的资源情况。如果没有的话,则返回;如果有的话,则进行排序,并使用相应的资源回收函数回收资源。

  5. 如果回收资源之后,资源充足则返回;否则使用上述 Pod 的排序函数对 Pod 进行排序,再次得到 activePods。

  6. 之后遍历 activePods,调用 m.evictPod 方法进行驱逐。需要注意的是:最多只会驱逐一个 Pod,驱逐成功一个 Pod 则直接返回。

func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc ActivePodsFunc) []*v1.Pod {
	...
	if m.dedicatedImageFs == nil {
		...
		m.signalToRankFunc = buildSignalToRankFunc(hasImageFs)
		m.signalToNodeReclaimFuncs = buildSignalToNodeReclaimFuncs(m.imageGC, m.containerGC, hasImageFs)
	}

	activePods := podFunc()
	updateStats := true
	summary, err := m.summaryProvider.Get(ctx, updateStats)

	// determine the set of thresholds met independent of grace period
	thresholds = thresholdsMet(thresholds, observations, false)
	sort.Sort(byEvictionPriority(thresholds))
	thresholdToReclaim, resourceToReclaim, foundAny := getReclaimableThreshold(thresholds)

	// rank the running pods for eviction for the specified resource
	rank(activePods, statsFunc)

	// we kill at most a single pod during each eviction interval
	for i := range activePods {
		...
		if m.evictPod(pod, gracePeriodOverride, message, annotations, condition) {
			return []*v1.Pod{pod}
		}
	}
  
	return nil
}

m.evictPod 会调用 killPodFunc() 函数,最终调用的是 killPodNow() 中的函数,

  • 这个函数是调用 podWorkers.UpdatePod() 方法来停止 Pod 中的所有业务容器以及 sandbox 容器。
  • 将 Pod.Status.Phase 设置为 Failed,添加驱逐的原因。
func (m *managerImpl) evictPod(pod *v1.Pod, gracePeriodOverride int64, evictMsg string, annotations map[string]string, condition *v1.PodCondition) bool {
	...
	err := m.killPodFunc(pod, true, &gracePeriodOverride, func(status *v1.PodStatus) {
		status.Phase = v1.PodFailed
		status.Reason = Reason
		status.Message = evictMsg
		if condition != nil {
			podutil.UpdatePodCondition(status, condition)
		}
	})
	...
	return true
}

func killPodNow(podWorkers PodWorkers, recorder record.EventRecorder) eviction.KillPodFunc {
	return func(pod *v1.Pod, isEvicted bool, gracePeriodOverride *int64, statusFn func(*v1.PodStatus)) error {
		...
		ch := make(chan struct{}, 1)
		podWorkers.UpdatePod(UpdatePodOptions{
			Pod:        pod,
			UpdateType: kubetypes.SyncPodKill,
			KillPodOptions: &KillPodOptions{
				CompletedCh:                              ch,
				Evict:                                    isEvicted,
				PodStatusFunc:                            statusFn,
				PodTerminationGracePeriodSecondsOverride: gracePeriodOverride,
			},
		})

		select {
		case <-ch:
			return nil
		case <-time.After(timeoutDuration):
			return fmt.Errorf("timeout waiting to kill pod")
		}
	}
}

更加详细的流程可参考源码,源码分析可参考:k8s 驱逐篇(3)-kubelet 节点压力驱逐-源码分析篇。

相关链接

k8s 驱逐篇(3)-kubelet 节点压力驱逐-源码分析篇:https://www.cnblogs.com/lianngkyle/p/16652129.html

卷死我
dawnguo 微信支付

微信支付

dawnguo 支付宝

支付宝

  • 本文作者: dawnguo
  • 本文链接: /archives/230
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# Kubernetes
Kubernetes 核心流程-Pod 创建过程
Kubernetes 核心流程- Pod 删除过程
  • 文章目录
  • 站点概览
dawnguo

dawnguo

215 日志
24 分类
37 标签
RSS
Creative Commons
© 2018 — 2025 程序锅
0%