控制调度
目标
使用全部七种决定 Pod 将运行在哪个节点上的方法,最后结合前面步骤形成的集群状态(被 cordon 的节点、带 taint 的节点)来设计调度方案。
为什么这很重要
调度在 CKA 中分值不算高,但一半的故障排查题实际上都是调度问题。看到 Pending Pod 时,必须能够判断它在 filter 阶段的哪个条件上被阻止。
尤其是 topologySpreadConstraints,经常被误解。计算偏差时使用的候选域,是能够通过 Pod nodeAffinity/nodeSelector 的节点。taint 默认会被忽略,仍参与计算。因此,Pod 实际无法使用的节点可能作为一个含 0 个 Pod 的域留下,增大偏差;当设置为 DoNotSchedule 时,其余 Pod 可能全部保持 Pending。理解 node affinity 是缩小候选范围的关键,正是本实验的核心。
步骤
- 创建 namespace
cka-sched。为lab-node-0添加标签topology.kubernetes.io/zone=zone-a,为lab-node-1添加zone-b,为lab-node-2添加zone-c,并额外为lab-node-0添加disktype=ssd。然后创建 Podssd-pod(镜像nginx:1.27),使用 nodeSelectordisktype=ssd将其调度到lab-node-0。 - 为
lab-node-2添加标签disktype=hdd,将 Podaffinity-required只使用 required node affinity(disktype In [hdd]) 调度到lab-node-2。不要使用 nodeSelector。 - 创建 Deployment
pref(副本数 2,镜像nginx:1.27),并添加 preferred node affinity。weight 为 50,条件为disktype In [ssd]。 - 创建 Deployment
spread(副本数 3,Pod 标签app=spread),并添加 required Pod anti-affinity。labelSelector 为app=spread,topologyKey 为topology.kubernetes.io/zone。 - 创建 Deployment
drain-demo(副本数 2,镜像nginx:1.27),然后 cordon 并 drainlab-node-1,将其完全清空。drain-demo应在其他节点上恢复到 2/2。 - 为
lab-node-2添加 taintworkload=batch:NoSchedule。为 Podbatch-pod设置能够容忍该 taint 的 toleration 和 nodeSelectordisktype=hdd,将其调度到lab-node-2。 - 创建 PriorityClass
cka-high。value 为 100000,globalDefault 为 false,preemptionPolicy 为PreemptLowerPriority。让 Podimportant使用该类。 - 创建 Deployment
evenly(副本数 4,Pod 标签app=evenly)。topologySpreadConstraints 设置为 maxSkew 1、topologyKeytopology.kubernetes.io/zone、whenUnsatisfiableDoNotSchedule、labelSelectorapp=evenly。同时添加 required node affinitydisktype In [ssd, hdd]和对workload=batch:NoSchedule的 toleration,使 4 个 Pod 在两个 zone 中按 2:2 分布。
参考
- 使用
kubectl get pods -n cka-sched -o wide可以立即查看 Pod 被调度到哪个节点。 - drain 通常需要添加
--ignore-daemonsets --delete-emptydir-data --force才能执行到底。 - 常见错误 1:第 5 步只执行 cordon 就结束。cordon 只会阻止新 Pod。
- 常见错误 2:第 8 步遗漏 node affinity。这样,被 cordon 的
lab-node-1会作为一个含 0 个 Pod 的 zone 留下,使偏差变为 2,导致一半 Pod 保持 Pending。
节点标签与 nodeSelector
创建 namespace cka-sched。为 lab-node-0 添加标签 topology.kubernetes.io/zone=zone-a,为 lab-node-1 添加 zone-b,为 lab-node-2 添加 zone-c,并额外为 lab-node-0 添加 disktype=ssd。然后创建 Pod ssd-pod(镜像 nginx:1.27),使用 nodeSelector disktype=ssd 将其调度到 lab-node-0。
nodeSelector 是位于 Pod spec 顶层的 map。标签必须精确匹配,且不能使用表达式。
required node affinity
为 lab-node-2 添加标签 disktype=hdd,将 Pod affinity-required 只使用 required node affinity(disktype In [hdd]) 调度到 lab-node-2。不要使用 nodeSelector。
affinity.nodeAffinity 下的 requiredDuringSchedulingIgnoredDuringExecution 中包含 nodeSelectorTerms 数组。matchExpressions 的 operator 为 In。不要使用 nodeSelector。
preferred node affinity
创建 Deployment pref(副本数 2,镜像 nginx:1.27),并添加 preferred node affinity。weight 为 50,条件为 disktype In [ssd]。
preferred 部分是数组,每一项都包含 weight 和 preference。即使无法满足也仍可调度,因此评分不检查具体落在哪个节点。
使用 Pod anti-affinity 分散部署
创建 Deployment spread(副本数 3,Pod 标签 app=spread),并添加 required Pod anti-affinity。labelSelector 为 app=spread,topologyKey 为 topology.kubernetes.io/zone。
podAntiAffinity 的条目包含 labelSelector 和 topologyKey。由于每个节点的 zone 标签不同,按 zone 分散最终也会分布到不同节点。
清空节点
创建 Deployment drain-demo(副本数 2,镜像 nginx:1.27),然后 cordon 并 drain lab-node-1,将其完全清空。drain-demo 应在其他节点上恢复到 2/2。
cordon 只阻止新 Pod,已运行的 Pod 会保留。要真正清空节点,必须执行 drain,并加入 DaemonSet 和 emptyDir 相关选项才能继续。
taint 与 toleration
为 lab-node-2 添加 taint workload=batch:NoSchedule。为 Pod batch-pod 设置能够容忍该 taint 的 toleration 和 nodeSelector disktype=hdd,将其调度到 lab-node-2。
taint 格式为 key=value:effect。toleration 的 operator 应设为 Equal,value 也必须匹配。具体调度到哪个节点还需要另行指定。
应用 PriorityClass
创建 PriorityClass cka-high。value 为 100000,globalDefault 为 false,preemptionPolicy 为 PreemptLowerPriority。让 Pod important 使用该类。
PriorityClass 是集群作用域资源。若将 globalDefault 设为 true,会影响集群中的所有 Pod,请谨慎。Pod 中只需填写名称,spec.priority 会自动设置。
综合:在剩余节点间均匀分布
创建 Deployment evenly(副本数 4,Pod 标签 app=evenly)。topologySpreadConstraints 设置为 maxSkew 1、topologyKey topology.kubernetes.io/zone、whenUnsatisfiable DoNotSchedule、labelSelector app=evenly。同时添加 required node affinity disktype In [ssd, hdd] 和对 workload=batch:NoSchedule 的 toleration,使 4 个 Pod 在两个 zone 中按 2:2 分布。
先数一数当前集群中有多少节点能够接收 Pod。被 cordon 的节点必须排除在候选之外,带 taint 的节点只有在存在 toleration 时才能使用。缩小候选范围的关键是 node affinity。