前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kubernetes调度器101

Kubernetes调度器101

作者头像
CNCF
发布2019-12-04 15:01:53
7730
发布2019-12-04 15:01:53
举报
文章被收录于专栏:CNCFCNCF

之前由Mohamed Ahmed在Magalix博客上发表

Kubernetes的调度器是什么?

如果你阅读过Kubernetes的任何文档、书籍或文章,那么毫无疑问,你会在“Pod被调度到下一个可用节点”之类的短语中看到调度“schedule”这个词。Kubernetes的调度不仅仅是在一个节点上放置一个pod。在本文中,我们将讨论Kubernetes在需要处理新pod时所遵循的不同机制,以及该过程中涉及的组件。

当你在Kubernetes集群上创建一个Pod时会发生什么?

在几秒钟内,Pod就会启动,并在一个集群节点上运行。然而,在这几秒钟里发生了很多事情。让我们来看看:

  1. Kubernetes调度器在扫描API服务器时(它一直在这样做),检测到有一个没有nodeName参数的新Pod。nodeName显示哪个节点应该拥有这个Pod。
  2. 调度器为这个Pod选择一个合适的节点,并用节点名(通过是nodeName参数)更新Pod定义。
  3. 所选节点上的kubelet被通知有一个pod正在等待执行。
  4. kubelet执行Pod,后者开始在节点上运行。

Kubernetes如何选择正确的节点?

以上步骤中最困难的部分可能是调度器决定应该选择哪个节点来运行pod。实际上,这一部分的工作量最大,因为调度器必须使用几种算法来进行决策。其中一些算法依赖于用户提供的选项,而Kubernetes自己计算其它的。可以将它们解释为调度器要求节点决定的一组问题。

你具备运行这个pod所需的条件吗(谓词)?

一个节点可能超载了许多繁忙的Pod,消耗了它的大部分CPU和内存。因此,当调度器需要部署Pod时,它将确定节点是否具有必要的资源。如果将Pod部署到没有足够内存(作为例子)供Pod请求的节点,承载的应用程序可能会出现意外甚至崩溃。

有时候,用户需要代表Kubernetes做出这个决定。假设你最近购买了几台配备了SSD磁盘的机器,并且希望显式地将它们用于应用程序的MongoDB部分。为此,你可以通过pod定义中的节点标签选择节点。当节点与提供的标签不匹配时,不选择它来部署Pod。

如上图所示,谓词决策解析为True(是的,在该节点上部署pod)或False(不,不要在该节点上部署pod)。

你是拥有这个pod的更好人选吗(优先级)?

除了正确/错误的决定,称为谓词,调度器执行一些计算(或函数)来确定哪个节点更适合承载有关的pod。

例如,已经存在pod镜像的节点(像在以前的部署中被拉取过一样)有更好的机会将pod调度到它,因为不会浪费时间重新下载镜像。

另一个例子是,调度器倾向于不包含相同服务的其它Pod的节点。该算法帮助尽可能多地将服务 Pod分散到多个节点上,这样一个节点故障就不会导致整个服务宕机。这种决策方法称为扩散函数。

将几个决策(如上面的示例)分组,并根据最终决策为每个节点计算权重。具有最高优先级的节点将获得pod部署。

最后的决定

你可能会问,如果Kubernetes调度器在选择部署pod的节点之前必须考虑许多因素,那么它如何才能选择正确的节点呢?

嗯,决策过程如下:

  1. 调度器确定它知道的所有节点的存在和状态。
  2. 调度器运行谓词测试来过滤不适合的节点。其余的节点组成一组可能用的节点。
  3. 调度器对可能用的节点运行优先级测试。按分数排序,分数最高的排在前面。此时,将选择得分最高的节点。但有时可能有多个节点具有相同的得分。
  4. 如果节点具有相同的得分,则将它们移动到最终列表。Kubernetes调度器以循环方式选择获胜节点,以确保它在机器之间平均分配负载。

如果那不是最好的决定呢?

在繁忙的Kubernetes集群中,调度器选择正确的节点与执行pod的节点上的kubelet之间的时间可能足以使节点上发生更改。即使时间不超过几毫秒,pod也可能在由于内存不足而被过滤掉的某个节点上终止。只有在当时没有超载的情况下,该节点才可能在优先级测试中获得更高的分数。但现在,可能是选择了一个不太合适的节点。

有些项目旨在解决这种情况,例如Kubernetes Descheduler项目。在这个应用程序中,如果另一个节点被证明是更好的点调度选择,那么pod将自动从节点中移除。pod返回到调度过程中,再次将其部署到正确的节点。

当相反的情况发生时,可能会出现更困难的情况。假设对一个节点进行了测试,看它是否能够提供2GB的内存。在调度器执行谓词检查时,节点确实有一些空闲RAM。然而,当kubelet对节点执行pod时,DaemonSet被部署到相同的节点。这个守护进程需要一些资源密集型的操作,需要消耗剩余的2GB。现在,当pod试图运行时,由于它缺少正确运行所需的内存,所以它失败了。如果这个pod仅使用一个pod定义进行部署,那么它所运行的应用程序将无法启动,Kubernetes对此无能为力。但是,如果这个pod是pod控制器,如Deployment或ReplicaSet,的一部分,那么一旦它失败,控制器将检测到比它应该处理的副本数量少。因此,控制器将请求安排另一个pod。调度器将再次运行所有检查并将pod调度到另一个节点。这就是为什么总是建议在创建pod时使用更高级别的对象(如Deployment)的原因之一。

用户定义的决策

