Kubernetes调度器是如何工作的?

Linux系统
333
0
0
2023-04-28
标签   Kubernetes

本文主要内容是了解Kubernetes调度程序如何发现新Pod并将其分配给节点。

img

Kubernetes已经成为容器和容器化工作负载的标准编排引擎。它提供了跨越公共和私有云环境的通用平台,开放源代码抽象层。

对于那些已经熟悉Kubernetes及其组件的人,讨论通常围绕最大化Kubernetes的功能。但是,当您只是学习Kubernetes时,明智的做法是先从一些有关Kubernetes及其组件(包括Kubernetes调度代码)的常识开始,如高级视图所示,然后再尝试在生产中使用它。

img

Kubernetes控制平面和节点

控制平面

也称为主控节点,这些节点负责制定有关群集的全局决策,并检测或响应群集事件。控制平面组件为:

  • kube-apiserver
  • kube-controller-manager
  • 调度器

节点

也称为工作程序节点,这些节点集是工作负载所在的位置。他们应该始终与控制平面对话,以获取工作负载运行以及在集群外部进行通信和连接所需的信息。工作节点的组件是:

  • kubelet
  • kube-proxy
  • 容器运行时接口。

希望通过这种背景可以帮助您了解Kubernetes组件是如何协作的。

Kubernetes Scheduler设计结构

Kubernetes调度器与其他主组件(例如APIServer)一起作为进程运行。它与APIServer的接口是监视没有被调度的Pod,对于每个Pod,它都会发布一个绑定,指示应该在何时调度Pod

代码结构

我们将调度程序从高层分为三层:

  • cmd/kube-scheduler/scheduler.go:这是main()函数,它在调用调度程序框架之前进行初始化。
  • pkg/scheduler/scheduler.go:这是调度程序框架,用于处理调度算法以外的内容(例如绑定)。
  • pkg/scheduler/core/generic_scheduler.go:为Pod分配节点的调度算法。

调度算法

img

Pod调度过程

调度器尝试为每个Pod查找一个节点。

首先,它应用一组谓词来过滤掉不适当的节点。例如,如果PodSpec指定了资源请求,则调度程序将滤除那些没有至少可用资源的节点(计算为节点的容量减去已经运行的容器的资源请求的总和)在节点上。

其次,它应用了一组优先级函数,这些函数对未被谓词检查滤除的节点进行排名。例如,它尝试将Pod分布在节点和区域上,同时偏向于(理论上)负载最少的节点(理论上,负载是作为在节点上运行的容器的资源请求的总和来衡量的)除以节点的容量。

最后,选择优先级最高的节点(或者,如果有多个这样的节点,则随机选择其中一个)。此主调度循环的代码Schedule()pkg/scheduler/core /generic_scheduler.go中的函数中。

谓词和优先级策略

谓词是一组策略,一个一个地应用以筛选出不适当的节点。优先级是一组逐个应用以对节点进行排名的策略(通过谓词过滤器对其进行排序)。默认情况下,Kubernetes提供内置的谓词和优先级策略,该策略记录在scheduler_algorithm.md中。谓词和优先级代码分别在pkg/scheduler/algorithm/predicates/predicates.gopkg/scheduler/algorithm/priorities中定义。

调度器扩展性

调度程序是可扩展的:集群管理员可以选择应用哪些预定义的调度策略,也可以自己添加新的。

修改方式

可以通过以下两种方式之一来选择在调度时所应用的策略。

使用的默认策略由功能选择,defaultPredicates()defaultPriorities()并在 pkg/scheduler/algorithmprovider/defaults/defaults.go中进行选择。但是,可以通过将命令行标志--policy-config-file传递给调度程序(在JSON文件指定要使用哪些调度策略)来覆盖策略的选择。

