- 浏览: 118896 次
- 性别:
- 来自: 北京
-
最新评论
Kubernetes 调度过程
===============
首先来看第一部分 - Kubernetes 的调度过程。如下图所示,画了一个很简单的 Kubernetes 集群架构,它包括了一个 kube-ApiServer,一组 Web-hook Controllers,以及一个默认的调度器 kube-Scheduler,还有两台物理机节点 Node1 和 Node2,分别在上面部署了两个 kubelet。

我们来看一下,假如要向这个 Kubernetes 集群提交一个 pod,它的调度过程是什么样的一个流程?
假设我们已经写好了一个 yaml 文件,就是下图中的橙色圆圈 pod1,然后往 kube-ApiServer 里提交这个 yaml 文件。

此时 ApiServer 会先把这个待创建的请求路由给我们的 webhook Controllers 进行校验。

通过校验之后,ApiServer 会在集群里面生成一个 pod,此时生成的 pod,它的 nodeName 是空的,并且它的 phase 是 Pending 状态。在生成了这个 pod 之后,kube-Scheduler 以及 kubelet 都能 watch 到这个 pod 的生成事件,kube-Scheduler 发现这个 pod 的 nodeName 是空的之后,会认为这个 pod 是处于未调度状态。

接下来,它会把这个 pod 拿到自己里面进行调度,通过一系列的调度算法,包括一系列的过滤和打分的算法后,Schedule 会选出一台最合适的节点,并且把这一台节点的名称绑定在这个 pod 的 spec 上,完成一次调度的过程。
此时我们发现,pod 的 spec 上,nodeName 已经更新成了 Node1 这个 node,更新完 nodeName 之后,在 Node1 上的这台 kubelet 会 watch 到这个 pod 是属于自己节点上的一个 pod。

然后它会把这个 pod 拿到节点上进行操作,包括创建一些容器 storage 以及 network,最后等所有的资源都准备完成,kubelet 会把状态更新为 Running,这样一个完整的调度过程就结束了。
通过刚刚一个调度过程的演示,我们用一句话来概括一下调度过程:它其实就是在做一件事情,即把 pod 放到**合适**的 node 上。
这里有个关键字“合适”,什么是合适呢?下面给出几点合适定义的特点:
* 首先要满足 pod 的资源要求;
* 其次要满足 pod 的一些特殊关系的要求;
* 再次要满足 node 的一些限制条件的要求;
* 最后还要做到整个集群资源的合理利用。
做到以上的要求后,可以认为我们把 pod 放到了一个合适的节点上了。
接下来我会为大家介绍 Kubernetes 是怎么做到满足这些 pod 和 node 的要求的。
Kubernetes 基础调度力
================
下面为大家介绍一下 Kubernetes 的基础调度能力,Kubernetes 的基础调度能力会以两部分来展开介绍:
1. 第一部分是资源调度——介绍一下 Kubernetes 基本的一些 Resources 的配置方式,还有 Qos 的概念,以及 Resource Quota 的概念和使用方式;
2. 第二部分是关系调度——在关系调度上,介绍两种关系场景:
1. pod 和 pod 之间的关系场景,包括怎么去亲和一个 pod,怎么去互斥一个 pod?
2. pod 和 node 之间的关系场景,包括怎么去亲和一个 node,以及有一些 node 怎么去限制 pod 调度上来。
如何满足 Pod 资源要求
-------------
### pod 的资源配置方法

上图是 pod spec 的一个 demo,我们的资源其实是填在 pod spec 中,具体在 containers 的 resources 里。
resources 包含两个部分:
* 第一部分是 requests;
* 第二部分是 limits。
这两部分里面的内容是一模一样的,但是它代表的含义有所不同:request 代表的是对这个 pod 基本保底的一些资源要求;limit 代表的是对这个 pod 可用能力上限的一种限制。request、limit 的实现是一个 map 结构,它里面可以填不同的资源的 key/value。
我们可以大概分成四大类的基础资源:
* 第一类是 CPU 资源;
* 第二类是 memory;
* 第三类是 ephemeral-storage,是一种临时存储;
* 第四类是通用的扩展资源,比如说像 GPU。
CPU 资源,比如说上面的例子填的是2,申请的是两个 CPU,也可以写成 2000m 这种十进制的转换方式,来表达有些时候可能对 CPU 可能是一个小数的需求,比如说像 0.2 个CPU,可以填 200m。而这种方式在 memory 和 storage 之上,它是一个二进制的表达方式,如上图右侧所示,申请的是 1GB 的 memory,同样也可以填成一个 1024mi 的表达方式,这样可以更清楚地表达我们对 memory 的需求。
在扩展资源上,Kubernetes 有一个要求,即扩展资源必须是整数的,所以我们没法申请到 0.5 的 GPU 这样的资源,只能申请 1 个 GPU 或者 2 个 GPU。
这里为大家介绍完了基础资源的申请方式。
接下来,我会详细给大家介绍一下 request 和 limit 到底有什么区别,以及如何通过 request/limit 来表示 QoS。
### Pod 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:
1. 第一类是 Guaranteed,它是一类高 QoS Class,一般拿 Guaranteed 配置给一些需要资源保障能力的 pods;
2. 第二类是 Burstable,它是中等的一个 QoS label,一般会为一些希望有弹性能力的 pod 来配置 Burstable;
3. 第三类是 BestEffort,它是低QoS Class,通过名字我们也知道,它是一种尽力而为式的服务质量,K8S不承诺保障这类Pods服务质量。
K8s 其实有一个不太好的地方,就是用户没法直接指定自己的 pod 是属于哪一类 QoS,而是通过 request 和 limit 的组合来自动地映射上 QoS Class。
通过上图的例子,大家可以看到:假如我提交的是上面的一个 spec,在 spec 提交成功之后,Kubernetes 会自动给补上一个 status,里面是 qosClass: Guaranteed,用户自己提交的时候,是没法定义自己的 QoS 等级。所以将这种方式称之为隐性的 QoS class 用法。
### Pod QoS 配置
接下来介绍一下,我们怎么通过 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。