在本文前面,我们提到用户可以使用pod定义或模板中的.spec.nodeSelector参数在特定节点上运行pod。节点选择器选择具有一个或多个特定标签的节点。然而,有时用户需求会变得更加复杂。例如,节点选择器选择在参数中定义了所有标签的节点。如果你想做出更灵活的选择呢?

节点关联(Node Affinity)

让我们考虑一下前面的示例,当时我们希望将pod安排在具有SSD磁盘的机器上运行。假设我们想让它们也使用八核主机。节点关联允许这样的灵活决策。以下pod模板选择标签为feature=ssd或feature=eight-cores的节点:

代码语言:javascript
复制
apiVersion: v1
kind: Pod
metadata:
 name: mongo
spec:
 affinity:
   nodeAffinity:
     requiredDuringSchedulingIgnoredDuringExecution:
       nodeSelectorTerms:
       - matchExpressions:
         - key: feature
           operator: In
           values:
           - ssd
           - eight-cores
 containers:
 - name: mongodb
   image: mogo

requiredDuringSchedulingIgnoredDuringExecution选项

这里有一个新选项:requiredDuringSchedulingIgnoredDuringExecution。这比看起来容易。这意味着我们只需要在标记为feature=ssd或feature=eight-cores的节点上运行这些pod。我们不希望调度器在这组节点之外做出决策。这与节点选择器的行为相同,但是语法更富表现力。

preferredDuringSchedulingIgnoredDuringExecution选项

假设我们希望在选定的节点上运行pod。但是,由于启动该pod是有绝对优先级的,所以我们需要运行它,即使所选的节点不可用。在这种情况下,我们可以使用preferredDuringSchedulingIgnoredDuringExecution选项。此选项将尝试在选择器指定的节点上运行pod。但是如果这些节点不可用(测试失败),调度器将尝试在次佳节点上运行pod。

节点反关联(Node Anti-Affinity)

有些场景要求不使用一个或多个节点,但特定的pod除外。可用考虑托管监视应用程序的节点为例子。由于其角色的性质,这些节点不应该有很多资源。因此,如果有监控应用程序之外的其它pod被调度到这些节点,它们会影响监控,也会降低它们所承载的应用程序的性能。在这种情况下,你需要使用节点反关联来避免pod与一组节点接触。以下是之前添加了反关联的pod定义:

代码语言:javascript
复制
apiVersion: v1
kind: Pod
metadata:
 name: mongo
spec:
 affinity:
   nodeAffinity:
     requiredDuringSchedulingIgnoredDuringExecution:
       nodeSelectorTerms:
       - matchExpressions:
         - key: feature
           operator: In
           values:
           - ssd
           - eight-cores
         - key: role
           operator: NotIn
           values:
           - monitoring


 containers:
 - name: mongodb
   image: mogo

使用操作符NotIn向matchexpression添加另一个键,将避免在任何标记为role=monitoring的节点上调度mongo pod。

学习如何持续优化K8s集群

节点污点(taint)和容忍(toleration)

虽然节点反关联模式允许你阻止pod在特定节点上运行,但是它们有一个缺点:pod定义必须明确声明不应该在这些节点上运行。那么,如果一个新成员加入了开发团队,为她的应用程序编写了一个Deployment,但是忘记将监视节点排除在目标节点之外,该怎么办?Kubernetes的管理员需要一种方法来击退节点上的pod,而不必修改每个pod的定义。这就是污点和容忍的作用。

当你点污一个节点时,它将自动从pod调度中排除。当调度在受污点的节点上运行谓词测试时,它们将失败,除非pod能够容忍该节点。例如,让我们点污监测节点mon01:

代码语言:javascript
复制
kubectl taint nodes mon01 role=monitoring:NoSchedule

现在,要是一个pod要在这个节点上运行,它必须有一定的容忍。例如,以下.spec.toleration:

代码语言:javascript
复制
tolerations:
- key: "role"
  operator: "Equal"
  value: "monitoring"
  effect: "NoSchedule"

匹配mon01上污点的键、值和效果(effect)。这意味着当调度器决定是否可以使用mon01来部署这个pod时,mon01将通过谓词测试。

需要注意的一件重要的事情是,容忍可使受点污的节点接受pod,但不能保证该pod在特定节点上运行。换句话说,受点污的节点mon01将被视为运行pod的候选节点之一。但是,如果另一个节点的优先级更高,则会选择它。对于这种情况,你需要将容忍与节点选择器或节点关联参数结合起来。

总结

  • Kubernetes调度器负责确定哪个节点最适合运行pods。
  • 它使用两个主要的决策过程:
    • 谓词:这是一组测试,每个测试都符合true或false。谓词失败的节点将被排除在流程之外。
    • 优先级:每个节点都要根据一些函数进行测试,这些函数会给它一个分数。选择得分最高的节点进行pod部署。
  • Kubernetes调度器还支持用户定义的影响其决策的因素:
    • 节点选择器:pod定义中的.spec.nodeSelector参数将节点选择范围缩小到那些在nodeSelector中定义了标签的节点。
    • 节点关联和反关联:它们用于在节点选择中提供更大的灵活性,因为它们允许更有表现力的选择标准。可以使用节点关联来确保只使用匹配的节点或只设置首选项。
  • 污点和容忍的工作方式与节点关联相同。但是,它们的默认操作是将pod从受点污的节点中排除,除非pod具有必要的容忍(键、值和效果)。容忍通常与节点关联或节点选择器参数相结合,以确保仅将匹配的节点用于pod调度。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CNCF 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档