Kubernetes 调度器如何工作

了解 Kubernetes 调度器如何发现新的 Pod 并将它们分配给节点。
52 位读者喜欢这篇文章。
Parts, modules, containers for software

Opensource.com

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

对于那些已经熟悉 Kubernetes 及其组件的人来说,讨论通常围绕如何最大化 Kubernetes 的能力。但是,当您刚开始学习 Kubernetes 时,最好先了解一些关于 Kubernetes 及其组件(包括 Kubernetes 调度器)的通用知识,如这个高层次视图所示,然后再尝试在生产环境中使用它。

Kubernetes 也使用控制平面和节点。

  1. 控制平面: 也称为 master,这些节点负责对集群做出全局决策,以及检测或响应集群事件。控制平面组件包括:
    • etcd
    • kube-apiserver 
    • kube-controller-manager
    • 调度器
  2. 节点: 也称为工作节点,这些节点集是工作负载驻留的地方。它们应始终与控制平面通信,以获取工作负载运行所需的信息,以及在集群外部进行通信和连接。工作节点组件包括:
    • kubelet
    • kube-proxy
    • 容器运行时接口。

我希望这个背景知识能帮助您理解 Kubernetes 组件是如何堆叠在一起的。

Kubernetes 调度器如何工作

Kubernetes Pod 由一个或多个容器组成,这些容器共享存储和网络资源。Kubernetes 调度器的任务是确保每个 Pod 都被分配到一个节点上运行。

从高层次来看,以下是 Kubernetes 调度器的工作方式

  1. 每个需要调度的 Pod 都会被添加到队列中
  2. 当创建新的 Pod 时,它们也会被添加到队列中
  3. 调度器不断地从队列中取出 Pod 并进行调度

调度器的代码 (scheduler.go) 很大,大约 9,000 行,而且相当复杂,但需要处理的重要部分是

  1. 等待/监视 Pod 创建的代码

    监视 Pod 创建的代码从 scheduler.go 的第 8970 行开始;它无限期地等待新的 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)
  2. 负责将 Pod 排队的代码

    负责 Pod 排队的函数是

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

    负责将 Pod 排队的代码从 scheduler.go 的第 7360 行开始。当事件处理程序被触发以指示有新的 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
    		}
    	}
    }
  3. 处理错误的代码

    在 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 被创建在具有足够可用资源的节点上。然后,调度器继续永远运行,监视是否有需要调度的 Pod。

了解更多

要拥有一个正常工作的 Kubernetes 集群,您需要使上述所有组件协同工作。调度器是一段复杂的代码,但 Kubernetes 是一个很棒的软件,目前,它是采用云原生应用程序时的默认选择。

学习 Kubernetes 需要时间和精力,但将其作为您的技能之一将为您带来优势,这应该在您的职业生涯中带来回报。有很多好的学习资源可用,并且文档也很好。如果您有兴趣了解更多信息,我建议从以下内容开始

您最喜欢的学习 Kubernetes 的方式是什么?请在评论中分享。

接下来阅读
标签
ID
Mike Calizo 是 Elastic.co 的首席客户成功经理,专注于政府客户,常驻澳大利亚堪培拉。Mike 认为“数据就是力量”,利用这种力量可以改进组织,使其能够利用自身的洞察力通过创新实现差异化,并通过成本优化策略提高效率。

评论已关闭。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.