Kubernetes 简介及其调度原理
Kubernetes(k8s)是一个全新的基于容器技术的分布式架构领先方案。在Docker技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理的便捷性。
前言
Kubernetes 是一个可移植、可扩展的开源平台,用于管理容器化工作负载和服务,有助于声明式配置和自动化。它拥有庞大且快速发展的生态系统。Kubernetes 服务、支持和工具广泛可用。
Scheduler 作为kubernetes 的核心调度组件,扮演管家的角色遵从一套机制——为Pod提供调度服务,例如基于资源的公平调度、调度Pod到指定节点、或者通信频繁的Pod调度到同一节点等。本篇文章着重剖析 kube-scheduler组件的工作流程及设计模式。
一、k8s是什么
首先,它是一个全新的基于容器技术的分布式架构领先方案。Kubernetes(k8s)是Google开源的容器集群管理系统(谷歌内部:Borg)。在Docker技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等一系列完整功能,提高了大规模容器集群管理的便捷性。
Kubernetes是一个完备的分布式系统支撑平台,具有完备的集群管理能力,多扩多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和发现机制、內建智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制以及多粒度的资源配额管理能力。同时Kubernetes提供了完善的管理工具,涵盖了包括开发、部署测试、运维监控在内的各个环节。
二、k8s 组件 scheduler 简介
kube-scheduler 是 kubernetes 的核心组件之一,主要负责整个集群资源的调度功能,根据特定的调度算法和策略,将 Pod 调度到最优的工作节点上面去,从而更加合理、更加充分地利用集群的资源。
kube-scheduler的主要作用就是根据特定的调度算法和调度策略将 Pod 调度到合适的 Node 节点上去,是一个独立的二进制程序,启动之后会一直监听 API Server,获取到 PodSpec.NodeName 为空的 Pod,对每个 Pod 都会创建一个 binding。
三、生产环境的挑战
-
如何保证全部的节点调度的公平性?并不是所有节点资源配置一定都是一样的
-
如何保证每个节点都能被分配资源?
-
集群资源如何能够被高效利用?
-
集群资源如何才能被最大化使用?
-
如何保证 Pod 调度的性能和效率?
-
用户是否可以根据自己的实际需求定制自己的调度策略?
四、Scheduler 解析
调度主要分为以下几个部分:
-
首先是预选过程,过滤掉不满足条件的节点,这个过程称为`Predicates`(过滤);
-
然后是优选过程,对通过的节点按照优先级排序,称之为`Priorities`(打分);
-
最后从中选择优先级最高的节点,如果中间任何一个步骤有错误,就直接返回错误。
`Predicates` 阶段首先遍历全部节点(高版本可配置),过滤掉不满足条件的节点,属于`强制性`规则,这一阶段输出的所有满足要求的节点将被记录并作为第二阶段的输入,如果所有的节点都不满足条件,那么 Pod 将会一直处于 Pending 状态,直到有节点满足条件,在这期间调度器会不断的重试。
所以我们在部署应用的时候,如果发现有 Pod 一直处于 Pending 状态,那么就是没有满足调度条件的节点,这个时候可以去检查下节点资源是否可用。
`Priorities` 阶段即再次对节点进行筛选,如果有多个节点都满足条件的话,那么系统会按照节点的优先级(`priorites`)大小对节点进行排序,最后选择优先级最高的节点来部署 Pod 应用。
五、优选预选策略详解
首先,客户端通过 API Server 的 REST API 或者 kubectl 工具创建 Pod 资源。API Server 收到用户请求后,存储相关数据到 etcd 数据库中,调度器监听 API Server 查看还未被调度(bind)的 Pod 列表,循环遍历地为每个 Pod 尝试分配节点,这个分配过程就是我们上面提到的两个阶段:
-
预选阶段(Predicates),过滤节点,调度器用一组规则过滤掉不符合要求的 Node 节点,比如 Pod 设置了资源的 request,那么可用资源比 Pod 需要的资源少的主机显然就会被过滤掉。
-
优选阶段(Priorities),为节点的优先级打分,将上一阶段过滤出来的 Node 列表进行打分,调度器会考虑一些整体的优化策略,比如把 Deployment 控制的多个 Pod 副本尽量分布到不同的主机上,使用最低负载的主机等等策略。
经过上面的阶段过滤后选择打分最高的 Node 节点和 Pod 进行 `binding` 操作,然后将结果存储到 etcd 中 最后被选择出来的 Node 节点对应的 kubelet 去执行创建 Pod 的相关操作(当然也是 watch APIServer 发现的)。
六、scheduler 调度框架
1、Sort 扩展用于对 Pod 的待调度队列进行排序,以决定先调度哪个 Pod,Sort 扩展本质上只需要实现一个方法 Less(Pod1, Pod2) 用于比较两个 Pod 谁更优先获得调度即可。
2、Pre-filter 扩展用于对 Pod 的信息进行预处理,或者检查一些集群或 Pod 必须满足的前提条件,然后将其存入缓存中待 Filter 扩展点执行的时候使用,如果 pre-filter 返回了 error,则调度过程终止。
3、Filter 扩展用于排除那些不能运行该 Pod 的节点,对于每一个节点,调度器将按顺序执行 filter 扩展;如果任何一个 filter 将节点标记为不可选,则余下的 filter 扩展将不会被执行,调度器可以同时对多个节点执行 filter 扩展。
4、 Post-filter 是在 Filter 扩展点的全部节点都被过滤掉了,在没有合适的节点进行调度的情况下,才会执行 PostFilter 扩展点,如果启用了 Pod 抢占特性,那么会在这个扩展点进行抢占操作,可以用于 logs/metircs。
5、 PreScore 扩展会对 Score 扩展点的数据做一些预处理操作,然后将其存入缓存中待 Score 扩展点执行的时候使用。
6、 Score扩展用于为所有可选节点进行打分,调度器将针对每一个节点调用每个Score 扩展,评分结果是一个范围内的整数,代表最小和最大分数。在 normalize scoring 阶段,调度器将会把每个 score 扩展对具体某个节点的评分结果和该扩展的权重合并起来,作为最终评分结果。
7、Normalize score扩展在调度器对节点进行最终排序之前修改每个节点的评分结果,注册到该扩展点的扩展在被调用时,将获得同一个插件中的 score 扩展的评分结果作为参数,调度框架每执行一次调度,都将调用所有插件中的一个 normalize score 扩展一次。
8、 Reserve是一个通知性质的扩展点,有状态的插件可以使用该扩展点来获得节点上为 Pod 预留的资源,该事件发生在调度器将 Pod 绑定到节点之前,目的是避免调度器在等待 Pod 与节点绑定的过程中调度新的 Pod 到节点上时,发生实际使用资源超出可用资源的情况(因为绑定 Pod 到节点上是异步发生的)。这是调度过程的最后一个步骤,Pod 进入 reserved 状态以后,要么在绑定失败时触发 Unreserve 扩展,要么在绑定成功时,由 Post-bind 扩展结束绑定过程。
9、 `Permit` 扩展在每个 Pod 调度周期的最后调用,用于阻止或者延迟 Pod 与节点的绑定。Permit 扩展可以做下面三件事中的一项:
-
approve(批准):当所有的 permit 扩展都 approve 了 Pod 与节点的绑定,调度器将继续执行绑定过程;
-
deny(拒绝):如果任何一个 permit 扩展 deny 了 Pod 与节点的绑定,Pod 将被放回到待调度队列,此时将触发 `Unreserve` 扩展;
-
wait(等待):如果一个 permit 扩展返回了 wait,则 Pod 将保持在 permit 阶段,直到被其他扩展 approve,如果超时事件发生,wait 状态变成 deny,Pod 将被放回到待调度队列,此时将触发 Unreserve 扩展。
10、`WaitOnPermit`扩展与 Permit 扩展点配合使用实现延时调度功能(内部默认实现的)。
11、 `Pre-bind`扩展用于在 Pod 绑定之前执行某些逻辑。例如,pre-bind 扩展可以将一个基于网络的数据卷挂载到节点上,以便 Pod 可以使用。如果任何一个 `pre-bind` 扩展返回错误,Pod 将被放回到待调度队列,此时将触发 Unreserve 扩展。
12、 `Bind`扩展用于将 Pod 绑定到节点上:
-
只有所有的 pre-bind 扩展都成功执行了,bind 扩展才会执行;
-
调度框架按照 bind 扩展注册的顺序逐个调用 bind 扩展;
-
具体某个 bind 扩展可以选择处理或者不处理该 Pod;
-
如果某个 bind 扩展处理了该 Pod 与节点的绑定,余下的 bind 扩展将被忽略。
13、 `Post-bind`是一个通知性质的扩展:
-
Post-bind 扩展在 Pod 成功绑定到节点上之后被动调用;
-
Post-bind 扩展是绑定过程的最后一个步骤,可以用来执行资源清理的动作。
14、 `Unreserve`是一个通知性质的扩展,如果为 Pod 预留了资源,Pod 又在被绑定过程中被拒绝绑定,则 unreserve 扩展将被调用。Unreserve 扩展应该释放已经为 Pod 预留的节点上的计算资源。在一个插件中,reserve 扩展和 unreserve 扩展应该成对出现。
以上就是本文的全部内容,如果喜欢就点个关注吧,我们会持续更新更多精彩内容!
更多推荐
所有评论(0)