首页 > 其他分享 >k8s教程(22)-pod调度总结

k8s教程(22)-pod调度总结

时间:2022-12-31 11:34:13浏览次数:45  
标签:Node 22 调度 Taint Pod Job pod k8s 节点


文章目录

  • ​​01 概述​​
  • ​​1.1 Pod调度控制器分类​​
  • ​​1.2 RC到Deployment的发展​​
  • ​​1.2.1 ReplicaSet​​
  • ​​1.3 Pod调度​​
  • ​​1.3.1 情景​​
  • ​​1.3.2 存在的问题​​
  • ​​1.3.3 解决方式​​
  • ​​02 全自动调度​​
  • ​​2.1 功能​​
  • ​​2.2 举例​​
  • ​​03 定向调度​​
  • ​​3.1 Step1- 给Node打上标签​​
  • ​​3.2 Step2- Pod指定NodeSelector​​
  • ​​3.3 Step3- 验证​​
  • ​​3.4 预定义的标签​​
  • ​​04 node亲和性调度​​
  • ​​4.1 亲和性调度分类​​
  • ​​4.2 举例​​
  • ​​4.3 注意事项​​
  • ​​05 亲和性与互斥性调度​​
  • ​​5.1 拓扑域​​
  • ​​5.2 举例​​
  • ​​5.2.1 参照目标pod​​
  • ​​5.2.2 pod的亲和性调度​​
  • ​​5.2.3 pod的互斥性调度​​
  • ​​5.4 其它​​
  • ​​06 污点与容忍​​
  • ​​6.1.1 污点与容忍设置​​
  • ​​6.1.1.1 Node设置污点​​
  • ​​6.1.1.2 Pod声明容忍​​
  • ​​6.1.1.3 小结​​
  • ​​6.1.2 特殊情况​​
  • ​​6.2 应用场景​​
  • ​​6.2.1 独占节点​​
  • ​​6.2.2 具有特殊硬件设备的节点​​
  • ​​6.2.3 定义Pod驱逐行为,以应对节点故障​​
  • ​​07 优先级调度​​
  • ​​7.1 案例​​
  • ​​7.1.1 创建PriorityClass​​
  • ​​7.1.2 Pod声明优先级类别​​
  • ​​7.1.3 注意事项​​
  • ​​08 DaemonSet(每个node上只调度一个pod)​​
  • ​​8.1 DaemonSet​​
  • ​​8.1.1 应用场景​​
  • ​​8.1.2 举例​​
  • ​​8.1.3 注意事项​​
  • ​​09 批处理调度​​
  • ​​9.1 批处理调度​​
  • ​​9.1.1 任务模式分类​​
  • ​​9.1.1.1 按实现方式分类​​
  • ​​9.1.1.2 按批处理并行分类​​
  • ​​9.1.2 案例​​
  • ​​9.1.2.1 Job Template Expansion案例​​
  • ​​9.1.2.2 Queue with Pod Per Work Item案例​​
  • ​​9.1.2.3 Queue with Variable Pod Count案例​​
  • ​​10 定时任务​​
  • ​​10.1 基本语法​​
  • ​​10.2 案例​​
  • ​​11 容灾调度​​
  • ​​11.1 如何实现?​​
  • ​​11.2 举例​​

01 概述

1.1 Pod调度控制器分类

Kubernetes平台上,我们很少会直接创建一个​​Pod​​,在大多数情况下会通过如下控制器完成对一组Pod副本的创建、调度 及全生命周期的自动控制任务

  • RC
  • Deployment
  • DaemonSet
  • Job等

1.2 RC到Deployment的发展

在最早的Kubernetes,版本里是没有这么多​​Pod​​​副本控制器的,只有一个​​Pod​​副本控制器RC(Replication Controller),这个控制器是这样设计实现的:

RC独立于所控制的Pod,并通过Label标签这个松耦合关联关系控制目标Pod实例的创建和销毁

随着Kubernetes的发展,​​RC​​​也出现了新的继任者-​​Deployment​​,用于更加自动地完成Pod副本的部署、版本更新、回滚等功能

严谨地说,​​RC​​​的继任者其实并不是​​Deployment​​,而是ReplicaSet,因为

ReplicaSet进一步增强了​​RC​​标签选择器的灵活性(之前RC的标签选择器只能选择一个标签而ReplicaSet拥有集合式的标签选择器,可以选择多个Pod标签),如下所示:

k8s教程(22)-pod调度总结_容器化技术

1.2.1 ReplicaSet

RC不同,ReplicaSet被设计成能控制多个不同标签的Pod副本

举例: 应用​​MyApp​​​目前发布了​​v1​​​与​​v2​​​两个版本,用户希望​​MyApp​​​的​​Pod​​​副本数保持为3个,可以同时包含​​v1​​​和​​v2​​​版本的​​Pod​​,就可以用ReplicaSet来实现这种控制,写法如下:

k8s教程(22)-pod调度总结_kubernetes_02


其实,​​Kubernetes​​​的滚动升级就是巧妙运用​​ReplicaSet​​​的这个特性来实现的, 同时,​​Deployment​​​也是通过​​ReplicaSet​​​来实现​​Pod​​副本自动控制功能的。

我们不应该直接使用底层的ReplicaSet来控制Pod副本,而应该通过管理ReplicaSet的Deployment对象来控制副本,这是来自官方的建议。

1.3 Pod调度

在大多数情况下,我们希望​​Deployment​​​创建的​​Pod​​副本被成功调度到集群中的任何一个可用节点,而不关心具体会调度到哪个节点。

1.3.1 情景

但是,在真实的生产环境中的确也存在一种需求:希望某种​​Pod​​的副本全部在指定的一个或者一些节点上运行,比如希望将MySQL数据库调度到一个具有​SSD​磁盘的目标节点上

此时​​Pod​​模板中的NodeSelector属性就开始发挥作用了,上述MySQL定向调度案例的实现方式可分为以下两步:

  1. 把具有SSD磁盘的Node都打上自定义标签disk=ssd
  2. 在Pod模板中设定NodeSelector的值为“disk:ssd”

如此一来,​​Kubernetes​​​在调度​​Pod​​​副本的时候,就会先按照​​Node​​的标签过滤出合适的目标节点,然后选择一个最佳节点进行调度。

1.3.2 存在的问题

上述逻辑看起来既简单又完美,但在真实的生产环境中可能面临以下令人尴尬的问题:

  1. 如果​​NodeSelector​​​选择的​​Label​​不存在或者不符合条件(比如:这些目标节点此时宕机或者资源不足,该怎么办?)
  2. 如果要选择多种合适的目标节点,比如​​SSD​​磁盘的节点或者超高速硬盘的节点,该怎么办?

备注:​​Kubernetes​​​引入了​​NodeAffinity​​(节点亲和性设置)来解决该需求。

