• Kubernetes高级调度- Taint和Toleration、Node Affinity分析


    此文分享了污点和Node Affinity实际使用过程中的细节、坑和思维误区。同时整理且回答了诸多细节问题,尤其那些在官方文档中不曾提及的细节。

    阅读提示:文中的节点指Node

    (避免Pod和Node同时出现在一小段文字中,所以Node以节点汉字表述)

    Taint和Toleration

    污点的理论支撑

    0410_1.jpg

    1.1 污点设置有哪些影响效果

    使用效果(Effect):

    • PreferNoSchedule:调度器尽量避免把Pod调度到具有该污点效果的节点上,如果不能避免(如其他节点资源不足),Pod也能调度到这个污点节点上。

    • NoSchedule:不容忍该污点效果的Pod永不会被调度到该节点上,通过kubelet管理的Pod(static Pod)不受限制;之前没有设置污点的Pod如果已运行在此节点(有污点的节点)上,可以继续运行。

    • NoExecute: 调度器不会把Pod调度到具有该污点效果的节点上,同时会将节点上已存在的Pod驱逐出去。

    污点设置的第一前提是: 节点上的污点key和Pod上的污点容忍key相匹配。

    1.2 设置污点的效果实测

    0410_2.jpg

    当Pod未设置污点容忍而节点设置了污点时

    • 当节点的污点影响效果被设置为:PreferNoSchedule时,已存在于此节点上的Pod不会被驱逐;新增但未设置污点容忍的Pod仍然可以被调度到此节点上。

    • 当节点的污点影响效果被设置为:NoSchedule时,已存在于此节点上的Pod不会被驱逐;同时,新增的Pod不会被调度此节点上。

    • 当节点的污点影响效果被设置为:NoExecute时,已存在于此节点上的Pod会发生驱逐(驱逐时间由tolerationSeconds字段确定,小于等于0会立即驱逐);新增的Pod不会调度到此节点上。

    当Node设置了污点且Pod设置了对应的污点容忍时,实测效果如下表:

    0410_3.jpg

    污点容忍设置, 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(不重要)

    0410_4.jpg

    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设置沿用上一个例子的, 根据上面的配置,我们会看到:

    0410_5.jpg

    如表所示, 最终Pod优先调度到10.x.x.85, 原因在于app=1的权重是4, level=important的权重是5, 所以:节点 10.x.x.80的权重是:9,但是仍然小于节点:10.x.x.85的权重。

    2.2 问题总结

    1. 其实强亲和性和首选亲和性区别体现在:Pod对节点的选择上。就强亲和性而言,如果节点不能匹配Pod对Label的要求, Pod永远不会调度到这类节点上,即使是Pod调度失败(没错,就是头铁),就首选亲和性来说,能调度到最优节点是一件非常值得开心的事情,如果不能调度到最优节点可以退而求其次,总有适合自己的。 (回答问题1)

    2. 首选亲和性体现在PodLabel的权重值上,而与节点Label的匹配个数无关。(回答问题2)

    3. 在首选亲和性配置中会多一个权重值的字段(weight),这个值越大,权重越大,Pod调度到对应此Label的节点的概率越高。(回答问题4)

    4. 一个节点有多个Label且节点能满足Pod所要求的所有Label,如果多个Label的权重值相加仍然小于某单个Label的节点,那么Pod首选是权重值高的节点;如果Pod能匹配到A 节点的所有Label,同时也能匹配到B 节点某一个Label.但是,A节点 Label的权重之和刚好等于B 节点的单个Label的权重,这时,Pod优先选择的A还是B这是随机的(只针对亲和性来说是随机的,实际情况还要考虑其他情况)。而不会管Label的匹配个数。(回答问题5)

    5. 创建或扩容Pod时,优先选择Label匹配权重值大的节点,若此节点的其他条件不满足(比如内存不足),选择次权重的,最后选择Label不匹配或者根本没有Label的节点。

    6. (回答问题6)缩容时,随机选择Pod杀掉,而不是我们预想的从低权重的节点开始杀,这点值得注意。

    7. (回答问题7)答案是不会,正在运行的Pod不会被调度到新的节点去, 当Pod因为某种原因重启(指Pod名字改变,触发重调度,名字不改变,意味着不触发调度器调度,只是原地重启)后,会自动调度到符合亲和性选择的节点上。

    污点和Node Affinity的使用总结

    0410_6.jpg

    1. 就污点而言,它的使用通常是负向的, 也就说, 污点常用在某Node不让大多数Pod调度只让少部分Pod调度时,又或者节点根本不参加工作负载时。比如:我们常见的master节点上不调度负载pod,保证master组件的稳定性;节点有特殊资源,大部分应用不需要而少部分应用需要,如GPU。

    2. 就Node Affinity来说,他的使用可以正向的,也就是说,我们想让某个应用的Pod部署在指定的一堆节点上。当然,也可以是负向的,比如说我们常说的Node 反亲和性,只需要把操作符设置为NotIn就能达成预期目标。

    3. 就污点而言,如果节点设置的污点效果是NoSchedule或者NoExecute,意味着没有设置污点容忍的Pod绝不可能调度到这些节点上。

    4. 就Node Affinity而言,如果节点设置了Label,但是Pod没有任何的Node Affinity设置,那么Pod是可以调度到这些节点上的。

    相关服务请访问:https://support.huaweicloud.com/cce/index.html?utm_content=cce_helpcenter_2019

  • 相关阅读:
    ${}和#{}的区别,使用场景
    Mysql 面试高频题:innodb,myisam(待更)
    Python 基础
    Python 基础
    python之input(), raw_input()
    Python之回调魔法
    python之setattr,getattr,hasattr
    python之类私有成员
    python之函数式编程
    python之类定义
  • 原文地址:https://www.cnblogs.com/CCE-SWR/p/10684985.html
Copyright © 2020-2023  润新知