3.2 过滤与打分:调度器的两段式选址


3.2 过滤与打分:调度器的两段式选址

Kubernetes 调度器为每个待调度的 Pod 执行两段式决策:预选(Predicates,过滤掉不满足硬性条件的节点)与优选(Priorities,给幸存节点打分排序),最终把得分最高者写进 Pod 的 nodeName 字段完成绑定。选址失败则 Pod 停留 Pending 并持续重试。

资源报价单已就位,这一节看调度器怎么花掉它。上一节的实验里 Pod 卡在 Pending,本节把决策过程完整展开:哪些条件会一票否决、打分偏好什么、以及 affinity 与 taint 两类精细控制怎么用。学完这节,Pending 不再是黑箱,而是一条可以逐项核对的证据链。

两段式决策现场

预选阶段逐项检查硬条件,任何一项不过即淘汰该节点。常见否决项按检查顺序:

  • 资源够不够:节点可分配量减去已申报量,能否容纳新 Pod 的 requests(上一节的主场);
  • 端口冲不冲突:hostNetwork 类 Pod 要独占节点端口,撞了就出局;
  • 卷能不能挂:节点是否在所需 PV 的拓扑域内、卷是否已被独占模式占用;
  • 亲和性满不满足:nodeSelector 与节点亲和的硬性要求;
  • 污点忍不忍得了:节点带污点而 Pod 无相应容忍,直接排除。

优选阶段只对幸存者打分,常见打分项:资源均衡度(倾向选分配后剩余比例更均衡的节点,避免堆叠)、镜像本地缓存(已有所需镜像的节点加分,启动更快)、反亲和命中(副本尽量散开)、拓扑打散。两段式的设计逻辑一句话:预选保证正确性,优选优化质量——先淘汰所有不可行解,再在可行解里挑更优解。

调度器自己不执行任何操作,只往账本写一个字段:

# 看调度器为 Pod 写回的绑定结论 kubectl get pod orders-api-6d9f7c8b5-k8w2t -n production -o jsonpath='{.spec.nodeName}' # node-c # 绑定事件也记录在案 kubectl describe pod orders-api-6d9f7c8b5-k8w2t -n production | grep -A1 Scheduled # Events: # Normal Scheduled 8m default-scheduler # Successfully assigned production/orders-api-... to node-c

精细控制之一:亲和性

nodeSelector 是最朴素的定向手段:给节点打标签,Pod 声明"只去带某标签的节点"。它只有硬性匹配一种语义,而亲和性(affinity)提供软硬两档,还支持按"和其他 Pod 的关系"选址:

spec: template: spec: affinity: podAntiAffinity: # 反亲和:副本之间互相排斥 preferredDuringSchedulingIgnoredDuringExecution: # 软性:尽量满足 - weight: 100 podAffinityTerm: topologyKey: kubernetes.io/hostname # 以节点为拓扑域 labelSelector: matchLabels: app: orders-api # 条件:同应用的其他副本 nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: node-type # 硬性:必须落在计算型节点 operator: In values: ["compute"]

这份声明的意图是:orders-api 的 3 个副本尽量分散在不同节点(软性,权重 100),且必须落在计算型节点(硬性)。调度时段(DuringScheduling)生效、运行时段(IgnoredDuringExecution)不强行迁移——软性偏好不满足也照常调度,硬性要求不满足则宁 Pending 不将就。名字很拗口,但语义就这两句。

精细控制之二:污点与容忍

亲和性是 Pod 挑节点,污点(taint)反其道而行:节点给 Pod 设门槛。控制平面节点就是靠污点保持清静的:

# 看控制平面节点自带的污点 kubectl describe node control-1 | grep Taints # Taints: node-role.kubernetes.io/control-plane:NoSchedule # 语义:不容忍此污点的 Pod 一律不许调度进来 # 常见系统污点(节点异常时自动打上): # node.kubernetes.io/not-ready 节点未就绪 # node.kubernetes.io/disk-pressure 磁盘压力 # node.kubernetes.io/memory-pressure 内存压力

Pod 想进门就得带对应的容忍(tolerations)。给一个可漂在大数据节点的日志伴随 workload 声明容忍:

spec: template: spec: tolerations: - key: "dedicated" operator: "Equal" value: "batch" # 精确匹配 dedicated=batch 的污点 effect: "NoSchedule" # 容忍其"不许调度"效果

污点三要素 key、value、effect 里,effect 决定后果:NoSchedule 拒新来者、NoExecute 连存量都驱逐、PreferNoSchedule 尽量别来。带 NoExecute 容忍还可以加 tolerationSeconds,表示"驱逐前宽限多久"——数据库类 Pod 常借此在网络抖动时争取缓冲。

案例:一次"只上两台节点"的反常分布

背景:orders-api 扩到 5 副本后,运维发现副本全部集中在 node-a 与 node-b,node-c 始终空着,怀疑调度器坏了。

操作:按证据链排查——

# 第一步:看 node-c 是否带污点 kubectl describe node node-c | grep -A2 Taints # Taints: dedicated=batch:NoSchedule # 命中:node-c 被打上了批处理专用污点 # 第二步:看 Pod 是否声明了容忍(检查声明文件后确认没有) # 第三步:看副本分布现状 kubectl get pods -n production -l app=orders-api -o wide # 5 个副本全部落在 node-a 与 node-b

结果:调度器没有坏。node-c 某天被平台组打了批处理专用污点,orders-api 不带容忍,预选阶段直接出局,5 个副本只能在剩下两台里挤着。

解读:反常分布几乎总有一个可查的标签或污点。排查顺序固定:先 Taints 再 Affinity 再资源账面,三步之内必有答案。这里的选择是给 node-c 去污点(如果它本该通用),或给 orders-api 补容忍(如果它被允许借用批处理节点)。

变式:若诉求是"控制面留一台给系统组件、其余节点给业务",标准做法就是保留控制面的默认污点、给监控代理等系统级 DaemonSet 声明容忍——第4章的工作负载图谱会再次用到这个组合。

本节要点回顾

  • 两段式:预选淘汰不可行(正确性),优选打分挑更优(质量),结论只是一个写回账本的 nodeName;
  • Pending 有证据链:describe 的事件段会写明否决原因与数量,按 Insufficient 之类的关键词定位;
  • 亲和性双向可用:软性 preferred 尽量满足,硬性 required 宁等不将就;反亲和用来打散副本;
  • 污点是节点的门槛:NoSchedule 拒新、NoExecute 逐旧,容忍是 Pod 的通行证;
  • 反常分布查三样:污点、亲和、资源账面,顺序固定,答案必在其中。

选址完成、nodeName 已写入账本。下一节跟进到节点现场:kubelet 如何认领任务,容器运行时如何把一行镜像声明变成运行中的进程。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U