比如说上面的例子,可以不用填写 memory 的资源,只要填写 CPU 的资源,它就是一种 Burstable Pod。
**BestEffort Pod**

第三类 BestEffort Pod,它也是条件比较死的一种使用方式。它必须是所有资源的 request/limit 都不填,才是一种 BestEffort Pod。
所以这里可以看到,通过 request 和 limit 不同的用法,可以组合出不同的 Pod QoS。
### 不同的 QoS 表现
接下来,为大家介绍一下:不同的 QoS 在调度和底层表现有什么样的不同?不同的 QoS,它其实在调度和底层表现上都有一些不一样。比如说调度表现,调度器只会使用 request 进行调度,也就是说不管你配了多大的 limit,它都不会进行调度使用。
在底层上,不同的 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 Pod,会固定配置默认的 -998 的 OOMScore;而 Burstable Pod 会根据 Pod 内存设计的大小和节点内存的比例来分配 2-999 的 OOMScore;BestEffort Pod 会固定分配 1000 的 OOMScore,OOMScore 得分越高的话,在物理机出现 OOM 的时候会优先被 kill 掉。
另外在节点上的 eviction 动作上,不同的 QoS 行为也是不一样的,比如说发生 eviction 的时候,会优先考虑驱逐 BestEffort 的 pod。所以不同的 QoS 在底层的表现是截然不同的。这反过来也要求我们在生产过程中,根据不同业务的要求和属性来配置资源的 Limits 和 Requests,做到合理的规划 QoS Class。
### 资源 Quota
在生产中我们还会遇到一个场景:假如集群是由多个人同时提交的,或者是多个业务同时在使用,我们肯定要限制某个业务或某个人提交的总量,防止整个集群的资源都会被一个业务使用掉,导致另一个业务没有资源使用。

Kubernetes 给我们提供了一个能力叫 ResourceQuota。它可以做到限制 namespace 资源用量。
具体的做法如上图右侧的 yaml 所示,可以看到它的 spec 包括了一个 hard 和 scopeSelector。hard 内容其实和 Resource 很像,这里可以填一些基础的资源。但是它比 Resource list 更丰富一点,还可以填写一些 Pod,这样可以限制 Pod 数量。另外,scopeSelector 还为这个 ResourceQuota 提供了更丰富的索引能力。
比如上面的例子中,索引出非 BestEffort 的 pod,限制的 cpu 是 1000 个,memory 是 200G,Pod 是 10 个。
ScopeName 除了提供 NotBestEffort,它还提供了更丰富的索引范围,包括 Terminating/Not Terminating,BestEffort/NotBestEffort,PriorityClass。
当我们创建了这样的 ResourceQuota 作用于集群,如果用户真的用超了资源,表现的行为是:它在提交 Pod spec 时,会收到一个 forbidden 的 403 错误,提示 exceeded quota。这样用户就无法再提交对应用超的资源了。
而如果再提交一个没有包含在这个 ResourceQuota 里的资源,还是能成功的。
这就是 Kubernetes 里 ResourceQuota 的基本用法。 我们可以用 ResourceQuota 方法来做到限制每一个 namespace 的资源用量,从而保证其他用户的资源使用。
### 小结:如何满足 Pod 资源要求?
上面介绍完了基础资源的使用方式,也就是我们做到了如何满足 Pod 资源要求。下面做一个小结:
* Pod 要配置合理的资源要求
* CPU/Memory/EphemeralStorage/GPU
* 通过 Request 和 Limit 来为不同业务特点的 Pod 选择不同的 QoS
* Guaranteed:敏感型,需要业务保障
* Burstable:次敏感型,需要弹性业务
* BestEffort:可容忍性业务
* 为每个 NS 配置 ResourceQuota 来防止过量使用,保障其他人的资源可用
如何满足 Pod 与 Pod 关系要求?
--------------------
接下来给大家介绍一下 Pod 的关系调度,首先是 Pod 和 Pod 的关系调度。我们在平时使用中可能会遇到一些场景:比如说一个 Pod 必须要和另外一个 Pod 放在一起,或者不能和另外一个 Pod 放在一起。
在这种要求下, Kubernetes 提供了两类能力:
* 第一类能力称之为 Pod 亲和调度:PodAffinity;
* 第二类就是 Pod 反亲和调度:PodAntAffinity。
### Pod 亲和调度

首先我们来看 Pod 亲和调度,假如我想把一个 Pod 和另一个 Pod 放在一起,这时我们可以看上图中的实例写法,填写上 podAffinity,然后填上 required 要求。
在这个例子中,必须要调度到带了 key: k1 的 Pod 所在的节点,并且打散粒度是按照节点粒度去打散索引的。这种情况下,假如能找到带 key: k1 的 Pod 所在节点,就会调度成功。假如这个集群不存在这样的 Pod 节点,或者是资源不够的时候,那就会调度失败。这是一个严格的亲和调度,我们叫做强制亲和调度。

有些时候我们并不需要这么严格的调度策略。这时候可以把 required 改成 preferred,变成一个优先亲和调度。也就是优先可以调度带 key: k2 的 Pod 所在节点。并且这个 preferred 里面可以是一个 list 选择,可以填上多个条件,比如权重等于 100 的是 key: k2,权重等于 10 的是 key: k1。那调度器在调度的时候会优先把这个 Pod 分配到权重分更高的调度条件节点上去。
### Pod 反亲和调度
上面介绍了亲和调度,反亲和调度与亲和调度比较相似,功能上是取反的,但语法上基本上是一样的。仅是 podAffinity 换成了 podAntiAffinity,也是包括 required 强制反亲和,以及一个 preferred 优先反亲和。
这里举了两个例子:一个是禁止调度到带了 key: k1 标签的 Pod 所在节点;另一个是优先反亲和调度到带了 key: k2 标签的 Pod 所在节点。

