共计 6028 个字符,预计需要花费 16 分钟才能阅读完成。
行业资讯
服务器
云计算
Kubernetes 高级调度中如何进行 Taint 和 Toleration、Node Affinity 分析
Kubernetes 高级调度中如何进行 Taint 和 Toleration、Node Affinity 分析,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
(避免 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 是可以调度到这些节点上的。
看完上述内容,你们掌握 Kubernetes 高级调度中如何进行 Taint 和 Toleration、Node Affinity 分析的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注丸趣 TV 行业资讯频道,感谢各位的阅读!