此文分享了污点和Node Affinity实际使用过程中的细节、坑和思维误区。同时整理且回答了诸多细节问题,尤其那些在官方文档中不曾提及的细节。
阅读提示:文中的节点指Node
(避免Pod和Node同时出现在一小段文字中,所以Node以节点汉字表述)
Taint和Toleration
污点的理论支撑
1.1 污点设置有哪些影响效果
使用效果(Effect):
-
PreferNoSchedule:调度器尽量避免把Pod调度到具有该污点效果的节点上,如果不能避免(如其他节点资源不足),Pod也能调度到这个污点节点上。
-
NoSchedule:不容忍该污点效果的Pod永不会被调度到该节点上,通过kubelet管理的Pod(static Pod)不受限制;之前没有设置污点的Pod如果已运行在此节点(有污点的节点)上,可以继续运行。
-
NoExecute: 调度器不会把Pod调度到具有该污点效果的节点上,同时会将节点上已存在的Pod驱逐出去。
污点设置的第一前提是: 节点上的污点key和Pod上的污点容忍key相匹配。
1.2 设置污点的效果实测
当Pod未设置污点容忍而节点设置了污点时
-
当节点的污点影响效果被设置为:PreferNoSchedule时,已存在于此节点上的Pod不会被驱逐;新增但未设置污点容忍的Pod仍然可以被调度到此节点上。
-
当节点的污点影响效果被设置为:NoSchedule时,已存在于此节点上的Pod不会被驱逐;同时,新增的Pod不会被调度此节点上。
-
当节点的污点影响效果被设置为:NoExecute时,已存在于此节点上的Pod会发生驱逐(驱逐时间由tolerationSeconds字段确定,小于等于0会立即驱逐);新增的Pod不会调度到此节点上。
当Node设置了污点且Pod设置了对应的污点容忍时,实测效果如下表:
污点容忍设置, Exists和Equal这两个操作符之间的区别是什么?
在配置上:
-
Exists必须把值设置为空字符串,而只关心key与节点的污点key是否匹配。
-
Equal需要同时设置key和value来匹配污点节点的Key和value。
两者之间的理解加深:
-
若一个节点存在多个污点, Pod使用Exists只容忍了其中一个污点, 仍然不能调度此节点, 原因在于Pod不容忍此节点的其他污点。
-
若一个节点存在多个污点, Pod使用Equal只容忍了其中一个污点, 仍然不能调度此节点, 原因在于Pod还是不容忍此节点的其他污点。
-
若想要一个Pod能够调度到含有多个污点的节点上, 那么此Pod需要容忍此节点的所有污点。
1.3 污点容忍里的一些小窍门:
-
在污点容忍设置时,若key,value是空字符且操作符是Exists 那么能Pod容忍节点的所有污点。(注意:仍然遵从于容忍效果的等级设置)。例如:一个Pod在设置污点容忍时,key,value为空且操作符是Exists,容忍效果为:NoExecute,那么该Pod不会调度到污点效果为:NoSchedule的节点上。
-
在设置污点容忍时, 若Pod的容忍效果(effect)被设置为空字符,那么Pod能匹配所有的容忍效果。
-
在设置污点容忍时, 若key,value为空、操作符是Exists且容忍效果(effect)也为空时,则等于没有设置。
默认情况下,操作符是Equal。
如果节点的影响效果是NoExecute,且不想Pod被立即驱逐,那么可以设置TolerationSeconds(延迟驱逐时间),若值是0或者负数会立即驱逐,若值大于0,则在此时间后开始驱逐。
从测试结果来看,只要节点设置了污点且效果是:NoExecute,不管Pod是否容忍了该污点都不能在对应节点上正常运行(一直处于删除,重建的过程),原因是能被调度到节点上是调度器选择的结果,Pod被杀掉是本地kubelet决策的结果,这是两个组件分管不同工作产生的效果,下面这种配置除外。
tolerations: - operator: Exists
#此Pod的污点配置能够容忍所有的污点,所有的影响效果,所有能调度到所有的节点上(包括影响效果被设置为:NoExecute的Node).
1.4 认知误区
1.4.1当一个节点设置了污点,那么只要设置Pod对此污点容忍就能调度上去且能正常运行。(错)
当节点的一个污点的影响效果被设置为:NoExecute,此时Pod对此污点的容忍效果也是NoExecute时, Pod能调度上去,但是也会被Terminating,不断的处于Terminating,ContainerCreating的过程。
对Node设置污点:
kubectl taint nodes 1xx status=unavailable:NoExecute
Pod设置的污点容忍:
tolerations: - effect: NoExecute key: status operator: Equal tolerationSeconds: 0 value: unavailable
效果:
tolerations: - operator: Exists
#此Pod的污点配置能够容忍所有的污点,所有的影响效果,所有能调度到所有的节点上(包括影响效果被设置为:NoExecute的Node).
1.4.2 当一个节点设置了多个污点,只要使用Exists操作符匹配到其中一个污点,此Pod就能调度到对应的节点上。(错)
原因在于Pod只能匹配到其中一个污点,但是还是不能匹配到其他污点。所以,不能调度上去。
1.4.3 在设置Pod容忍时,只要匹配到key和value就行了,不用关心容忍效果的设置。(错)
容忍效果的设置和key/value的设置一样重要,甚至更加重要。如果容忍效果不匹配。也会导致Pod调度不能到对应节点上。
1.4.4 如果Pod没有设置任何的污点容忍,Pod就不能调度到有污点的节点上。(错)
如果节点的污点效果是: PreferNoSchedule, 没有设置任何污点容忍的Pod也能调度到此节点上。原因在于:PreferNoSchedule的意思是优先不调度,但是当没有节点可用时,Pod仍然能调度到此节点。
二
Node Affinity
Node Affinity可以让指定应用调度到指定的节点,这有利于应用的稳定性,减少重要业务和不重要业务之间相互抢占资源的可能,同时也可以降低不重要业务对重要业务的影响,另一方面,也能够进行多租户之间的隔离。根据租户需求为租户提供特定的运行环境。
2.1 NodeAffinity配置要点
NodeAffinity配置分类两大部分:
requiredDuringSchedulingIgnoredDuringExecution (强亲和性)
preferredDuringSchedulingIgnoredDuringExecution (首选亲和性)
但是,在真实的配置环节时,又会犯迷糊:
-
强亲和性到底多强算强?
-
首选亲和性中的首选体现在那些方面?
-
强亲和性配置时,有两种配置方式,两种的区别是什么?
-
首选亲和性中的权重值到底是什么规则? 值越大权重值越高么?还是值越小越高(1最大)?
-
首选亲和性配置中, 如果Pod能匹配A节点的多个Label,也能匹配B节点的一个Label(A的Label权重之和等于B单个Label的权重),这时Pod会优先调度到A节点么?
-
缩容时,是先从低权重的节点上开始杀么? 这些问题, 我们都不能全靠注释和理解去揣测答案,必须经过实测得到真实答案,否则一旦上了生产再想修改就需要付出更大的成本。
-
如果Pod是以强亲和性的方式绑定在节点上且Pod已经正常运行在此节点上,此时删除节点的标签是否会导致Pod重启发生漂移。
强亲和性:
requiredDuringSchedulingIgnoredDuringExecution
例子Node Labels设定:
level: important(重要),general(一般),unimportant(不重要)
Pod与运算的配置:
注意: 强亲和性的配置分为: 与运算、或运算两部分
requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: level operator: In values: - important - key: app operator: In values: - 1
在与运算的配置中,我们发现,在同一个matchExpressions中既需要匹配level=important的标签也需要匹配app=1的标签。也就是说:Pod只会选择同时匹配这两个Label的节点。
根据上面Pod的Node亲和性设置,两个Label求一个交集,只有同时满足两个Label的节点才会纳入这个Pod的调度池,显而易见,只有10.x.x.80这个节点。所以,此Pod只能调度到这个节点,如果这个节点资源不足,那么此Pod调度失败。
Pod或运算配置:
requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: level operator: In values: - important - matchExpressions: - key: level operator: In values: - unimportant
在或运算的配置中,我们发现有一个matchExpressions数组,里面的Label列表求并集。也就是说:Pod可以选择只要匹配其中一个Label的节点就行,不用全匹配。
举个例子:
节点的Label设置沿用上一个例子的。 节点的标签只要能满足Pod的其中一个标签, 节点就能纳入这个Pod的调度池,显而易见,这个Pod可选的节点有:10.x.x.78, 10.x.x.79,10.x.x.80, 10.x.x.86, 10.x.x.87, 10.x.x.88。
首选亲和性:
preferredDuringSchedulingIgnoredDuringExecution
它的使用风格应该是:如果Pod能调度到指定Label的节点最好,如果不能,也不强求,Pod选择其他的节点也行,即使这个节点根本没有Label或者节点的Label和我完全不匹配。
Pod首选亲和性设置:
preferredDuringSchedulingIgnoredDuringExecution: - preference: matchExpressions: - key: level operator: In values: - important weight: 5 - preference: matchExpressions: - key: app operator: In values: - "1" weight: 4 - preference: matchExpressions: - key: master operator: In values: - "1" weight: 10
示例: Node的Label设置沿用上一个例子的, 根据上面的配置,我们会看到:
如表所示, 最终Pod优先调度到10.x.x.85, 原因在于app=1的权重是4, level=important的权重是5, 所以:节点 10.x.x.80的权重是:9,但是仍然小于节点:10.x.x.85的权重。
2.2 问题总结
-
其实强亲和性和首选亲和性区别体现在:Pod对节点的选择上。就强亲和性而言,如果节点不能匹配Pod对Label的要求, Pod永远不会调度到这类节点上,即使是Pod调度失败(没错,就是头铁),就首选亲和性来说,能调度到最优节点是一件非常值得开心的事情,如果不能调度到最优节点可以退而求其次,总有适合自己的。 (回答问题1)
-
首选亲和性体现在PodLabel的权重值上,而与节点Label的匹配个数无关。(回答问题2)
-
在首选亲和性配置中会多一个权重值的字段(weight),这个值越大,权重越大,Pod调度到对应此Label的节点的概率越高。(回答问题4)
-
一个节点有多个Label且节点能满足Pod所要求的所有Label,如果多个Label的权重值相加仍然小于某单个Label的节点,那么Pod首选是权重值高的节点;如果Pod能匹配到A 节点的所有Label,同时也能匹配到B 节点某一个Label.但是,A节点 Label的权重之和刚好等于B 节点的单个Label的权重,这时,Pod优先选择的A还是B这是随机的(只针对亲和性来说是随机的,实际情况还要考虑其他情况)。而不会管Label的匹配个数。(回答问题5)
-
创建或扩容Pod时,优先选择Label匹配权重值大的节点,若此节点的其他条件不满足(比如内存不足),选择次权重的,最后选择Label不匹配或者根本没有Label的节点。
-
(回答问题6)缩容时,随机选择Pod杀掉,而不是我们预想的从低权重的节点开始杀,这点值得注意。
-
(回答问题7)答案是不会,正在运行的Pod不会被调度到新的节点去, 当Pod因为某种原因重启(指Pod名字改变,触发重调度,名字不改变,意味着不触发调度器调度,只是原地重启)后,会自动调度到符合亲和性选择的节点上。
三
污点和Node Affinity的使用总结
-
就污点而言,它的使用通常是负向的, 也就说, 污点常用在某Node不让大多数Pod调度只让少部分Pod调度时,又或者节点根本不参加工作负载时。比如:我们常见的master节点上不调度负载pod,保证master组件的稳定性;节点有特殊资源,大部分应用不需要而少部分应用需要,如GPU。
-
就Node Affinity来说,他的使用可以正向的,也就是说,我们想让某个应用的Pod部署在指定的一堆节点上。当然,也可以是负向的,比如说我们常说的Node 反亲和性,只需要把操作符设置为NotIn就能达成预期目标。
-
就污点而言,如果节点设置的污点效果是NoSchedule或者NoExecute,意味着没有设置污点容忍的Pod绝不可能调度到这些节点上。
-
就Node Affinity而言,如果节点设置了Label,但是Pod没有任何的Node Affinity设置,那么Pod是可以调度到这些节点上的。
相关服务请访问:https://support.huaweicloud.com/cce/index.html?utm_content=cce_helpcenter_2019