1.3.3 解决方式

在真实的生产环境中还存在如下所述的特殊需求:

需求

举例描述

解决方式

不同Pod​之间的亲和性(​Affinity​)

比如MySQL数据库与Redis中间件 不能被调度到同一个目标节点上,或者两种不同的Pod必须被调度到同一个Node 上,以实现本地文件共享或本地网络通信等特殊需求

PodAffinity来解决该问题

有状态集群的调度

对于ZooKeeper、Elasticsearch、MongoDB、Kafka等有状态集群,虽然集群中的每个Worker节点看起来都是相同的,但每个Worker节点都必须有明确的、不变的唯一ID(主机名或IP地址),这些节点的启动和停止次序通常有严格的顺序。此外,由于集群需要持久化保存状态数据所以集群中的Worker节点对应的Pod不管在哪个Node上恢复,都需要挂载原来的Volume,因此这些Pod还需要捆绑具体的PV

针对这种复杂的需求,​​Kubernetes ​​提供了StatefulSet这种特殊的副本控制器来解决问题,在Kubernetes1.9版本发布后,StatefulSet才可用于正式生产环境中

在每个Node上调度并且仅仅创建一个Pod副本

这种调度通常用于系统监控相关的Pod,比如主机上的日志采集、主机性能采集等进程需要被部署到集群中的每个节点,并且只能部署一个副本

DaemonSet来解决这种特殊Pod副本控制器

对于批处理作业,需要创建多个Pod副本来协同工作,当这些Pod副本都完成自己的任务时,整个批处理作业就结束了

这种Pod运行且仅运行一次的特殊调度,用常规的RC或者Deployment都无法解决

引入了新的Pod调度控制器Job来解决问题,并继续延伸了定时作业的调度控制器CronJob

与单独的Pod实例不同,由RC、ReplicaSet、Deployment、DaemonSet等控制器创建的Pod副本实例都是归属于这些控制器的,这就产生了一个问题:控制器被删除后,归属于控制器的Pod副本该何去何从?

kubernetes版本

操作

1.9之前

在RC等对象被删除后,它们所创建的Pod副本都不会被删除

1.9以后

这些Pod副本会被一并删除。如果不希望这样做,则可以通过kubectl命令的- cascade=false参数(例如:​​kubectl delete replicaset my-repset --cascade=false​​)来取消这一默认特性

02 全自动调度

2.1 功能

Deployment或RC的主要功能之一就是自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群内始终维持用户指定的副本数量,。

2.2 举例

举例:使用配置文件可以创建一个​​ReplicaSet​​​,这个​​ReplicaSet​​​会创建3个​​Nginx​​​应用的​​Pod​​:

k8s教程(22)-pod调度总结_Pod_03

使用​​create​​​命令创建之后,查看​​Deployment​​的状态:

kubectl get deployments

k8s教程(22)-pod调度总结_互斥_04

该状态说明​​Deployment​​已创建好所有3个副本,并且所有副本都是最新的可用的。


通过运行​​kubectl get rs​​​和​​kubectl get pods​​​可以查看已创建的​​ReplicaSet (RS)​​​和​​Pod​​的信息。

k8s教程(22)-pod调度总结_互斥_05

从调度策略上来说,这3个​​Nginx Pod​​由系统全自动完成调度。它们各自最终运行在哪个节点上,完全由Master的Scheduler经过一系列算法计算得出,用户无法干预调度过程和结果。

03 定向调度

3.1 Step1- 给Node打上标签

如果要实现定向调度,首先的第一步就是要为Node节点搭上标签(Label),可以使用​​kubectl label​​命令:

kubectl label nodes <node-name><label-key>=<label-value>

例如这里为​​k8s-node-1​​​节点打上一个​​zone=north​​标签,表明它是“北方”的一个节点:

k8s教程(22)-pod调度总结_kubernetes_06

3.2 Step2- Pod指定NodeSelector

然后,在​​Pod​​​的定义中加上​​nodeSelector​​​的设置,以​​redis-master- controller.yaml​​为例:

k8s教程(22)-pod调度总结_互斥_07

3.3 Step3- 验证

运行​​kubectl create -f​​​命令创建​​Pod​​​,​​scheduler​​​就会将该​​Pod​​调度到拥有 “zone=north” 标签的​​Node​​上。

使用​​kubectl get pods-o wide​​​命令可以验证​​Pod​​​所在的​​Node​​:

k8s教程(22)-pod调度总结_云原生_08

需要注意的是,如果我们指定了​​Pod​​​的​​nodeSelector​​​条件,且在集群中不存在包含相应标签的​​Node​​​,则即使在集群中还有其他可供使用的​​Node​​,这个Pod也无法被成功调度

3.4 预定义的标签

除了用户可以自行给​​Node​​​添加标签,​​Kubernetes​​​也会给​​Node​​预定义一些标签,包括:

  • kubernetes.io/hostname;
  • beta.kubernetes.io/os(从1.14版本开始更新为稳定版,到1.18版本删除);
  • beta.kubernetes.io/arch(从1.14版本开始更新为稳定版,到1.18版本删除);
  • kubernetes.io/os(从1.14版本开始启用);
  • kubernetes.io/arch(从1.14版本开始启用)。

04 node亲和性调度

4.1 亲和性调度分类

目前有两种节点亲和性表达:

表达式

含义

​RequiredDuringSchedulingIgnoredDuringExecution​

必须满足指定的规则才可以调度Pod到Node上(功能与nodeSelector很像,但是使用的是不同的语法),相当于限制

​PreferredDuringSchedulingIgnoredDuringExecution​

强调优先满足指定规则,调度器会尝试调度Pod到Node上,但并不强求,相当于软限制

多个优先级规则还可以设置权重(weight)值,以定义执行的先后顺序。

IgnoredDuringExecution的意思是:如果一个​​Pod​​​所在的节点在​​Pod​​​运行期间 标签发生了变更,不再符合该​​Pod​​的节点亲和性需求,则系统将忽略Node​Label ​的变化,该​Pod​能继续在该节点上运行

4.2 举例

有如下要求:

  • requiredDuringSchedulingIgnoredDuringExecution:要求只运行在amd64的节点上(beta.kubernetes.io/arch In amd64);
  • preferredDuringSchedulingIgnoredDuringExecution:要求尽量运行在磁盘类型为ssd(disk-type In ssd)的节点上;

则资源文件的定义如下:

apiVersion:vl
kind:Pod
metadata:
name:with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms
- matchExpressions:
- key:beta.kubernetes.io/arch
operator:In
values:
- amd64
preferredDuringSchedulingIgnoredDuringExecution:
- weight:1
preference:
matchExpressions:
- key:disk-type
operator:In
values:
- ssd
containers:
- name:with-node-affinity
image:gcr.io/google containers/pause:2.0

从上面的配置中可以看到In操作符,​​NodeAffinity​​​语法支持的操作符包括​​In、NotIn、Exists、DoesNotExist、Gt、Lt​​​。虽然没有节点排斥功能,但是用​​NotIn​​​ 和​​DoesNotExist​​就可以实现排斥的功能了。

4.3 注意事项

  • 如果同时定义了​​nodeSelector​​​和​​nodeAffinity​​​,那么必须两个条件都得到满足,​​Pod​​才能最终运行在指定的Node上;
  • 如果​​nodeAffinity​​​指定了多个​​nodeSelectorTerms​​,那么其中一个能匹配成功即可;
  • 如果在​​nodeSelectorTerms​​​中有多个​​matchExpressions​​​,则一个节点必须满足所有​​matchExpressions​​才能运行该Pod。

05 亲和性与互斥性调度

亲和性与互斥性可以理解为就是相关联的两种或多种Pod是否可以在同一个拓扑域中共存或者互斥

那么什么是拓扑域?

5.1 拓扑域

拓扑域的概念:

  • 一个拓扑域由一些​​Node​​​节点组成,这些​​Node​​节点通常有相同的地理空间坐标,比如在同一个机架、机房或地区;
  • 一般用​​region​​​表示机架、 机房等的拓扑区域,用​​Zone​​表示地区这样跨度更大的拓扑区域;
  • 极端情况下, 我们也可以认为一个Node就是一个拓扑区域。

​k8s​​内置了如下一些常用的默认拓扑域,主要是为了确定各个节点所属的拓扑域

默认拓扑域

描述

kubernetes.io/hostname

在Node节点初始化时,controller–manager会为Node打上该标签

topology.kubernetes.io/region

公有云厂商提供的Kubernetes服务或者使用cloud-controller-manager创建的集群,会给Node打上该标签

topology.kubernetes.io/zone

同上

5.2 举例

​Pod​​​亲和与互斥的调度是通过在​​Pod​​的定义上增加topologyKey 属性来声明对应的目标拓扑区域内几种相关联的​​Pod​​要 “在一起或不在一起”。

与节点亲和相同,Pod亲和与互斥的条件设置也是​​requiredDuringSchedulingIgnoredDuringExecution​​​和
​​​preferredDuringSchedulingIgnoredDuringExecution​​:

  • Pod的亲和性被定义于PodSpec的affinity字段的podAffinity子字段中;
  • Pod间的互斥性则被定义于同一层次的podAntiAffinity子字段中.

下面通过实例来说明Pod间的亲和性和互斥性策略设置。

5.2.1 参照目标pod

首先,创建一个名为​​pod-flag​​​的​​Pod​​​,带有标签​​security=S1​​​和​​app=nginx​​​,后面的例子将使用​​pod-flag​​​作为​​Pod​​​亲和与互斥的目标​​Pod​​:

apiversion:v1
kind:Pod
metadata:
name:pod-flag
labels:
security:"S1"
app:"nginx"
spec:
containers:
-name:nginx
image:nginx

5.2.2 pod的亲和性调度

下面创建第2个Pod来说明Pod的亲和性调度,这里定义的亲和标签是 “security=S1”,对应上面的Pod “pod-flag”,topologyKey的值被设置为 “kubernetes.io/hostname“:

apiVersion:vl
kind:Pod
metadata:
name:pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key:security
operator:In
values:
-S1
topologyKey:kubernetes.io/hostname
containers:
- name:with-pod-affinity
image:gcr.io/google_containers/pause:2.0

创建​​Pod​​​之后,使用​​kubectl get pods -o wide​​​命令可以看到,这两个​​Pod​​​在同
一个​​​Node​​上运行。

在创建这个​​Pod​​​之前,删掉这个节点的 ​​kubernetes.io/hostname​​​ 标签,重复上面的创建步骤,将会发现​​Pod​​​一直处于​​Pending​​​状态,这是因为找不到满足条件的​​Node​​了。

5.2.3 pod的互斥性调度

创建第3个​​Pod​​​,我们希望它不与目标​​Pod​​​运行在同一个​​Node​​上:

apiversion:v1
kind:Pod
metadata:
name:anti-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key:security
operator:In
values:
-S1
topologyKey:topology.kubernetes.io/zone
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key:app
operator:In
values:
-nginx
topologyKey:kubernetes.io/hostname
containers:
- name:anti-affinity
image:gcr.io/google_containers/pause:2.0

这里要求这个新​​Pod​​​与​​security=S1​​​的Pod为同一个​​zone​​​,但是不与​​app=nginx​​​ 的​​Pod​​​为同一个​​Node​​。

创建​​Pod​​​之后,同样用​​kubectl get pods -o wide​​​来查看,会看到新的​​Pod​​​被调度到了同一​​Zone​​​内的不同​​Node​​上。

5.4 其它

与节点亲和性类似,​​Pod​​​亲和性的操作符也包括​​In、NotIn、Exists、 DoesNotExist、Gt、Lt​​。

原则上,​​topologyKey​​​可以使用任意合法的标签​​Key​​​赋值,但是出于性能和安全方面的考虑,对​​topologyKey​​有如下限制:

  • 在Pod亲和性和RequiredDuringScheduling的Pod互斥性的定义中,不允许使用空的topologyKey
  • 如果Admission controller包含了LimitPodHardAntiAffinityTopology,那么针对Required DuringScheduling的Pod互斥性定义就被限制为kubernetes.io/hostname,要使用自定义的topologyKey,就要改写或禁用该控制器。
  • 在PreferredDuringScheduling类型的Pod互斥性定义中,空的
    topologyKey会被解释为kubernetes.io/hostname、failure-
    domain.beta.kubernetes.io/zone 及 failure-domain.beta.kubernetes.io/region的组合
  • 如果不是上述情况,就可以采用任意合法的topologyKey了。

PodAffinity规则设置的注意事项如下:

  • 除了设置Label Selector和topologyKey,用户还可以指定Namespace列表
    进行限制。同样,使用Label Selector对Namespace进行选择,Namespace的定义 和Label Selector及topologyKey同级,省略Namespace的设置,表示使用定义了 affinity/anti-affinity的Pod所在的命名空间。如果Namespace被设置为空值 (“”),则表示所有命名空间.
  • 在所有关联requiredDuringSchedulingIgnoredDuringExecution的
    matchExpressions 全都满足之后 ,系统才能将Pod调度到某个Node上。

06 污点与容忍

Taint(污点) 则正好相反,它让​​Node​​​拒绝​​Pod​​的运行。简单地说,被标记为Taint的节点就是存在问题的节点,比 如磁盘要满、资源不足、存在安全隐患要进行升级维护,希望新的​​Pod​​不会被调度过来。

但被标记为​​Taint​​​的节点并非故障节点,仍是有效的工作节点,所以仍需将某些​​Pod​​​调度到这些节点上时,可以通过使用​​Toleration​​属性来实现。

6.1.1 污点与容忍设置

在默认情况下,在​​Node​​​上设置一个或多个​​Taint​​​之后,除非​​Pod​​​明确声明能够容忍这些污点,否则无法在这些​​Node​​上运行。

6.1.1.1 Node设置污点

可以用​​kubectl taint​​​命令为​​Node​​​设置​​Taint​​信息:

kubectl taint nodes node1 key=value:NoSchedule

描述:这个设置为​​node1​​​加上了一个​​Taint​​​,该​​Taint​​​的键为​​key​​​,值为​​value​​​,​​Taint​​​的效果是​​NoSchedule​​,这意味着除非Pod明确声明可以容忍这个​Taint​,否则不会被调度到​node1​

6.1.1.2 Pod声明容忍

在​​Pod​​​上声明容忍的例子如下,下面的两个​​Toleration​​​都被设置为可以容忍(​​Tolerate​​​)具有该​​Taint​​​的​​Node​​​,使得​​Pod​​​能够被调度到​​node1​​上:

tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"

或者

tolerations:
- key: "key"
operator: "Exists"
effect: "NoSchedule"

6.1.1.3 小结

PodToleration声明中的​​key​​​和​​effect​​​需要与​​Taint​​的设置保持一致,并且满足以下条件之一:


条件

key

空的​​key​​​配合​​Exists​​操作符能够匹配所有键和值

operator

值是​​Exists​​​(无须指定​​value​​​),​​ operator​​​的值是​​Equal​​​并且​​value​​​相等, 如果不指定​​operator​​​,则默认值为​​Equal ​

effect

空的​​effect​​​匹配所有​​effect​​​,在上面的例子中,​​effect​​​的取值为​​NoSchedule​​​,还可以取值为​​PreferNoSchedule​​​,这个值的意思是优先,也可以算作​​NoSchedule​​​的软限制版本 - 一个​​Pod​​​如果没有声明容忍这个​​Taint​​​,则系统会尽量避免把这个​​Pod​​调度到这一 节点上,但不是强制的

系统允许在同一个Node上设置多个Taint,也可以在Pod上设置多个Toleration

Kubernetes调度器处理多个Taint和Toleration的逻辑顺序为:首先列出节点中所有的Taint,然后忽略Pod的Toleration能够匹配的部分,剩下的没被忽略的Taint就是对Pod的效果了

6.1.2 特殊情况

下面是几种特殊情况:

  • 如果在剩余的​​Taint​​​中存在​​effect=NoSchedule​​​,则调度器不会把该​​Pod​​调度到这一节点上;
  • 如果在剩余的​​Taint​​​中没有​​NoSchedule​​​效果,但是有​​PreferNoSchedule​​​效果,则调度器会尝试不把这个​​Pod​​指派给这个节点;
  • 如果在剩余的​​Taint​​​中有​​NoExecute​​​效果,并且这个​​Pod​​已经在该节点上运行,则会被驱逐;
  • 如果没有在该节点上运行,则也不会再被调度到该节点上。

例如,我们这样对一个节点进行Taint设置:

kubectl taint nodes node1 keyl=valuel:NoSchedule 
kubectl taint nodes node1 keyl=valuel:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

然后在​​Pod​​​上设置两个​​Toleration​​:

tolerations:
- key: "key1"
operator: "Equal"
value: "valuel"
effect: "NoSchedule"
- key: "key1"
operator: "Equal"
value: "valuel"
effect: "NoExecute"

结果:

  • 这样的结果是该​​Pod​​​无法被调度到​​node1​​​上,这是因为第3个​​Taint​​​没有匹配的​​Toleration​​。
  • 但是如果该​​Pod​​​已经在​​node1​​​上运行了,那么在运行时设置第3个​​Taint​​​,它还能继续在​​node1​​​上运行,这是因为​​Pod​​可以容忍前两个Taint。

一般来说,如果给​​Node​​​加上​​effect=NoExecute​​​的​​Taint​​​,那么在该​​Node​​​上正在运行的所有无对应​​Toleration​​​的​​Pod​​​都会被立刻驱逐,而具有相应​​Toleration​​​的​​Pod​​​永远不会被驱逐。不过,系统允许给具有​​NoExecute​​​效果的​​Toleration​​​加入一 个可选​​tolerationSeconds​​​“字段,这个设置表明​​Pod​​​可以在​​Taint​​​添加到​​Node​​​之后还能在这个​​Node​​​上运行多久(单位为​​s​​):

tolerations:
- key: "key1"
operator: "Equal"
value: "valuel"
effect: "NoExecute"
tolerationSeconds: 3600

上述定义的意思是,如果Pod正在运行,所在节点都被加入一个匹配的​Taint​,则这个​Pod​会持续在这个节点上存活​3600s​后被逐出。如果在这个宽限期内​Taint​被移除,则不会触发驱逐事件

6.2 应用场景

TaintToleration一种处理节点并且让Pod进行规避或者驱逐Pod的弹性处理方式,下面列举一些常见的用例。

6.2.1 独占节点

如果想要拿出一部分节点专门给一些特定应用使用,则可以为节点添加这样​​Taint​​:

kubectl taint nodes nodename dedicated=groupName:NoSchedule

然后给这些应用的​Pod​​加入对应的​​Toleration​​​,这样,带有合适​​Toleration​​​的​​Pod​​​就会被允许同使用其他节点一样使用有​​Taint​​的节点

通过自定义​​Admission Controller​​​也可以实现这一目标。如果希望让这些应用独占一批节点,并且确保它们只能使用这些节点,则还可以给这些​​Taint​​​节点加入类似的标签​​dedicated=groupName​​​,然后​​Admission Controller​​​需要加入节点亲和 性设置,要求​​Pod​​只会被调度到具有这一标签的节点上。

6.2.2 具有特殊硬件设备的节点

在集群里可能有一小部分节点安装了特殊的硬件设备(如​​GPU​​芯片),用户自然会希望把不需要占用这类硬件的Pod排除在外,以确保对这类硬件有需求的​Pod​能够被顺利调度到这些节点上

可以用下面的命令为节点设置​​Taint​​:

kubectl taint nodes nodename special=true:NoSchedule 
kubectl taint nodes nodename special=true:PreferNoSchedule

然后在​​Pod​​​中利用对应的​​Toleration​​​来保障特定的​​Pod​​能够使用特定的硬件。

和上面独占节点的示例类似,使用​​Admission Controller​​来完成这一任务会更方便,例如:

  • Admission Controller使用Pod的一些特征来判断这些Pod,如果可以使用这些件,就添加Toleration来完成这一工作;
  • 要保障需要使用特殊硬件的Pod只被调度到安装这些硬件的节点上,则还需要一些额外的工作,比如将这些特殊资源使用opaque-int-resource的方式对自定义资源进行量化,然后在PodSpec中进行请求;
  • 也可以使用标签的方式来标注这些安装有特别硬件的节点,然后在Pod 中定义节点亲和性来实现这个目标。

6.2.3 定义Pod驱逐行为,以应对节点故障

前面提到的​​NoExecute​​​这个​​Taint​​​效果对节点上正在运行的​​Pod​​有以下影响:

  • 没有设置Toleration的Pod会被立刻驱逐;
  • 配置了对应Toleration的Pod,如果没有为tolerationSeconds赋值,则会一直留在这一节点中;
  • 配置了对应Toleration的Pod且指定了tolerationSeconds值,则会在指定的时间后驱逐(注意,在节点发生故障的情况下,系统将会以限速(rte- limiting)模式逐步给Node设置Taint,这样就能避免在一些特定情况下(比如
    Master暂时失联)有大量的Pod被驱逐)。

注意,Kubernetes会自动给Pod添加下面几种Toleration:

  • key为node.kubernetes.io/not-ready,并配置tolerationSeconds=300;
  • key 为node.kubernetes.io/unreachable,并配置tolerationSeconds=300。

以上添加的这种自动机制保证了在某些节点发生一些临时性问题时,Pod默认能够继续停留在当前节点运行5min等待节点恢复,而不是立即被驱逐,从而避免系统的异常波动。

另外,Kubernetes从1.6版本开始引入两个与Taint相关的新特性,TaintNodesByCondition及TaintBasedEvictions用来改善异常情况下的Pod调度与驱逐问题,比如在节点内存吃紧、节点磁盘空间已满、节点失联等情况下,是 否自动驱逐某些Pod或者暂时保留这些Pod等待节点恢复正常。这个过程的完整逻 辑基本如下。

  1. 不断地检查所有Node状态,设置对应的Condition;
  2. 不断地根据Node Condition设置对应的Taint;
  3. 不断地根据Taint驱逐Node上的Pod。

其中,检查​​Node​​​的状态并设置​​Node​​​的​​Taint​​​就是​​TaintNodesByCondition​​特性,即在Node满足某些特定的条件时,自动为Node节点添加Taint,目前主要有以下几种条件:

条件

描述

node.kubernetes.io/not-ready:节点未就绪

对应NodeCondition Ready为False的情况

node.kubernetes.io/unreachable:节点不可触达

对应NodeCondition Ready.为Unknown的情况

node.kubernetes.io/out-of-disk

节点磁盘空间已满

node.kubernetes.io/network-unavailable

节点网络不可用

node.kubernetes.io/unschedulable

节点不可调度

node.cloudprovider,kubernetes.io/uninitialized

如果kubelet是由"外部"云服务商启动的,则该污点用来标识某个节点当前为不可用状态。在云控制器 (cloud-controller-manager)初始化这个节点以后,kubelet会将此污点移除

自Kubernetes 1.13开始,上述两个特性被默认启用,TaintNodesByCondition 这个特性只会为节点添加NoSchedule效果的污点,TaintBasedEviction则为节点添加NoExecute效果的污点。

在TaintBasedEvictions特性被开启之后,kubelet会在有资源压力时对相应的Node节点自动加上对应的NoExecute效果的Taint,例如 node.kubernetes.io/memory-pressure、node.kubernetes.io/disk-pressure。

如果Pod没有设置对应的Toleration,则这部分Pod将被驱逐,以确保节点不会崩溃。

07 优先级调度

对于运行各种负载(如:​​Service​​​、​​Job​​)的中等规模或者大规模的集群来说,出于各种原因,我们需要尽可能提高集群的资源利用率

提高资源利用率的常规做法是采用优先级方案,即不同类型的负载对应不同的优先级,同时允许集群中的所有负载所需的资源总量超过集群可提供的资源,在这种情况下,当发生资源不足的情况时,系统可以选择释放一些不重要的负载(优先级最低的),保障最重要的负载能够获取足够的资源稳定运行。

7.1 案例

7.1.1 创建PriorityClass

首先,由集群管理员创建​​PriorityClass​​​(​​PriorityClass​​不属于任何命名空间):

apiversion:scheduling.k8s.io/vlbetal kind:Priorityclass
metadata:
name:high-priority
va1ue:1000000
globalDefault:false
description:"This priority class should be used for XYZ service pods only."

上述​​YAML​​​文件定义了一个名为​​high-priority​​​的优先级类别,优先级为 ​​100000​​,数字越大,优先级越高,超过一亿的数字被系统保留,用于指派给系统组件。

7.1.2 Pod声明优先级类别

可以在任意​​Pod​​​上引用上述​​Pod​​优先级类别:

apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityclassName: high-priority

如果发生了需要抢占的调度,高优先级Pod就可能抢占节点​N​,并将其低优先级​Pod​驱逐出节点​N​,高优先级​​Pod​​​的​​status​​​信息中的​​nominatedNodeName​​字段会记录目标节点的名称。

需要注意,高优先级Pod仍然无法保证最终被调度到节点​N​上,在节点​N​上低优先级​Pod​被驱逐的过程中,如果有新的节点满足高优先级​Pod​的需求,就会把它调度到新的​Node​

而如果在等待低优先级的Pod退出的过程中,又出现了优先级更高的​Pod​,调度器就会调度这个更高优先级的​Pod​到节点​N​上,并重新调度之前等待的高优先级​Pod​

7.1.3 注意事项

优先级抢占的调度方式可能会导致调度陷入“死循环”状态。当​​Kubernetes​​​集群配置了多个调度器(​​Scheduler​​)时,这一行为可能就会发生,比如下面这个例子:

​Scheduler A​​​为了调度一个(批)​​Pod​​​,特地驱逐了一些​​Pod​​​,因此在集群中有了空余的空间可以用来调度,此时​​Scheduler B​​​恰好抢在​​Scheduler A​​​之前调度了一个新的​​Pod​​​,消耗了相应的资源,因此,当​​Scheduler A​​​清理完资源后正式发起​​Pod​​​的调度时,却发现资源不足,被目标节点的​​kubelet​​进程拒绝了调度请求! 这种情况的确无解,因此最好的做法是让多个Scheduler相互协作来共同实现一个目标。

高优先级​​Pod​​​抢占节点并驱逐低优先级的​​Pod​​​,这个问题对于普通的服务型的
​​​Pod​​​来说问题不大,但对于执行批处理任务的​​Pod​​​来说就可能是个灾难,当一个高 优先级的批处理任务的​​Pod​​​创建后,正在执行批处理任务的某个低优先级的​​Pod​​可 能因为资源不足而被驱逐,从而导致对应的批处理任务被搁置。

为了避免这个问题发生,​PriorityClass增加了一个新的属性一​preemptionPolicy​,当它的值为 ​preemptionLowerPriorty​(默认)时,就执行抢占功能,当它的值被设置为​Never​ 时,就默认不抢占资源,而是静静地排队,等待自己的调度机会

08 DaemonSet(每个node上只调度一个pod)

DaemonSet是 ​​Kubernetes1.2​​ 版本新增的一种资源对象,用于管理在集群中的每个Node上仅运行一份​Pod​的副本实例,如下图所示:

k8s教程(22)-pod调度总结_Pod_09

8.1 DaemonSet

8.1.1 应用场景

下面举例DaemonSet的一些使用场景:

  • 在每个​​Node​​​上都运行一个​​GlusterFS​​​存储或者​​Ceph​​​存储的​​Daemon​​进程;
  • 在每个​​Node​​​上都运行一个日志采集程序,例如​​Fluentd​​​或者​​Logstach​​;
  • 在每个​​Node​​​上都运行一个性能监控程序,采集该​​Node​​​的运行性能数据, 例如​​Prometheus Node Exporter​​​、​​collectd​​​、​​New Relic agent​​​或者​​Ganglia gmond ​​等。

DaemonSet的Pod调度策略与RC类似,除了使用系统内置的算法在每个Node上进行调度,也可以在Pod的定义中使用NodeSelector或NodeAffinity来指定满足条件的Node范围进行调度

8.1.2 举例

下面的例子定义了为在每个Node上都启动一个​​fluentd​​​容器,配置文件 ​​fluentd-ds.yaml​​​的内容如下 (其中挂载了物理机的两个目录"​​/var/log​​​"和 “​​/var/lib/docker/containers​​”):

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-cloud-logging
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
template:
metadata:
namespace: kube-system
labels:
k8s-app: fluentd-cloud-logging
spec:
containers:
- name: fluentd-cloud-logging
image: gcr.io/google containers/fluentd-elasticsearch:1.17
resources:
limits:
cpu: 100m
memory: 200Mi
env:
- name: FLUENTD ARGS
value: -q
volumeMounts
- name: varlog
mountPath: /var/log
readOnly: false
- name: containers
mountPath: /var/lib/docker/containers
readonly: false
volumes:
- name: containers
hostPath:
path: /var/lib/docker/containers
- name: varlog
hostPath:
path: /var/log

使用​​kubectl create​​​命令创建该​​DaemonSet​​:

kubectl create -f fluentd-ds.yaml 
daemonset "fluentd-cloud-logging"created

查看创建好的​​DaemonSet​​​和​​Pod​​​,可以看到在每个​​Node​​上都创建了一个

​Pod​​:

k8s教程(22)-pod调度总结_云原生_10

8.1.3 注意事项

DaemonSet调度不同于普通的Pod调度,所以没有用默认的​​Kubernetes Scheduler​​​进行调度,而是通过专有的​​DaemonSet Controller​​​进行调度。但是随着​​Kubernetes​​版本的改进和调度特性不断丰富,产生了一些难以解决的矛盾,最主要的两个矛盾如下:

  • 普通的Pod是在Pending状态触发调度并被实例化的,DaemonSet Controller并不是在这个状态调度Pod的,这种不一致容易误导和迷惑用户
  • Pod优先级调度是被Kubernetes Scheduler执行的,而DaemonSet
    Controller并没有考虑到Pod优先级调度的问题,也产生了不一致的结果

从Kubernetes 1.8开始,​​DaemonSet​​​的调度默认切换到​​Kubernetes Scheduler​​​进行,从而一劳永逸地解决了以上问题及未来可能的新问题,因为默认切换到了​​Kubernetes Scheduler​​​统一调度​​Pod​​​,因此​​DaemonSet​​​也能正确处理​​Taints​​​和​​Tolerations​​的问题。

09 批处理调度

Kubernetes从1.2版本开始支持批处理类型的应用,我们可以通过​​Kubernetes Job​资源对象来定义并启动一个批处理任务。

9.1 批处理调度

批处理任务通常并行(或者串行) 启动多个计算进程去处理一批工作项(​​Work item​​),处理完成后,整个批处理任务结束。

9.1.1 任务模式分类

9.1.1.1 按实现方式分类

按照批处理任务实现方式的不同,批处理任务可以分为如图所示的几种模式:

k8s教程(22)-pod调度总结_互斥_11


模式分类:

模式名称

描述

Job Template Expansion

一个​​Job​​​对象对应一个待处理的​​Work item​​​,有几个​​Work item​​​就产生几个独立的​​Job​​​,通常适合​​Work item​​​数量少、每 个​​Work item​​​要处理的数据量比较大的场景,比如有一个​​100GB​​​的文件作为一个 ​​Work item​​​,总共有​​10​​个文件需要处理。

Queue with Pod Per Work Item

采用一个任务队列存放​​Work item​​​,一个​​Job​​​对象作为消费者去完成这些​​Work item​​​,在这种模式下,​​Job​​​会启动​​N​​​个​​Pod​​​,每个​​Pod​​​都对应一个​​Work item​

Queue with Variable Pod Count

也是采用一个任务队列存放​​Work item​​​,一个​​Job​​​对象作为消费者去完成这些​​Work item​​​,但与上面的模式不同,​​Job​​​启动的​​Pod​​数量是可变的

Single Job with Static Work Assignment

也是一个​​Job​​​产生多个​​Pod​​,但它采用程序静态方式分配任务项,而不是采用队列模式进行动态分配

模式对比:

模式名称

是否是一个job

pod的数量少于work item

用户程序是否要做相应的修改

kubernetes是否支持

Job Template Expansion

/

/



Queue with Pod Per Work Item


/

有时候需要


Queue with Variable Pod Count


/

/


Single Job with Static Work Assignment


/


/

9.1.1.2 按批处理并行分类

考虑到批处理的并行问题,​​Kubernetes​​​将​​Job​​分以下三种类型:

类型

描述

Non-parallel Jobs

通常一个Job只启动一个Pod,除非Pod异常,才会重启该Pod,一旦此Pod正常结束,Job将结束

Parallel Jobs with a fixed completion count

并行Job会启动多个Pod,此时需要设定Job的​​.spec.completions​​​参数为一个正数,当正常结束的Pod 数量达至此参数设定的值后,Job结束。此外,Job的​​.spec.parallelism​​参数用来控制并行度,即同时启动几个Job来处理Work item

Parallel Jobs with a work queue

任务队列方式的并行Job需要一个独 立的Queue,Work item都在一个Queue中存放,不能设置Job 的​​.spec.completions​​参数,此时Job有以下特性:① 每个Pod都能独立判断和决定是否还有任务项需要处理,如果某个Pod正常结束,则Job不会再启动新的Pod 。 ②如果一个Pod成功结束,则此时应该不存在其他Pod还在工作的情况,它们应该都处于即将结束、退出的状态。③如果所有Pod都结束了,且至少有一个Pod成功结束,则整个Job成功结束