Kubernetes 除了 In 这个 Operator 语法之外,还提供了更多丰富的语法组合来给大家使用。比如说 In/NotIn/Exists/DoesNotExist 这些组合方式。上图的例子用的是 In,比如说第一个强制反亲和例子里面,相当于我们必须要禁止调度到带了 key: k1 标签的 Pod 所在节点。
同样的功能也可以使用 Exists,Exists 范围可能会比 In 范围更大,当 Operator 填了 Exists,就不需要再填写 values。它做到的效果就是禁止调度到带了 key: k1 标签的 Pod 所在节点,不管 values 是什么值,只要带了 k1 这个 key 标签的 Pod 所在节点,都不能调度过去。
以上就是 Pod 与 Pod 之间的关系调度。
如何满足 Pod 与 Node 关系调度
--------------------
Pod 与 Node 的关系调度又称之为 Node 亲和调度,主要给大家介绍两类使用方法。
### NodeSelector

第一类是 NodeSelector,这是一类相对比较简单的用法。比如说有个场景:必须要调度 Pod 到带了 k1: v1 标签的 Node 上,这时可以在 Pod 的 spec 中填写一个 nodeSelector 要求。nodeSelector 本质是一个 map 结构,里面可以直接写上对 node 标签的要求,比如 k1: v1。这样我的 Pod 就会强制调度到带了 k1: v1 标签的 Node 上。
### NodeAffinity
NodeSelector 是一个非常简单的用法,但这个用法有个问题:它只能强制亲和调度,假如我想优先调度,就没法用 nodeSelector 来做。于是 Kubernetes 社区又新加了一个用法,叫做 NodeAffinity。

它和 PodAffinity 有点类似,也提供了两类调度的策略:
* 第一类是 required,必须调度到某一类 Node 上;
* 第二类是 preferred,就是优先调度到某一类 Node 上。
它的基本语法和上文中的 PodAffinity 以及 PodAntiAffinity 也是类似的。在 Operator 上,NodeAffinity 提供了比 PodAffinity 更丰富的 Operator 内容。增加了 Gt 和 Lt,数值比较的用法。当使用 Gt 的时候,values 只能填写数字。
### Node 标记/容忍
还有第三类调度,可以通过给 Node 打一些标记,来限制 Pod 调度到某些 Node 上。Kubernetes 把这些标记称之为 Taints,它的字面意思是污染。

