首先来看第一部分 - Kubernetes 的调度过程。以下图所示,画了一个很简单的 Kubernetes 集群架构,它包括了一个 kube-ApiServer,一组 webhooks 的 Controller,以及一个默认的调度器 kube-Scheduler,还有两台物理机节点 Node1 和 Node2,分别在上面部署了两个 kubelet。node
咱们来看一下,假如要向这个 Kubernetes 集群提交一个 pod,它的调度过程是什么样的一个流程?web
假设咱们已经写好了一个 yaml 文件,就是下图中的橙色圆圈 pod1,而后咱们往 kube-ApiServer 里面提交这个 yaml 文件。算法
此时 ApiServer 会先把这个待建立的请求路由给咱们的 webhooks 的 Controlles 进行校验。markdown
在经过校验以后,ApiServer 会在集群里面生成一个 pod,但此时生成的 pod,它的 nodeName 是空的,而且它的 phase 是 Pending 状态。在生成了这个 pod 以后,kube-Scheduler 以及 kubelet 都能 watch 到这个 pod 的生成事件,kube-Scheduler 发现这个 pod 的 nodeName 是空的以后,会认为这个 pod 是处于未调度状态架构
接下来,它会把这个 pod 拿到本身里面进行调度,经过一系列的调度算法,包括一系列的过滤和打分的算法后,Schedule 会选出一台最合适的节点,而且把这一台节点的名称绑定在这个 pod 的 spec 上,完成一次调度的过程。ide
此时咱们发现,pod 的 spec 上,nodeName 已经更新成了 Node1 这个 node,更新完 nodeName 以后,在 Node1 上的这台 kubelet 会 watch 到这个 pod 是属于本身节点上的一个 pod。性能
而后它会把这个 pod 拿到节点上进行操做,包括建立一些容器 storage 以及 network,最后等全部的资源都准备完成,kubelet 会把状态更新为 Running,这样一个完整的调度过程就结束了。spa
经过刚刚一个调度过程的演示,咱们用一句话来归纳一下调度过程:它其实就是在作一件事情,就是把 pod 放到合适的 node 上。设计
这里有个关键字“合适”,什么是合适呢?这里给出了几点合适定义的特色:3d
一、首先要知足 pod 的资源要求;
二、其次要知足 pod 的一些特殊关系的要求;
三、再次要知足 node 的一些限制条件的要求;
四、最后还要作到整个集群资源的合理利用。
下面为你们介绍一下 Kubernetes 的基础调度能力,Kubernetes 的基础调度能力会用两部分来展开介绍:
第一部分是资源调度——介绍一下 Kubernetes 基本的一些 Resources 的配置方式,还有 Qos 的概念,以及 Resource Quota 的概念和使用方式;
第二部分是关系调度——在关系调度上,介绍两种关系场景:
pod 的资源配置方法
上图是 pod spec 的一个 demo,咱们的资源实际上是填在 pod spec 里面,Container 里面有一个 resources 里面的 key 里面。
resources 其实包含两个部分:第一部分是 request;第二部分是 limits。
这两部分里面的内容是如出一辙的,可是它表明的含义有所不一样:request 表明的是对这个 pod 基本保底的一些资源要求;limit 表明的是对这个 pod 可用能力上限的一种限制。具体的 request、limit 的理念,其实都是一个 resources 的一个 map 结构,它里面能够填不一样的资源的 key。
咱们能够大概分红四大类的基础资源:
在扩展资源上,Kubernetes 有一个要求,即扩展资源必须是整数的,因此咱们无法申请到 0.5 的 GPU 这样的资源,只能申请 1 个 GPU 或者 2 个 GPU,这里为你们介绍一下基础资源的申请方式。
接下来,我会详细的给你们介绍一下 request 和 limit 到底有什么区别,以及如何经过 request/limit 来引出 Qos 的概念。
K8s 在 pod resources 里面提供了两种填写方式:第一种是 request,第二种是 limit。它实际上是为用户提供了对 Pod 一种弹性能力的定义。好比说咱们能够对 request 填 2 个 CPU,对 limit 填 4 个 CPU,这样其实表明了我但愿是有 2 个 CPU 的保底能力,但实际上是在闲置的时候,可使用 4 个 GPU。
说到这个弹性能力,咱们不得不提到一个概念:Qos 的概念。什么是 Qos呢?Qos 全称是 Quality of Service,它实际上是 Kubernetes 用来表达一个 pod 在资源能力上的服务质量的标准,Kubernetes 提供了三类的 Qos Class:
K8s 其实有一个不太好的地方,就是用户无法指定本身的 pod 是属于哪一类 Qos,而是经过 request 和 limit 的组合来自动地映射上 Qos Class。
经过上图的例子,你们能够看到:假如我提交的是上面的一个 spec,在 spec 提交成功以后,Kubernetes 会自动给补上一个 status,里面是 qosClass: Guaranteed,用户本身提交的时候,是无法定义本身的 Qos 等级。因此将这种方式称之为隐性的 Qos class 用法。
接下来介绍一下,咱们怎么经过 request 和 limit 的组合来肯定咱们想要的 Qos level。
Guaranteed Pod
首先咱们如何建立出来一个 Guaranteed Pod?Kubernetes 里面有一个要求:若是你要建立出一个 Guaranteed Pod,那么你的基础资源(就是包括 CPU 和 memory),必须它的 request==limit,其余的资源能够不相等。只有在这种条件下,它建立出来的 pod 才是一种 Guaranteed Pod,不然它会属于 Burstable,或者是 BestEffort Pod。
Burstable Pod
而后看一下,咱们怎么建立出来一个 Burstable Pod,Burstable Pod 的范围比较宽泛,它只要知足 CPU/Memory 的 request 和 limit 不相等,它就是一种 Burstable Pod。
接下来,为你们介绍一下:不一样的 Qos 在调度和底层表现有什么样的不一样?不一样的 Qos,它其实在调度和底层表现上都有一些不同。好比说调度表现,调度器只会使用 request 进行调度,也就是无论你配了多大的 limit,它都不会进行调度使用,它只会使用 request 进行调度。
在底层上,不一样的 Qos 表现更不相同。好比说 CPU,它实际上是按 request 来划分权重的,不一样的 Qos,它的 request 是彻底不同的,好比说像 Burstable 和 BestEffort,它可能 request 能够填很小的数字或者不填,这样的话,它的权重实际上是很是低的。像 BestEffort,它的权重多是只有 2,而 Burstable 或 Guaranteed,它的权重能够多到几千。
另外,当咱们开启了 kubelet 的一个特性,叫 cpu-manager-policy=static 的时候,咱们 Guaranteed Qos,若是它的 request 是一个整数的话,好比说配了 2,它会对 Guaranteed Pod 进行绑核。也就是具体像下面这个例子,它分配 CPU0 和 CPU1 给 Guaranteed Pod。
非整数的 Guaranteed/Burstable/BestEffort,它们的 CPU 会放在一块,组成一个 CPU share pool,好比说像上面这个例子,这台节点假如说有 8 个核,已经分配了 2 个核给整数的 Guaranteed 绑核,那么剩下的 6 个核 CPU2~CPU7,它会被非整数的 Guaranteed/Burstable/BestEffort 共享,而后它们会根据不一样的权重划分时间片来使用 6 个核的 CPU。
另外在 memory 上也会按照不一样的 Qos 进行划分:OOMScore。好比说 Guaranteed,它会配置默认的 -998 的 OOMScore;Burstable 的话,它会根据内存设计的大小和节点的关系来分配 2-999 的 OOMScore。BestEffort 会固定分配 1000 的 OOMScore,OOMScore 得分越高的话,在物理机出现 OOM 的时候会优先被 kill 掉。
另外在节点上的 eviction 动做上,不一样的 Qos 也是不同的,好比说发生 eviction 的时候,会优先考虑驱逐 BestEffort 的 pod。因此不一样的 Qos 其实在底层的表现是大相径庭的。这也反过来要求咱们在生产过程当中,根据不一样业务的要求和属性来配置资源的 Limits 和 Request,作到合理的规划 Qos Class。
Kubernetes 给咱们提供了一个能力叫:ResourceQuota 方法。它能够作到限制 namespace 资源用量。
具体的作法如上图右侧的 yaml 所示,能够看到它的 spec 包括了一个 hard 和 scopeSelector。hard 内容其实和 Resourcelist 很像,这里能够填一些基础的资源。可是它比 ResourceList 更丰富一点,它还能够填写一些 Pod,这样能够限制 Pod 数量能力。而后 scopeSelector 为这个 Resource 方法定义更丰富的索引能力。
好比上面的例子中,索引出非 BestEffort 的 pod,限制的 cpu 是 1000 个,memory 是 200G,Pod 是 10 个,而后 Scope 除了提供 NotBestEffort,它还提供了更丰富的索引范围,包括 Terminating/Not Terminating,BestEffort/NotBestEffort,PriorityClass。
当咱们建立了这样的 ResourceQuota 做用于集群,若是用户真的用超了资源,表现的行为是:它在提交 Pod spec 时,会收到一个 forbidden 的 403 错误,提示 exceeded quota。这样用户就没法再提交 cpu 或者是 memory,或者是 Pod 数量的资源。
假如再提交一个没有包含在这个 ResourceQuota 方案里面的资源,仍是能成功的。这就是 Kubernetes 里 ResourceQuota 的基本用法。 咱们能够用 ResourceQuota 方法来作到限制每个 namespace 的资源用量,从而保证其余用户的资源使用。
上面介绍完了基础资源的使用方式,也就是咱们作到了如何知足 Pod 资源要求。下面作一个小结:
经过 Request 和 Limit 来为不一样业务特色的 Pod 选择不一样的 QoS
介绍完了基础调度能力以后,下面来了解一下高级调度能力。
优先级调度
优先级调度和抢占,主要概念有:
首先来看一下调度过程提到的四个特色,咱们如何作到集群的合理利用?当集群资源足够的话,只须要经过基础调度能力就能组合出合理的使用方式。可是假如资源不够,咱们怎么作到集群的合理利用呢?一般的策略有两类:
在实际生产中,若是使用先到先得策略,是一种不公平的策略,由于公司业务里面确定是有高优先级的业务和低优先级的业务,因此优先级策略会比先到先得策略更可以符合平常公司业务特色。
接着介绍一下优先级策略下的优先级调度是什么样的一个概念。好比说有一个 Node 已经被一个 Pod 占用了,这个 Node 只有 2 个 CPU。另外一个高优先级 Pod 来的时候,低优先级的 Pod 应该把这两个 CPU 让给高优先级的 Pod 去使用。低优先级的 Pod 须要回到等待队列,或者是业务从新提交。这样的流程就是优先级抢占调度的一个流程。
在 Kubernetes 里,PodPriority 和 Preemption,就是优先级和抢占的特色,在 v1.14 版本中变成了 stable。而且 PodPriority 和 Preemption 默认都是开启的。
怎么使用?
如何使用优先级调度呢?须要建立一个 priorityClass,而后再为每一个 Pod 配置上不一样的 priorityClassName,这样就完成了优先级以及优先级调度的配置。
首先来看一下如何建立一个 priorityClass。上图右侧定义了两个 demo:
固然 Kubernetes 里面还内置了默认的优先级。如 DefaultpriorityWhenNoDefaultClassExistis,若是集群中没有配置 DefaultpriorityWhenNoDefaultClassExistis,那全部的 Pod 关于此项数值都会被设置成 0。
另外一个内置优先级是用户可配置最大优先级限制:HighestUserDefinablePriority = 10000000000(10 亿)
系统级别优先级:SystemCriticalPriority = 20000000000(20 亿)
内置系统级别优先级:
这就是优先级调度的基本配置以及内置的优先级配置。
当作完上面的配置后,整个优先级调度是怎样一个流程呢?下面将会介绍一下简单的过程。
首先介绍一下只触发优先级调度可是没有触发抢占调度的流程。
假若有一个 Pod1 和 Pod2,Pod1 配置了高优先级,Pod2 配置了低优先级。同时提交 Pod1 和 Pod2 到调度队列里。
调度器处理队列的时候会挑选一个高优先级的 Pod1 进行调度,通过调度过程把 Pod1 绑定到 Node1 上。
紧接着再调度 Pod1,由于 Node1 上已经存在了两个 Pod,资源不足,因此会遇到调度失败。
在调度失败时 Pod1 会进入抢占流程,这时会进行整个集群的节点筛选,最后挑出要抢占的 Pod 是 Pod2,此时调度器会把 Pod2 从 Node1 上移除数据。
再把 Pod1 调度到 Node1 上。这样就完成了一次抢占调度的流程。
接下来介绍一下具体的抢占策略和抢占的流程是什么样的。
上图右侧是整个优先级抢占的调度流程,也就是 kube-scheduler 的工做流程。首先一个 Pod 进入抢占的时候,会判断 Pod 是否拥有抢占的资格,有可能上次已经抢占过一次。若是符合抢占资格,它会先对全部的节点进行一次过滤,过滤出符合此次抢占要求的节点,若是不符合就过滤掉这批节点。
接着从过滤剩下的节点中,挑选出合适的节点进行抢占。此次抢占的过程会模拟一次调度,也就是把上面优先级低的 Pod 先移除出去,再把待抢占的 Pod 尝试可否放置到此节点上。而后经过这个过程选出一批节点,进入下一个过程叫 ProcessPreemptionWithExtenders。这是一个扩展的钩子,用户能够在这里加一些本身抢占节点的策略,若是没有扩展的钩子,这里面是不作任何动做的。
接下来的流程叫作 PickOneNodeForPreemption,就是从上面 selectNodeForPreemption list 里面挑选出最合适的一个节点,这是有必定的策略的。上图左侧简单介绍了一下策略:
简单介绍了一下调度的高级策略,在集群资源紧张的时候也能合理调度资源。咱们回顾一下作了哪些事情: