图解kubernetes调度器抢占流程与算法设计

本文深入探讨Kubernetes的抢占调度器设计,包括抢占原理、中断预算、优先级反转以及五种抢占选择算法。通过源码分析,揭示抢占核心流程、条件检测、节点筛选等关键步骤,帮助理解分布式调度中的资源优化策略。
摘要由CSDN通过智能技术生成

抢占调度是分布式调度中一种常见的设计,其核心目标是当不能为高优先级的任务分配资源的时候,会通过抢占低优先级的任务来进行高优先级的调度,本文主要学习k8s的抢占调度以及里面的一些有趣的算法

1. 抢占调度设计

1.1 抢占原理

抢占调度原理其实很简单就是通过高优先级的pod抢占低优先级的pod资源,从而满足高优先pod的调度

1.2 中断预算

在kubernetes中为了保证服务尽可能的高可用,设计PDB(PodDisruptionBudget)其核心目标就是在保证对应pod在指定的数量,主要是为了保证服务的可用性,在进行抢占的过程中,应尽可能遵守该设计,尽量不去抢占有PDB的资源,避免因为抢占导致服务的不可用

1.3 优先级反转

优先级反转是信号量里面的一种机制即因为低优先级任务的运行阻塞高优先级的任务运行

在k8s中抢占调度是通过高优先级抢占低优先级pod,如果高优先级pod依赖低优先级pod, 则会因为依赖问题,导致优先级失效,所以应该尽可能减少高优先级pod对低优先级的pod的依赖, 后面进行筛选源码分析时可以看到

1.4 抢占选择算法

抢占选择算法是指的通过抢占部分节点后,如何从被抢占的node数组中筛选出一个node节点,目前k8s中主要实现了5个算法

1.4.1 最少违反PDB

即最少违反PDB规则

1.4.2 最高优先级最小优先

比较所有node的最高优先级的pod,找到优先级最低的node

1.4.3 优先级总和最低优先

计算每个node上面的被抢占的pod优先级之和,选择优先级和最低的节点

1.4.4 最少抢占数量优先

计算需要抢占的节点数量最少的节点优先

1.4.5 最近更新节点优先

比较每个node中被驱逐的pod中最早启动的pod的启动时间,最近启动的pod的节点,会被选择

2. 源码设计

2.1 抢占核心流程

image.png抢占的流程主要是通过Preempt来实现,其针对预选失败的节点来进行驱逐某些低优先级的pod来满足高优先级pod

func (g *genericScheduler) Preempt(pluginContext *framework.PluginContext, pod *v1.Pod, scheduleErr error) (*v1.Node, []*v1.Pod, []*v1.Pod, error) {
    // 只允许预选失败的pod进行重试
    fitError, ok := scheduleErr.(*FitError)
    if !ok || fitError == nil {
        return nil, nil, nil, nil
    }
    // 是否允许抢占其他提议的pod
    if !podEligibleToPreemptOthers(pod, g.nodeInfoSnapshot.NodeInfoMap, g.enableNonPreempting) {
        klog.V(5).Infof("Pod %v/%v is not eligible for more preemption.", pod.Namespace, pod.Name)
        return nil, nil, nil, nil
    }
    // 获取当前集群中的所有node
    allNodes := g.cache.ListNodes()
    if len(allNodes) == 0 {
        return nil, nil, nil, ErrNoNodesAvailable
    }
    // 初步筛选潜在的可以进行抢占操作的node
    potentialNodes := nodesWherePreemptionMightHelp(allNodes, fitError)
    if len(potentialNodes) == 0 {
        klog.V(3).Infof("Preemption will not help schedule pod %v/%v on any node.", pod.Namespace, pod.Name)
        // In this case, we should clean-up any existing nominated node name of the pod.
        return nil, nil, []*v1.Pod{pod}, nil
    }
    // 获取所有pdb
    pdbs, err := g.pdbLister.List(labels.Everything())
    if err != nil {
        return nil, nil, nil, err
    }
    // 针对之前初步筛选的node尝试进行抢占和预选操作,返回结果中包含所有可以通过抢占低优先级pod完成pod调度的node节点与抢占的pod
    nodeToVictims, err := g.selectNodesForPreemption(pluginContext, pod, g.nodeInfoSnapshot.NodeInfoMap, potentialNodes, g.predicates,
        g.predicateMetaProducer, g.schedulingQueue, pdbs)
    if err != nil {
        return nil, nil, nil, err
    }

    // 调用extenders进行再一轮的筛选
    nodeToVictims, err = g.processPreemptionWithExtenders(pod, nodeToVictims)
    if err != nil {
        return nil, nil, nil, err
    }

    // 从筛选结果中选择最适合抢占的node
    candidateNode := pickOneNodeForPreemption(nodeToVictims)
    if candidateNode == nil {
        return nil, nil, nil, nil
    }

    // 如果candidateNode不为nil,则找到一个最优的执行抢占操作的node, 返回低优先的提议的pod
    // 还有抢占的pod和当前节点
    nominatedPods := g.getLowerPriorityNominatedPods(pod, candidateNode.Name)
    if nodeInfo, ok := g.nodeInfoSnapshot.NodeInfoMap[candidateNode.Name]; ok {
        return nodeInfo.Node(), nodeToVictims[candidateNode].Pods, nominatedPods, nil
    }

    return nil, nil, nil, fmt.Errorf(
        "preemption failed: the target node %s
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值