9.1.2 案例

9.1.2.1 Job Template Expansion案例

首先是Job Template Expansion模式,由于在这种模式下每个​​Work item​​​都对应一个​​Job​​​实例,所以这种模式首先定义一个​​Job​​​模板,模板里的主要参数是​​Work item​​​的标识,因为每个​​Job​​​都处理不同的​​Work item​​。

如下所示的​​Job​​​模板(文件名为​​job.yaml.txt​​)中的 $ITEM 可以作为任务项的标识:

apiVersion: batch/v1
kind: Job
metadata:
name: process-item-$ITEM
labels:
jobgroup: jobexample
spec:
template:
metadata:
name: jobexample
labels:
jobgroup: jobexample
spec:
containers:
- name: c
image: busybox
command: ["sh","-c","echo Processing item $ITEM &sleep 5"]
restartPolicy: Never

通过下面的操作,生成了3个对应的​​Job​​​定义文件并创建​​Job​​:

> for i in apple banana cherry 
> do
> cat job.yaml.txt | sed "s/\$ITEM/$i/" > ./jobs/job-$i.yaml
> done
# ls jobs
job-apple.yaml job-banana.yaml job-cherry.yaml
# kubectl create -f jobs
job "process-item-apple"created
job "process-item-banana"created
job "process-item-cherry"created

观察Job的运行情况:

$ kubect1 get jobs -l jobgroup=jobexample

NAME DESIRED SUCCESSFUL AGE
process-item-apple 1 1 4m
process-item-banana 1 1 4m
process-item-cherry 1 1 4m
9.1.2.2 Queue with Pod Per Work Item案例

在这种模式下需要一个任务队列存放​​Work item​​​,比如​​RabbitMQ​​​客户端程序先把要处理的任务变成​​Work item​​​放入任务队列,然后编写​​Worker​​​程序、打包镜像并定义成为​​Job​​​中的​​Work Pod​​。

​Worker​​​程序的实现逻辑是从任务队列中拉取一个​​Work item​​并处理, 在处理完成后结束进程。并行度为2的Demo如下图所示:

k8s教程(22)-pod调度总结_云原生_12

9.1.2.3 Queue with Variable Pod Count案例

由于这种模式下,​​Worker​​​程序需要知道队列中是否还有等待处理的​​Work item​​,如果有就取出来处理,否则就认为所有工作完成并结束进程,所以任务队列通常要采用Redis或者数据库来实现:

k8s教程(22)-pod调度总结_云原生_13

10 定时任务

Kubernetes从1.5版本开始增加了一种新类型的Job,即类似Linux Cron的定时任务​​Cron Job​​​,下面看看如何定义和使用这种类型的​​Job​​。

10.1 基本语法

首先,确保​​Kubernetes​​​的版本为​​1.8​​及以上。

Cron Job的定时表达式基本上照搬了Linux Cron的表达式,格式如下:

Minutes Hours DayofMonth Month DayofWeek

其中每个域都可出现的字符如下。


描述

Minutes

可出现​​“,”​​​ ​​“-”​​​ ​​“*”​​​ ​​“/”​​​ 这4个字符,有效范围为​​0~59​​的整数

Hours

可出现​​“,”​​​ ​​“-” ​​​ ​​“%”​​​ ​​“/”​​​ 这4个字符,有效范围为​​0~23​​的整数

DayofMonth

可出现​​“,”​​​ ​​“-​​​ ​​“*”​​​ ​​“/“​​​ ​​ “?”​​​ ​​ “L”​​​ ​​ “W“​​​ ​​ “C”​​​这8个字符,有效范围 为​​1~31​​的整数

Month

可出现​​“,”​​​ ​​ “-”​​​ ​​“*”​​​ ​​“/”​​​这4个字符,有效范围为1~12的整数或​​JAN~DEC​

DayofWeek

可出现​​“,”​​​ ​​“*”​​​ ​​“/”​​​ ​​“?”​​​ ​​“L”​​​ ​​“C”​​​ ​​“#” ​​​这8个字符,有效范围为​​1~7​​​的整数或​​SUN~SAT​​​。​​1​​​表示星期天,​​2​​表示星期一,以此类推

表达式中的特殊字符​​“*”​​​与​​“/”​​的含义如下:

  • ​*​​​:表示匹配该域的任意值,假如在​Minutes​​域使用​​“*”​​,则表示每分钟都会触发事件;
  • ​/​​:表示从起始时间开始触发,然后每隔固定时间触发一次,例如在
    Minutes域设置为5/20,则意味着第1次触发在第5min时,接下来每20min触发一 次,将在第25min、第45min等时刻分别触发

10.2 案例

比如,我们要每隔​​1min​​执行一次任务,则Cron表达式如下:

*/1 * * * *

编写一个Cron Job的配置文件(​​cron.yaml​​):

apiversion: batch/vl beta 
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name:hello
image:busybox
args:
- /bin/sh
- -C
- date;echo Hello from the Kubernetes cluster restartPolicy:OnFailure

该例子定义了一个名为​​hello​​​的​​Cron Job​​​,任务每隔1min执行一次,运行的镜像是​​busybox​​​,运行的命令是​​Shell​​脚本,脚本运行时会在控制台输出当前时间和字符串"Hello from the Kubernetes cluster".

接下来运行​​kubectl create​​命令完成创建:

$ kubectl create -f cron.yaml 

cronjob "hello"created

然后每隔​​1min​​​运行​​kubectl get cronjob hello​​查看任务状态,发现的确每分钟调度了一次:

k8s教程(22)-pod调度总结_容器化技术_14


还可以通过查找Cron Job对应的容器,验证每隔1min产生一个容器的事实:

k8s教程(22)-pod调度总结_Pod_15


查看任意一个容器的日志,结果如下:

k8s教程(22)-pod调度总结_Pod_16


运行下面的命令,可以更直观地了解Cron Job定期触发任务执行的历史和现状:

k8s教程(22)-pod调度总结_互斥_17


在​​Kubernetes1.9​​​版本后,​​kubectl​​​命令增加了别名​​cj​​​来表示​​cronjob​​​,同时 ​​kubectl set image/env​​​命令也可以作用在​​CronJob​​对象上。

11 容灾调度

我们可以将​​Pod​​的各种常规调度策略认为是将整个集群视为一个整体,然后进行 “打散或聚合” 的调度。

当我们的集群是为了容灾而建设的跨区域的多中心(多个​​Zone​​)集群,即集群中的节点位于不同区域的机房时,比如:

北京、上海、广 州、武汉,要求每个中心的应用相互容灾备份,又能同时提供服务,此时最好的调度策略就是将需要容灾的应用均匀调度到各个中心,当某个中心出现问题时, 又自动调度到其他中心均匀分布,

​Pod​​的多中心均匀分布调度效果图如下所示(不管每个中心的Nod节点数量如何):

k8s教程(22)-pod调度总结_Pod_18

11.1 如何实现?

用普通的基于​​Node​​​标签选择的调度方式也可以实现上述效果,比如为每个
Zone都建立一个Deployment,Pod的副本总数除以Zone的数量就是每个分区的
Pod副本数量。但这样做有个问题:如果某个Zone失效,那么这个Zone的Pod就无法迁移到其他Zone。

另外,topology.kubernetes.io/zone就是Kubernetes默认支持的重要拓扑域之
一,那是否可以用Pod的亲和性调度来解决这个问题呢?不能,因为Pod的亲和性 调度用于解决相关联的Pod的调度问题,不能保证被依赖的Pod被均匀调度到多个Zone。

为了满足这种容灾场景下的特殊调度需求,在Kubernetes1.16版本中首次引入Even Pod Spreading特性用于通过topologyKey属性识别Zone,并通过设置新 的参数topologySpreadConstraints来将Pod均匀调度到不同的Zone

11.2 举例

举个例子, 假如我们的集群被划分为多个​​Zone​​​,我们有一个应用(对应的​​Pod​​​标签为 ​​app=foo​​​)需要在每个​​Zone​​​均匀调度以实现容灾,则可以定义​​YAML​​文件如下:

spec: 
topologySpreadConstraints:
- maxSkew: 1
whenUnsatisfiable: DoNotSchedule
topologyKey: topology.kubernetes.io/zone
selector:
matchLabels:
app: foo

在以上YAML定义中,关键的参数是​​maxSkew​​,用于指定Pod在各个​Zone​上调度时能容忍的最大不均衡数

  • 值越大,表示能接受的不均衡调度越大;
  • 值越小,表示各个Zone的Pod数量分布越均匀。

为了理解​​maxSkew​​​,我们需要先理解​​skew​​参数的计算公式:

skew[topo]=count[topo]-min(count[topo])

即每个拓扑区域的skew值都为该区域包括的目标Pod数量与整个拓扑区域最少Pod数量的差,而naxSkew就是最大的skew值。

假如在上面的例子中有3个拓扑区域,分别为Zone A、Zone B及Zone C,有3个目标Pod需要调度到这些拓扑区域,那么前两个毫无疑问会被调度到Zone A和Zone B,Even Pod Spreading调度效果如图所示:

k8s教程(22)-pod调度总结_kubernetes_19


那么,第3个​​Pod​​会被调度到哪里呢?我们可以手动计算每个Zone的skew

  • 首先计算出​​min(count[topo])​​是0,对应Zone C;
  • 于是​​Zone A​​的skew=1-0=1,​​Zone B​​的skew=1-0=0,​​Zone C​​的skew=0-0=0,于是第3个Pod应该被放在​​Zone C​​,此时min(count[topo])的值就变成了1,而实际的maxSkew的值为0,符合预期设置;
  • 如果我们把maxSkew设置为2,则在这种情况下,第3个Pod被放在
    ​​​Zone A​​​或​​Zone B​​都是符合要求的。

有了新的Even Pod Spreading调度特性的加持,再加上之前就已成熟的Pod亲和性调度,Kubernetes就可以完美实现特定应用的容灾部署目标了。

具体做法也很简单:将一个应用中需要部署在一起的几个Pod用亲和性调度声明捆绑,然后选 择其中一个Pod,加持Even Pod Spreading调度规则即可。最终的部署效果图如下:

k8s教程(22)-pod调度总结_互斥_20


标签:Node,22,调度,Taint,Pod,Job,pod,k8s,节点
From: https://blog.51cto.com/u_15294985/5982053

相关文章

  • C++团购订单信息管理系统[2022-12-31]
    C++团购订单信息管理系统[2022-12-31]团购订单信息管理系统功能说明:要求编写一个团购订单信息管理系统。系统包含对订单的添加、查询、修改、删除和浏览等功能。系统......
  • 2022年终总结
    一波三折哈喽大家好,我是贺志营,又到了年底,一年一度的年终总结来了。今年对我也算发生了几件具有转折性意义的事。按时间顺序来吧,首先是准备复试,结果还准备的一塌糊涂,复试好像......
  • 2022-12版本的Rstudio它来了,它喊我升级了
    1.Rstudio喊我升级最近每一次打开Rstudio,总是推送给我最新版的Rstudio:它之前不是这样的,那时候它所在的公司还叫Rstudio,现在改名叫Posit了,就开始推送了,也许它认为是重大的更......
  • Kubernetes 1.26 新功能 Pod 调度就绪特性解析
    Kubernetes1.26新功能Pod调度就绪特性解析Kubernetes1.26引入了Pod的一个新特性:​​schedulinggates​​。在Kubernetes中,调度门是告诉调度程序何时准备好考虑调......
  • k8s教程(25)-pod扩缩容
    文章目录​​01引言​​​​02手动扩缩容机制​​​​03自动扩缩容机制​​​​3.1HPA控制器​​​​3.2指标的类型​​​​3.3扩缩容算法​​​​3.4HorizontalPodA......
  • k8s教程(21)-pod之容灾调度
    文章目录​​01引言​​​​02如何实现?​​​​03举例​​​​04文末​​01引言声明:本文为《Kubernetes权威指南:从Docker到Kubernetes实践全接触(第5版)》的读书笔记我们......
  • 使用MicroK8s部署第一个应用程序
    Kubernetes具有挑战性。对此,没有任何争议。不仅在将容器部署到Kubernetes集群中有很多移动部件,而且在这一过程中可能会出现很多问题。更为复杂的是,部署Kubernetes集群可能是......
  • 2022最炫酷的圣诞树合集(附动态效果展示和网盘源码)
    文章目录​​3D旋转水晶球(雪屋)​​​​3D旋转水晶球(圣诞树)​​​​豪华圣诞树​​​​Garland圣诞树​​​​花灯圣诞树​​​​Live圣诞树​​​​五彩圣诞树​​​​Gre......
  • 【云原生】K8s PSP 和 securityContext 介绍与使用
    目录一、概述二、PodSecurityPolicy的发展1)以前为什么需要PodSecurityPolicy?2)现在为什么PodSecurityPolicy要消失?三、PSP简单使用1)开启PSP2)示例演示1、没有PSP场景测......
  • 我希望来年,更多是靠关系和模式挣钱——2022年我的总结与思考
      我记得是2017年开始在博客园写博客,那年我儿子出生,并与当年年底写了17年的年度总结。   n年前,我没钱但年轻,我怕n年后我老时,还是一无所成——2017我的收获和反思......