应用高可用
- 被抢占的 non-prod 任务放回 pending queue,等待重新调度。
在线业务的命是高可用,一个业务失去了高可用那么就完全没法提供在线服务,这个后果是不可承担的。
在线业务要永远保持在线业务的高可用,比如你要去部署或者扩容在线业务,他就会为你这个在线业务去做调度,当集群里面没有可用资源了怎么办?那么就要去杀掉离线业务,会将离线业务的资源抢过来,将其应用杀掉,将这个资源交给在线业务,这样先让在线业务先跑,但是离线业务不能丢,所以会将离线业务放进pending queue,等什么时候有资源了再重新跑。这样即保证了在线业务的高可用,又保证了离线业务的作业不可丢。
资源利用率
通过在线和离线业务的混合部署,使得资源利用率非常的高,如果我们只跑在线,一定会有些问题,业务波谷的资源是空闲的,为了节省整个资源中心数据成本,
• 通过将在线任务(prod)和离线任务(non-prod,batch)混合部署,空闲时,离线任务可以充分利用计算资源,繁忙时,在线任务通过抢占的方式保证优先得到执行,合理地利用资源。
--------------------------------------------------------------------------------------------------------------------------------
介绍完了基础调度能力之后,下面来了解一下高级调度能力。
优先级调度
优先级调度和抢占,主要概念有:
- Priority
- Preemption
首先来看一下调度过程提到的四个特点,我们如何做到集群的合理利用?
- 当集群资源足够的话,只需要通过基础调度能力就能组合出合理的使用方式。
- 但是假如资源不够,我们怎么做到集群的合理利用呢?
通常的策略有两类:
- 先到先得策略 (FIFO) -简单、相对公平,上手快
- 优先级策略 (Priority) - 符合日常公司业务特点
在实际生产中,如果使用先到先得策略,是一种不公平的策略,因为公司业务里面肯定是有高优先级的业务和低优先级的业务,所以优先级策略会比先到先得策略更能够符合日常公司业务特点。
接着介绍一下优先级策略下的优先级调度是什么样的一个概念。比如说有一个 Node 已经被一个 Pod 占用了,这个 Node 只有 2 个 CPU。
另一个高优先级 Pod 来的时候,低优先级的 Pod 应该把这两个 CPU 让给高优先级的 Pod 去使用。低优先级的 Pod 需要回到等待队列,或者是业务重新提交。这样的流程就是优先级抢占调度的一个流程。
在 Kubernetes 里,PodPriority 和 Preemption,就是优先级和抢占的特点,在 v1.14 版本中变成了 stable。并且 PodPriority 和 Preemption 默认都是开启的。
优先级调度配置
怎么使用?
如何使用优先级调度呢?需要创建一个 priorityClass,然后再为每个 Pod 配置上不同的 priorityClassName,这样就完成了优先级以及优先级调度的配置。
首先来看一下如何创建一个 priorityClass。上图右侧定义了两个 demo:
- 一个是创建名为 high 的 priorityClass,它是高优先级,得分为 10000;
- 然后还创建了一个 low 的 priorityClass,它的得分是 100。
并且在第三部分给 Pod 配置上了 high,Pod2 上配置了 low priorityClassName,蓝色部分显示了 pod 的 spec 的配置位置,就是在 spec 里面填写一个 priorityClassName: high。这样 Pod 和 priorityClass 做完配置,就为集群开启了一个 priorityClass 调度。
内置优先级配置
当然 Kubernetes 里面还内置了默认的优先级。如 DefaultpriorityWhenNoDefaultClassExistis,如果集群中没有配置 DefaultpriorityWhenNoDefaultClassExistis,那所有的 Pod 关于此项数值都会被设置成 0。
另一个内置优先级是用户可配置最大优先级限制:HighestUserDefinablePriority = 10000000000(10 亿)
系统级别优先级:SystemCriticalPriority = 20000000000(20 亿)
内置系统级别优先级:
- system-cluster-critical
- system-node-critical
这就是优先级调度的基本配置以及内置的优先级配置。
优先级调度过程(资源足够,在调度队列优先出队列,未触发抢占调度)
当做完上面的配置后,整个优先级调度是怎样一个流程呢?下面将会介绍一下简单的过程。
首先介绍一下只触发优先级调度但是没有触发抢占调度的流程。
假如有一个 Pod1 和 Pod2,Pod1 配置了高优先级,Pod2 配置了低优先级。同时提交 Pod1 和 Pod2 到调度队列里。
调度器处理队列的时候会挑选一个高优先级的 Pod1 进行调度,经过调度过程把 Pod1 绑定到 Node1 上。
其次再挑选一个低优先的 Pod2 进行同样的过程,绑定到 Node1 上。
这样就完成了一个简单的优先级调度的流程。
优先级抢占过程(资源不够,触发抢占)
假如高优先级的 Pod 在调度的时候没有资源,那么会是一个怎么样的流程呢?
首先是跟上文同样的场景,但是提前在 Node1 上放置了 Pod0,占去了一部分资源。同样有 Pod1 和 Pod2 待调度,Pod1 的优先级大于 Pod2。
假如先把 Pod2 调度上去,它经过一系列的调度过程绑定到了 Node1 上。
紧接着再调度 Pod1,因为 Node1 上已经存在了两个 Pod,资源不足,所以会遇到调度失败。
在调度失败时 Pod1 会进入抢占流程,这时会进行整个集群的节点筛选,最后挑出要抢占的 Pod 是 Pod2,此时调度器会把 Pod2 从 Node1 上移除数据。
再把 Pod1 调度到 Node1 上。这样就完成了一次抢占调度的流程。
优先级抢占策略
接下来介绍一下具体的抢占策略和抢占的流程是什么样的。
上图右侧是整个优先级抢占的调度流程,也就是 kube-scheduler 的工作流程。首先一个 Pod 进入抢占的时候,会判断 Pod 是否拥有抢占的资格,有可能上次已经抢占过一次。
如果符合抢占资格,它会先对所有的节点进行一次过滤,过滤出符合这次抢占要求的节点,如果不符合就过滤掉这批节点。
接着从过滤剩下的节点中,挑选出合适的节点进行抢占。这次抢占的过程会模拟一次调度,也就是把上面优先级低的 Pod 先移除出去,再把待抢占的 Pod 尝试能否放置到此节点上。然后通过这个过程选出一批节点,进入下一个过程叫 ProcessPreemptionWithExtenders。这是一个扩展的钩子,用户可以在这里加一些自己抢占节点的策略,如果没有扩展的钩子,这里面是不做任何动作的。
接下来的流程叫做 PickOneNodeForPreemption,就是从上面 selectNodeForPreemption list 里面挑选出最合适的一个节点,这是有一定的策略的。上图左侧简单介绍了一下策略:
- 优先选择打破 PDB 最少的节点(影响这个节点可用的服务可用性最少)
- 其次选择待抢占 Pods 中最大优先级最小的节点(对待抢占的pod进行排序,最大优先级的pod必须是所有节点中最小的)
- 再次选择待抢占 Pods 优先级加和最小的节点
- 接下来选择待抢占 Pods 数目最小的节点
- 最后选择拥有最晚启动 Pod 的节点
通过这五步串行策略过滤之后,会选出一个最合适的节点。然后对这个节点上待抢占的 Pod 进行 delete,这样就完成了一次待抢占的过程。
小结:如何做到集群资源合理利用?
上面就简单的介绍了调度的高级策略,这样可以在集群资源紧张的条件下也能够合理的利用集群的资源。
• 创建自定义的一些优先级类别( PriorityClass )
• 给不同类型 Pods 配置不同的优先级( PriorityClassName )
• 通过组合不同类型 Pods 运行和优先级抢占让集群资源和调度弹性起来