有关示例配置文件(https://github.com/kubernetes/examples/blob/master/staging/scheduler-policy/scheduler-policy-config.json),请参见examples/cheduler-policy-config.json。(请注意,配置文件格式已版本化;APIpkg/scheduler/ api中定义)。

因此,如果要添加新的调度策略,您应该修改pkg/scheduler/algorithm/predicates/predicates.go或将其添加到目录中pkg/scheduler/algorithm/priorities,然后在defaultPredicates()或中注册策略defaultPriorities(),或使用策略配置文件。

Kubernetes调度器如何工作

Kubernetes容器是由一个或多个具有共享存储和网络资源的容器组成。Kubernetes调度程序的任务是确保将每个Pod分配到一个并且在其上运行的节点。

如下所示正是Kubernetes调度程序的工作方式:

1、需要调度的每个Pod都添加到队列中 2、创建新Pod后,它们也会添加到队列中 3、调度器连续将Pod从该队列中移出并调度它们 该调度程序的代码(scheduler.go)是大约9,000行,且相当复杂,但需要解决重要问题有三个,下面会从代码层面说明。

等待/监视pod创建

监视pod创建的代码是从第8970行开始(scheduler.go)。它无限期地等待新Pod创建:

// Run begins watching and scheduling. It waits for cache to be synced, then starts a goroutine and returns immediately.

func (sched *Scheduler) Run() {
        if !sched.config.WaitForCacheSync() {
                return
        }

        go wait.Until(sched.scheduleOne, 0, sched.config.StopEverything)

负责对Pod队列存储

负责对Pod进行队列存储的代码从的第7360行开始(scheduler.go)。

// queue for pods that need scheduling
        podQueue *cache.FIFO

触发事件处理程序用于指示有一个新Pod可用时,此代码段自动将新Pod放入队列:

func (f *ConfigFactory) getNextPod() *v1.Pod {
        for {
                pod := cache.Pop(f.podQueue).(*v1.Pod)
                if f.ResponsibleForPod(pod) {
                        glog.V(4).Infof("About to try and schedule pod %v", pod.Name)
                        return pod
                }
        }
}

处理错误的代码

pod调度中,不可避免地会遇到调度错误。以下代码是调度程序处理错误的方式。它侦听podInformer然后抛出一个错误,提示该Pod尚未调度并终止:

// scheduled pod cache
        podInformer.Informer().AddEventHandler(
                cache.FilteringResourceEventHandler{
                        FilterFunc: func(obj interface{}) bool {
                                switch t := obj.(type) {
                                case *v1.Pod:
                                        return assignedNonTerminatedPod(t)
                                default:
                                        runtime.HandleError(fmt.Errorf("unable to handle object in %T: %T", c, obj))
                                        return false
                                }
                        },

调度器主要职责

换句话说,Kubernetes调度器主要负责:

  • 将新创建的Pod安排在具有足够空间的节点上,以满足Pod的资源需求
  • 监听kube-apiserver和控制器是否存在新创建的Pod,然后将它们调度到集群上的可用节点
  • 监视未调度的pod,并使用/binding pod子资源API将其绑定到节点。

例如,假设正在部署一个需要1GB内存和两个CPU内核的应用程序。因此,在具有足够可用资源的节点上创建该应用程序的容器。然后,调度器将继续永远运行,然后观察是否有需要调度的Pod

更多

要使Kubernetes集群正常工作,您需要使以上所有组件同步工作。调度器是非常复杂的模块,但是Kubernetes是很重要的基础设施,目前,它是采用云原生部署应用程序时的默认选择。

学习Kubernetes需要时间和精力,但是将其作为您的一项技能将为您带来应为您的职业带来回报的优势。有很多好的学习资源可供使用,而且文档也不错。如果您有兴趣了解更多信息,建议从以下内容开始:

  • https://github.com/kelseyhightower/kubernetes-the-hard-way
  • https://github.com/Praqma/LearnKubernetes/blob/master/kamran/Kubernetes-The-Hard-Way-on-BareMetal.md
  • https://github.com/Praqma/LearnKubernetes/blob/master/kamran/Kubernetes-The-Hard-Way-on-AWS.md

您最喜欢学习Kubernetes的哪些方式?请在评论区给出分享。