那我们如何限制 Pod 调度到某些 Node 上呢?比如说现在有个 node 叫 demo-node,这个节点有问题,我想限制一些 Pod 调度上来。这时可以给这个节点打一个 taints,taints 内容包括 key、value、effect:
* key 就是配置的键值
* value 就是内容
* effect 是标记了这个 taints 行为是什么
目前 Kubernetes 里面有三个 taints 行为:
1. NoSchedule 禁止新的 Pod 调度上来;
2. PreferNoSchedul 尽量不调度到这台;
3. NoExecute 会 evict 没有对应 toleration 的 Pods,并且也不会调度新的上来。这个策略是非常严格的,大家在使用的时候要小心一点。
如上图绿色部分,给这个 demo-node 打了 k1=v1,并且 effect 等于 NoSchedule 之后。它的效果是:新建的 Pod 没有专门容忍这个 taint,那就没法调度到这个节点上去了。
假如有些 Pod 是可以调度到这个节点上的,应该怎么来做呢?这时可以在 Pod 上打一个 Pod Tolerations。从上图中蓝色部分可以看到:在 Pod 的 spec 中填写一个 Tolerations,它里面也包含了 key、value、effect,这三个值和 taint 的值是完全对应的,taint 里面的 key,value,effect 是什么内容,Tolerations 里面也要填写相同的内容。
Tolerations 还多了一个选项 Operator,Operator 有两个 value:Exists/Equal。Equal 的概念是必须要填写 value,而 Exists 就跟上文说的 NodeAffinity 一样,不需要填写 value,只要 key 值对上了,就认为它跟 taints 是匹配的。
上图中的例子,给 Pod 打了一个 Tolerations,只有打了这个 Tolerations 的 Pod,才能调度到绿色部分打了 taints 的 Node 上去。这样的好处是 Node 可以有选择性的调度一些 Pod 上来,而不是所有的 Pod 都可以调度上来,这样就做到了限制某些 Pod 调度到某些 Node 的效果。
小结
--
我们已经介绍完了 Pod/Node 的特殊关系和条件调度,来做一下小结。
首先假如有需求是处理 Pod 与 Pod 的时候,比如 Pod 和另一个 Pod 有亲和的关系或者是互斥的关系,可以给它们配置下面的参数:
* PodAffinity
* PodAntiAffinity
假如存在 Pod 和 Node 有亲和关系,可以配置下面的参数:
* NodeSelector
* NodeAffinity
假如有些 Node 是限制某些 Pod 调度的,比如说一些故障的 Node,或者说是一些特殊业务的 Node,可以配置下面的参数:
* Node -- Taints
* Pod -- Tolerations
Kubernetes 高级调度能力
=================
介绍完了基础调度能力之后,下面来了解一下高级调度能力。
优先级调度
-----
优先级调度和抢占,主要概念有:
* 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。
同时在第三部分给 Pod1 配置上了 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
这就是K8S优先级调度里内置的优先级配置。
优先级调度过程
-------
下面介绍简单的优先级调度过程:
首先介绍只触发优先级调度但是没有触发抢占调度的流程。
假如有一个 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 中最大优先级最小的节点;
* 再次选择待抢占 Pods 优先级加和最小的节点;
* 接下来选择待抢占 Pods 数目最小的节点;
* 最后选择拥有最晚启动 Pod 的节点;
通过这五步串行策略过滤之后,会选出一个最合适的节点。然后对这个节点上待抢占的 Pod 进行 delete,这样就完成了一次待抢占的过程。
小结
--
简单介绍了一下调度的高级策略,在集群资源紧张的时候也能合理调度资源。我们回顾一下做了哪些事情:
* 创建自定义的一些优先级类别 (PriorityClass);
* 给不同类型 Pods 配置不同的优先级 (PriorityClassName);
* 通过组合不同类型 Pods 运行和优先级抢占让集群资源和调度弹性起来。
[原文链接](https://yq.aliyun.com/articles/740356?utm_content=g_1000095993)
本文为阿里云内容,未经允许不得转载。
===============
首先来看第一部分 - Kubernetes 的调度过程。如下图所示,画了一个很简单的 Kubernetes 集群架构,它包括了一个 kube-ApiServer,一组 Web-hook Controllers,以及一个默认的调度器 kube-Scheduler,还有两台物理机节点 Node1 和 Node2,分别在上面部署了两个 kubelet。

我们来看一下,假如要向这个 Kubernetes 集群提交一个 pod,它的调度过程是什么样的一个流程?
假设我们已经写好了一个 yaml 文件,就是下图中的橙色圆圈 pod1,然后往 kube-ApiServer 里提交这个 yaml 文件。

此时 ApiServer 会先把这个待创建的请求路由给我们的 webhook Controllers 进行校验。

通过校验之后,ApiServer 会在集群里面生成一个 pod,此时生成的 pod,它的 nodeName 是空的,并且它的 phase 是 Pending 状态。在生成了这个 pod 之后,kube-Scheduler 以及 kubelet 都能 watch 到这个 pod 的生成事件,kube-Scheduler 发现这个 pod 的 nodeName 是空的之后,会认为这个 pod 是处于未调度状态。

接下来,它会把这个 pod 拿到自己里面进行调度,通过一系列的调度算法,包括一系列的过滤和打分的算法后,Schedule 会选出一台最合适的节点,并且把这一台节点的名称绑定在这个 pod 的 spec 上,完成一次调度的过程。
此时我们发现,pod 的 spec 上,nodeName 已经更新成了 Node1 这个 node,更新完 nodeName 之后,在 Node1 上的这台 kubelet 会 watch 到这个 pod 是属于自己节点上的一个 pod。

然后它会把这个 pod 拿到节点上进行操作,包括创建一些容器 storage 以及 network,最后等所有的资源都准备完成,kubelet 会把状态更新为 Running,这样一个完整的调度过程就结束了。
通过刚刚一个调度过程的演示,我们用一句话来概括一下调度过程:它其实就是在做一件事情,即把 pod 放到**合适**的 node 上。
这里有个关键字“合适”,什么是合适呢?下面给出几点合适定义的特点:
* 首先要满足 pod 的资源要求;
* 其次要满足 pod 的一些特殊关系的要求;
* 再次要满足 node 的一些限制条件的要求;
* 最后还要做到整个集群资源的合理利用。
做到以上的要求后,可以认为我们把 pod 放到了一个合适的节点上了。
接下来我会为大家介绍 Kubernetes 是怎么做到满足这些 pod 和 node 的要求的。
Kubernetes 基础调度力
================
下面为大家介绍一下 Kubernetes 的基础调度能力,Kubernetes 的基础调度能力会以两部分来展开介绍:
1. 第一部分是资源调度——介绍一下 Kubernetes 基本的一些 Resources 的配置方式,还有 Qos 的概念,以及 Resource Quota 的概念和使用方式;
2. 第二部分是关系调度——在关系调度上,介绍两种关系场景:
1. pod 和 pod 之间的关系场景,包括怎么去亲和一个 pod,怎么去互斥一个 pod?
2. pod 和 node 之间的关系场景,包括怎么去亲和一个 node,以及有一些 node 怎么去限制 pod 调度上来。
如何满足 Pod 资源要求
-------------
### pod 的资源配置方法

上图是 pod spec 的一个 demo,我们的资源其实是填在 pod spec 中,具体在 containers 的 resources 里。
resources 包含两个部分:
* 第一部分是 requests;
* 第二部分是 limits。
这两部分里面的内容是一模一样的,但是它代表的含义有所不同:request 代表的是对这个 pod 基本保底的一些资源要求;limit 代表的是对这个 pod 可用能力上限的一种限制。request、limit 的实现是一个 map 结构,它里面可以填不同的资源的 key/value。
我们可以大概分成四大类的基础资源:
* 第一类是 CPU 资源;
* 第二类是 memory;
* 第三类是 ephemeral-storage,是一种临时存储;
* 第四类是通用的扩展资源,比如说像 GPU。
CPU 资源,比如说上面的例子填的是2,申请的是两个 CPU,也可以写成 2000m 这种十进制的转换方式,来表达有些时候可能对 CPU 可能是一个小数的需求,比如说像 0.2 个CPU,可以填 200m。而这种方式在 memory 和 storage 之上,它是一个二进制的表达方式,如上图右侧所示,申请的是 1GB 的 memory,同样也可以填成一个 1024mi 的表达方式,这样可以更清楚地表达我们对 memory 的需求。
在扩展资源上,Kubernetes 有一个要求,即扩展资源必须是整数的,所以我们没法申请到 0.5 的 GPU 这样的资源,只能申请 1 个 GPU 或者 2 个 GPU。
这里为大家介绍完了基础资源的申请方式。
接下来,我会详细给大家介绍一下 request 和 limit 到底有什么区别,以及如何通过 request/limit 来表示 QoS。
### Pod 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:
1. 第一类是 Guaranteed,它是一类高 QoS Class,一般拿 Guaranteed 配置给一些需要资源保障能力的 pods;
2. 第二类是 Burstable,它是中等的一个 QoS label,一般会为一些希望有弹性能力的 pod 来配置 Burstable;
3. 第三类是 BestEffort,它是低QoS Class,通过名字我们也知道,它是一种尽力而为式的服务质量,K8S不承诺保障这类Pods服务质量。
K8s 其实有一个不太好的地方,就是用户没法直接指定自己的 pod 是属于哪一类 QoS,而是通过 request 和 limit 的组合来自动地映射上 QoS Class。
通过上图的例子,大家可以看到:假如我提交的是上面的一个 spec,在 spec 提交成功之后,Kubernetes 会自动给补上一个 status,里面是 qosClass: Guaranteed,用户自己提交的时候,是没法定义自己的 QoS 等级。所以将这种方式称之为隐性的 QoS class 用法。
### Pod QoS 配置
接下来介绍一下,我们怎么通过 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。

比如说上面的例子,可以不用填写 memory 的资源,只要填写 CPU 的资源,它就是一种 Burstable Pod。
**BestEffort Pod**

第三类 BestEffort Pod,它也是条件比较死的一种使用方式。它必须是所有资源的 request/limit 都不填,才是一种 BestEffort Pod。
所以这里可以看到,通过 request 和 limit 不同的用法,可以组合出不同的 Pod QoS。
### 不同的 QoS 表现
接下来,为大家介绍一下:不同的 QoS 在调度和底层表现有什么样的不同?不同的 QoS,它其实在调度和底层表现上都有一些不一样。比如说调度表现,调度器只会使用 request 进行调度,也就是说不管你配了多大的 limit,它都不会进行调度使用。
在底层上,不同的 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 Pod,会固定配置默认的 -998 的 OOMScore;而 Burstable Pod 会根据 Pod 内存设计的大小和节点内存的比例来分配 2-999 的 OOMScore;BestEffort Pod 会固定分配 1000 的 OOMScore,OOMScore 得分越高的话,在物理机出现 OOM 的时候会优先被 kill 掉。
另外在节点上的 eviction 动作上,不同的 QoS 行为也是不一样的,比如说发生 eviction 的时候,会优先考虑驱逐 BestEffort 的 pod。所以不同的 QoS 在底层的表现是截然不同的。这反过来也要求我们在生产过程中,根据不同业务的要求和属性来配置资源的 Limits 和 Requests,做到合理的规划 QoS Class。
### 资源 Quota
在生产中我们还会遇到一个场景:假如集群是由多个人同时提交的,或者是多个业务同时在使用,我们肯定要限制某个业务或某个人提交的总量,防止整个集群的资源都会被一个业务使用掉,导致另一个业务没有资源使用。

Kubernetes 给我们提供了一个能力叫 ResourceQuota。它可以做到限制 namespace 资源用量。
具体的做法如上图右侧的 yaml 所示,可以看到它的 spec 包括了一个 hard 和 scopeSelector。hard 内容其实和 Resource 很像,这里可以填一些基础的资源。但是它比 Resource list 更丰富一点,还可以填写一些 Pod,这样可以限制 Pod 数量。另外,scopeSelector 还为这个 ResourceQuota 提供了更丰富的索引能力。
比如上面的例子中,索引出非 BestEffort 的 pod,限制的 cpu 是 1000 个,memory 是 200G,Pod 是 10 个。
ScopeName 除了提供 NotBestEffort,它还提供了更丰富的索引范围,包括 Terminating/Not Terminating,BestEffort/NotBestEffort,PriorityClass。
当我们创建了这样的 ResourceQuota 作用于集群,如果用户真的用超了资源,表现的行为是:它在提交 Pod spec 时,会收到一个 forbidden 的 403 错误,提示 exceeded quota。这样用户就无法再提交对应用超的资源了。
而如果再提交一个没有包含在这个 ResourceQuota 里的资源,还是能成功的。
这就是 Kubernetes 里 ResourceQuota 的基本用法。 我们可以用 ResourceQuota 方法来做到限制每一个 namespace 的资源用量,从而保证其他用户的资源使用。
### 小结:如何满足 Pod 资源要求?
上面介绍完了基础资源的使用方式,也就是我们做到了如何满足 Pod 资源要求。下面做一个小结:
* Pod 要配置合理的资源要求
* CPU/Memory/EphemeralStorage/GPU
* 通过 Request 和 Limit 来为不同业务特点的 Pod 选择不同的 QoS
* Guaranteed:敏感型,需要业务保障
* Burstable:次敏感型,需要弹性业务
* BestEffort:可容忍性业务
* 为每个 NS 配置 ResourceQuota 来防止过量使用,保障其他人的资源可用
如何满足 Pod 与 Pod 关系要求?
--------------------
接下来给大家介绍一下 Pod 的关系调度,首先是 Pod 和 Pod 的关系调度。我们在平时使用中可能会遇到一些场景:比如说一个 Pod 必须要和另外一个 Pod 放在一起,或者不能和另外一个 Pod 放在一起。
在这种要求下, Kubernetes 提供了两类能力:
* 第一类能力称之为 Pod 亲和调度:PodAffinity;
* 第二类就是 Pod 反亲和调度:PodAntAffinity。
### Pod 亲和调度

首先我们来看 Pod 亲和调度,假如我想把一个 Pod 和另一个 Pod 放在一起,这时我们可以看上图中的实例写法,填写上 podAffinity,然后填上 required 要求。
在这个例子中,必须要调度到带了 key: k1 的 Pod 所在的节点,并且打散粒度是按照节点粒度去打散索引的。这种情况下,假如能找到带 key: k1 的 Pod 所在节点,就会调度成功。假如这个集群不存在这样的 Pod 节点,或者是资源不够的时候,那就会调度失败。这是一个严格的亲和调度,我们叫做强制亲和调度。

有些时候我们并不需要这么严格的调度策略。这时候可以把 required 改成 preferred,变成一个优先亲和调度。也就是优先可以调度带 key: k2 的 Pod 所在节点。并且这个 preferred 里面可以是一个 list 选择,可以填上多个条件,比如权重等于 100 的是 key: k2,权重等于 10 的是 key: k1。那调度器在调度的时候会优先把这个 Pod 分配到权重分更高的调度条件节点上去。
### Pod 反亲和调度
上面介绍了亲和调度,反亲和调度与亲和调度比较相似,功能上是取反的,但语法上基本上是一样的。仅是 podAffinity 换成了 podAntiAffinity,也是包括 required 强制反亲和,以及一个 preferred 优先反亲和。
这里举了两个例子:一个是禁止调度到带了 key: k1 标签的 Pod 所在节点;另一个是优先反亲和调度到带了 key: k2 标签的 Pod 所在节点。

Kubernetes 除了 In 这个 Operator 语法之外,还提供了更多丰富的语法组合来给大家使用。比如说 In/NotIn/Exists/DoesNotExist 这些组合方式。上图的例子用的是 In,比如说第一个强制反亲和例子里面,相当于我们必须要禁止调度到带了 key: k1 标签的 Pod 所在节点。
同样的功能也可以使用 Exists,Exists 范围可能会比 In 范围更大,当 Operator 填了 Exists,就不需要再填写 values。它做到的效果就是禁止调度到带了 key: k1 标签的 Pod 所在节点,不管 values 是什么值,只要带了 k1 这个 key 标签的 Pod 所在节点,都不能调度过去。
以上就是 Pod 与 Pod 之间的关系调度。
如何满足 Pod 与 Node 关系调度
--------------------
Pod 与 Node 的关系调度又称之为 Node 亲和调度,主要给大家介绍两类使用方法。
### NodeSelector

第一类是 NodeSelector,这是一类相对比较简单的用法。比如说有个场景:必须要调度 Pod 到带了 k1: v1 标签的 Node 上,这时可以在 Pod 的 spec 中填写一个 nodeSelector 要求。nodeSelector 本质是一个 map 结构,里面可以直接写上对 node 标签的要求,比如 k1: v1。这样我的 Pod 就会强制调度到带了 k1: v1 标签的 Node 上。
### NodeAffinity
NodeSelector 是一个非常简单的用法,但这个用法有个问题:它只能强制亲和调度,假如我想优先调度,就没法用 nodeSelector 来做。于是 Kubernetes 社区又新加了一个用法,叫做 NodeAffinity。

它和 PodAffinity 有点类似,也提供了两类调度的策略:
* 第一类是 required,必须调度到某一类 Node 上;
* 第二类是 preferred,就是优先调度到某一类 Node 上。
它的基本语法和上文中的 PodAffinity 以及 PodAntiAffinity 也是类似的。在 Operator 上,NodeAffinity 提供了比 PodAffinity 更丰富的 Operator 内容。增加了 Gt 和 Lt,数值比较的用法。当使用 Gt 的时候,values 只能填写数字。
### Node 标记/容忍
还有第三类调度,可以通过给 Node 打一些标记,来限制 Pod 调度到某些 Node 上。Kubernetes 把这些标记称之为 Taints,它的字面意思是污染。

那我们如何限制 Pod 调度到某些 Node 上呢?比如说现在有个 node 叫 demo-node,这个节点有问题,我想限制一些 Pod 调度上来。这时可以给这个节点打一个 taints,taints 内容包括 key、value、effect:
* key 就是配置的键值
* value 就是内容
* effect 是标记了这个 taints 行为是什么
目前 Kubernetes 里面有三个 taints 行为:
1. NoSchedule 禁止新的 Pod 调度上来;
2. PreferNoSchedul 尽量不调度到这台;
3. NoExecute 会 evict 没有对应 toleration 的 Pods,并且也不会调度新的上来。这个策略是非常严格的,大家在使用的时候要小心一点。
如上图绿色部分,给这个 demo-node 打了 k1=v1,并且 effect 等于 NoSchedule 之后。它的效果是:新建的 Pod 没有专门容忍这个 taint,那就没法调度到这个节点上去了。
假如有些 Pod 是可以调度到这个节点上的,应该怎么来做呢?这时可以在 Pod 上打一个 Pod Tolerations。从上图中蓝色部分可以看到:在 Pod 的 spec 中填写一个 Tolerations,它里面也包含了 key、value、effect,这三个值和 taint 的值是完全对应的,taint 里面的 key,value,effect 是什么内容,Tolerations 里面也要填写相同的内容。
Tolerations 还多了一个选项 Operator,Operator 有两个 value:Exists/Equal。Equal 的概念是必须要填写 value,而 Exists 就跟上文说的 NodeAffinity 一样,不需要填写 value,只要 key 值对上了,就认为它跟 taints 是匹配的。
上图中的例子,给 Pod 打了一个 Tolerations,只有打了这个 Tolerations 的 Pod,才能调度到绿色部分打了 taints 的 Node 上去。这样的好处是 Node 可以有选择性的调度一些 Pod 上来,而不是所有的 Pod 都可以调度上来,这样就做到了限制某些 Pod 调度到某些 Node 的效果。
小结
--
我们已经介绍完了 Pod/Node 的特殊关系和条件调度,来做一下小结。
首先假如有需求是处理 Pod 与 Pod 的时候,比如 Pod 和另一个 Pod 有亲和的关系或者是互斥的关系,可以给它们配置下面的参数:
* PodAffinity
* PodAntiAffinity
假如存在 Pod 和 Node 有亲和关系,可以配置下面的参数:
* NodeSelector
* NodeAffinity
假如有些 Node 是限制某些 Pod 调度的,比如说一些故障的 Node,或者说是一些特殊业务的 Node,可以配置下面的参数:
* Node -- Taints
* Pod -- Tolerations
Kubernetes 高级调度能力
=================
介绍完了基础调度能力之后,下面来了解一下高级调度能力。
优先级调度
-----
优先级调度和抢占,主要概念有:
* 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。
同时在第三部分给 Pod1 配置上了 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
这就是K8S优先级调度里内置的优先级配置。
优先级调度过程
-------
下面介绍简单的优先级调度过程:
首先介绍只触发优先级调度但是没有触发抢占调度的流程。
假如有一个 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 中最大优先级最小的节点;
* 再次选择待抢占 Pods 优先级加和最小的节点;
* 接下来选择待抢占 Pods 数目最小的节点;
* 最后选择拥有最晚启动 Pod 的节点;
通过这五步串行策略过滤之后,会选出一个最合适的节点。然后对这个节点上待抢占的 Pod 进行 delete,这样就完成了一次待抢占的过程。
小结
--
简单介绍了一下调度的高级策略,在集群资源紧张的时候也能合理调度资源。我们回顾一下做了哪些事情:
* 创建自定义的一些优先级类别 (PriorityClass);
* 给不同类型 Pods 配置不同的优先级 (PriorityClassName);
* 通过组合不同类型 Pods 运行和优先级抢占让集群资源和调度弹性起来。
[原文链接](https://yq.aliyun.com/articles/740356?utm_content=g_1000095993)
本文为阿里云内容,未经允许不得转载。
相关推荐
内容概要:本文详细介绍了利用MATLAB及其工具箱YALMIP和CPLEX进行电动汽车削峰填谷的多目标优化调度方法。通过设定不同的权重系数,将电池损耗成本、峰谷差以及负荷波动三个关键因素整合为目标函数,实现了对电动汽车充放电行为的有效管理。文中不仅展示了具体的数学模型和代码实现,还讨论了求解过程中的一些技巧和注意事项,如充放电互斥约束的设计、求解器的选择与配置等。此外,作者通过实例演示了优化前后负荷曲线的变化情况,证明了该方法能够显著改善电网负荷分布。 适合人群:从事智能电网、能源管理系统开发的研究人员和技术人员,尤其是那些希望深入了解电动汽车参与电网调度的技术细节的人。 使用场景及目标:适用于需要解决电网负荷不均衡问题的实际应用场景,旨在通过合理的电动汽车调度策略,在保障用户利益的同时提高整个电力系统的运行效率和稳定性。 其他说明:文中提到的方法可以作为研究和开发类似系统的参考案例,同时也强调了在实际应用中需要根据具体情况进行调整和优化。
内容概要:本文详细介绍了三菱FX5U PLC的加密与授权管理系统,主要基于ST(Structured Text)语言实现。文中展示了多个关键模块的设计思路和技术细节,如授权验证逻辑、时间预警机制、通信检测、程序块加密以及硬件抽象层设计。授权验证逻辑通过预存密钥数组进行分期授权和永久授权的管理;时间预警机制利用定时器和系统时钟确保授权到期前提醒用户;通信检测采用心跳包机制保障通信安全;程序块加密通过预处理器指令和无效代码混淆保护源代码;硬件抽象层设计使得系统能够适应不同型号的PLC。此外,还讨论了移植性和客户破解防范措施。 适合人群:从事工业控制系统开发的技术人员,尤其是对PLC编程和系统安全有兴趣的专业人士。 使用场景及目标:适用于需要对PLC系统进行高级加密和授权管理的工程项目,旨在提高系统的安全性和可控性,防止未经授权的操作和访问。 其他说明:本文不仅提供了具体的代码实现,还深入探讨了设计理念和技术难点,帮助读者全面理解并应用于实际项目中。
1. 开启root登录 2.启用PAM 3.关闭selinux 4.操作以下内容 cat > /etc/pam.d/sshd << EOF #%PAM-1.0 auth required pam_sepermit.so auth include password-auth account required pam_nologin.so account include password-auth password include password-auth session required pam_limits.so session required pam_selinux.so close session required pam_loginuid.so session optional pam_keyinit.so force revoke session include password-auth EOF
Delphi 12.3控件之Pascal Expert 9.12.14 For Delphi 12 [CS].7z
内容概要:本文全面介绍了层次聚类算法,一种无监督学习方法,广泛应用于数据挖掘、机器学习和模式识别。文章首先阐述了聚类算法的基础理论,特别是层次聚类的独特之处——生成树状图展示数据点之间的相似性关系。随后,详细讲解了凝聚型层次聚类的工作原理,包括初始化、合并和重复步骤,并探讨了多种距离度量方法(如单链、完全链、平均链和重心法)。文中通过Python代码实例展示了如何使用SciPy库进行层次聚类,并生成树状图。此外,文章还讨论了层次聚类在生物信息学、图像分割和文本数据分析中的具体应用场景,以及评估聚类质量的方法(如轮廓系数和Calinski-Harabasz指数)。最后,文章总结了层次聚类的优点和缺点,并展望了未来的优化方向和挑战。 适合人群:数据科学家、机器学习工程师、研究人员和其他对聚类算法感兴趣的从业者。 使用场景及目标:①理解层次聚类的基本原理和工作流程;②掌握如何使用Python实现层次聚类;③学会评估聚类质量和优化聚类性能;④了解层次聚类在不同领域的应用。 其他说明:本文不仅提供了理论知识,还包括丰富的代码示例,使读者能够在实践中加深对层次聚类的理解。同时,文章还探讨了层次聚类
内容概要:本文深入探讨了一款30kW三相PFC充电桩的技术细节,涵盖硬件设计、主控芯片选择、关键算法实现及其性能表现。文中详细介绍了主电路采用的T型三相维也纳结构,主控芯片TMS320F28069的特点及其应用,包括CLA协处理器的优势。同时,对锁相环算法、PWM控制、中点平衡控制等进行了详细的解释,并提供了相应的代码示例。此外,还分享了一些实战经验和注意事项,如DQ变换的正确实现、滞回控制的应用技巧等。最后,附上了27页的量产测试报告,展示了该程序在实际生产环境中的稳定性和可靠性。 适合人群:从事电力电子、嵌入式系统开发的专业人士,尤其是对大功率充电设备感兴趣的工程师和技术爱好者。 使用场景及目标:适用于希望深入了解30kW三相PFC充电桩内部工作机制的研发人员。通过学习本文,可以掌握从硬件设计到软件实现的完整流程,为类似项目提供宝贵的参考资料和技术支持。 其他说明:本文不仅提供了理论知识,还结合了大量的实战经验,帮助读者避免常见陷阱,提高开发效率。
Java项目基于ssm框架的课程设计,包含LW+ppt
基于YOLOv8深度学习的布料外观缺陷检测系统(带GUI界面)(Python源码+Pyqt5界面+2100多张标注好的数据集+安装使用教程+训练好的模型+评估指标曲线+演示图片视频),开箱即用 高准确率,6个类别:带沙,断沙,面球,破洞,脱沙,污渍。 1、该资源内项目代码都经过本人训练测试并运行成功,功能都OK的情况下才上传的,请放心下载使用!有问题请私信我,提供技术支持。 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也适合小白学习进阶,当然也可作为毕设项目、课程设计、日常作业、实战项目演示等。 3、可参考学习,也可在此基础上略做修改,以实现其他功能,也可直接用于毕设、课设和作业等。 升级版YOLO11资源详情展示:https://blog.csdn.net/m0_37302966/article/details/146467358 更多资源下载:https://blog.csdn.net/m0_37302966/article/details/146387003
远程连接工具
9月最新开心彩虹易码支付最新版转账付款功能,支持通过插件扩展 1. 新增微信公众号消息提醒功能 2. 重构转账付款功能,支持通过插件扩展 3. 商户后台新增代付功能 4. 后台新增付款记录列表 5. 支付宝插件新增预授权支付 6. 优化支付通道列表,支持翻页与快速复制通道 7. 新增创建订单人机验证,防止被恶意创建订单 8. 修复其他一些已知问题 9. 增加两套前端模板以及订单查询 安装教程:上传源码,访问即可安装。
内容概要:本文详细介绍了如何利用正余弦算法(SCA)优化广义回归神经网络(GRNN)进行数据回归预测的方法。首先解释了GRNN的基本结构及其平滑因子σ的重要性,然后展示了SCA通过正弦和余弦震荡寻找全局最优σ值的具体实现步骤。文中提供了完整的MATLAB代码片段,涵盖数据预处理、适应度函数设计、SCA参数更新规则以及最终模型评估等多个方面。实验结果显示,经过SCA优化后的GRNN在多个测试案例中均表现出更高的预测精度,特别是在处理中小型数据集时优势明显。 适合人群:具有一定MATLAB编程经验的数据分析师、机器学习爱好者及研究人员。 使用场景及目标:适用于需要提高数据回归预测准确性的小型至中型数据集处理任务。主要目标是通过自动化参数寻优减少人工干预,从而获得更加精确稳定的预测结果。 其他说明:作者强调了数据归一化的必要性和SCA参数配置的最佳实践,如适当设定搜索范围、种群大小和迭代次数等。此外还提到可以通过引入交叉验证等方式进一步增强模型鲁棒性。
MainActivity.kt
Delphi 12.3控件之ReportBuilder v22.04 for D10.2-D12.7z
芯原微电子笔试试卷真题,IC工程师笔试真题,自己下载。
数据集相关资源,AFQMC 蚂蚁金融语义相似度数据集,AFQMC(Ant Financial Question Matching Corpus)蚂蚁金融语义相似度数据集,用于问题相似度计算。
TIDBPTCA真题.html
内容概要:本文深入探讨了利用COMSOL Multiphysics软件中的等离子体模块建立针-针电极空气流注放电模型的方法。文中详细介绍了模型的几何结构设定、物理场配置(如电子、正负离子的载流子选择)、化学反应的设置(含21组带电粒子反应)以及Helmholtz光电离过程的具体实现方法。此外,还提供了关于求解器配置、边界条件处理等方面的实用技巧,确保模型能够稳定且高效地运行。通过该模型可以直观地观察到空气流注放电过程中的电场分布、粒子密度变化等情况。 适合人群:从事等离子体物理研究的专业人士,特别是那些对高压放电现象感兴趣的科研工作者和技术人员。 使用场景及目标:适用于研究等离子体行为及其在不同条件下的演化规律,特别是在针-针电极间的空气流注放电特性方面。该模型可用于验证理论预测、探索新型放电器件的设计思路,以及优化现有设备的工作性能。 其他说明:文中不仅提供了详细的建模步骤,还包括了一些实际操作中的注意事项和优化建议,有助于提高仿真的成功率并减少计算成本。同时,作者鼓励读者尝试调整模型参数以获得不同的仿真效果,从而进一步加深对该领域的理解。
内容概要:本文详细介绍了Java开发中的四个关键技术领域:JUnit单元测试、Java日志框架、Maven项目管理和Tomcat服务器。首先,阐述了JUnit的基本概念、核心功能及其在提高代码质量和开发效率中的重要作用。接着,讲解了几种主流的日志框架(如Log4j、Log4j2、Logback和SLF4J)的特点和使用方法。随后,深入探讨了Maven的功能特点、依赖管理和项目结构,强调了其在简化项目构建和管理方面的优势。最后,介绍了Tomcat服务器的工作原理、特点及其在Java Web应用中的应用。 适合人群:具备一定Java编程基础的研发人员,尤其是希望深入了解Java开发工具链的专业人士。 使用场景及目标:① 学习如何使用JUnit进行高效的单元测试;② 掌握常见Java日志框架的配置和使用;③ 理解Maven的依赖管理和项目管理机制;④ 掌握Tomcat服务器的配置和使用,搭建Java Web应用。 其他说明:本文不仅涵盖了理论知识,还包括了大量的实践案例和代码示例,有助于读者更好地理解和应用这些技术。
“传统节日”宣传介绍教学课件
内容概要:本文详细介绍了一套用于三菱Q系列PLC的伺服定位控制功能块(FB)程序的设计与实现方法。该程序采用结构化文本(Structured Text)编写,分为参数配置、运动控制和状态监控三大模块。文中强调了程序的结构清晰、注释详尽以及实战经验的应用,确保系统的稳定性和易维护性。具体亮点包括:轴参数配置结构体、运动控制逻辑的状态检测、异常处理机制、速度参数的安全处理、位置窗口比较法的位置到达判定、详细的报警处理模块等。此外,作者还分享了许多实际应用中的经验和技巧,如加减速曲线的选择、软极限保护、伺服使能控制等。 适合人群:从事工业自动化控制领域的工程师和技术人员,尤其是对三菱Q系列PLC和伺服控制系统有一定了解并希望提升编程技能的人群。 使用场景及目标:适用于需要编写高效稳定的三菱Q系列PLC伺服定位控制程序的场合。目标是帮助工程师掌握如何设计结构清晰、易于维护且性能可靠的伺服控制程序,减少调试时间和提高系统稳定性。 其他说明:本文不仅提供了具体的编程指导,还分享了大量来自生产一线的实际经验,有助于读者更好地理解和应对实际工作中可能出现的问题。