土法炼钢兴趣小组的算法知识备份

【Kubernetes 网络深度系列】nodeSelector 与节点放置:Filter、亲和性与拓扑打散

文章导航

分类入口
kubernetesnetworking
标签入口
#nodeSelector#nodeAffinity#topologySpread#taint#toleration#kube-scheduler#Filter#Score#scheduling

目录

本系列前面的篇章跟踪的是已经落在某台节点上的包:veth、CNI、Service、NetworkPolicy。但有一类事故根本走不到抓包:Pod 长期 Pending,事件里写着 0/N nodes are available。包路径不存在,是因为调度器从未把 Pod 绑到任何节点。

本文回答一个更靠前的问题:nodeSelectornodeAffinitytopologySpreadConstraints 和污点容忍各自解决什么,kube-scheduler 在 Filter / Score 里怎么用节点标签,以及为什么「标签写对了仍然 Pending」。

它挂在网络系列末尾,不是因为调度属于数据面,而是因为:没有合法落点,就没有节点上的网络栈可讲。 排查「Service 不通」之前,应先确认 Pod 是否已经 RunningnodeName 非空。

本文依据:Kubernetes 官方文档 Assigning Pods to NodesTaints and TolerationsPod Topology Spread Constraints;调度器插件源码路径以 kubernetes/kubernetes 主线 pkg/scheduler/framework/plugins/nodeaffinity 为准。行为描述对齐官方语义;未做本机 kind 实测数字,不写吞吐/延迟 benchmark。


一、先给结论:四套旋钮各管什么

机制 作用对象 硬/软 调度器阶段(默认画像) 典型用途
spec.nodeSelector 节点标签 硬:全部 key=value 必须匹配 Filter(NodeAffinity 插件一并处理节点选择) 最简单的「只能去带某标签的节点」
affinity.nodeAffinity 节点标签 硬(required…)或软(preferred… Filter + Score 多值、操作符、In/NotIn、偏好权重
tolerations + 节点 taints 节点污点 NoSchedule 硬拒;PreferNoSchedule 软;NoExecute 还可驱逐 Filter(TaintToleration 专用节点、GPU 节点、不可调度/故障驱逐
topologySpreadConstraints 已有 Pod 在拓扑域的分布 可硬可软(whenUnsatisfiable Filter / Score(PodTopologySpread 跨 zone / hostname 打散,降相关故障

官方文档把「把 Pod 放到特定节点」的推荐入口写成:用标签选择器,而不是手写 spec.nodeName(后者绕过调度器,也绕过大部分安全与容量检查)。见 Assigning Pods to Nodes

flowchart TD
  pod[Pod 进入调度队列]
  pre[PreFilter 预处理约束]
  filter[Filter 否决不可行节点]
  score[Score 给可行节点打分]
  bind[Bind 写入 nodeName]
  pod --> pre --> filter --> score --> bind
  filter --> ns[NodeAffinity: nodeSelector 与 required affinity]
  filter --> tt[TaintToleration]
  filter --> fit[NodeResourcesFit]
  filter --> pts[PodTopologySpread]
  score --> pref[preferred nodeAffinity 权重]
  score --> other[资源打分 / 镜像本地性等]

二、nodeSelector:最简单的硬约束

2.1 语义

nodeSelector 是 PodSpec 上的一张 map[string]string。调度器只把 Pod 放到同时拥有每一个所列标签键值的节点上。文档原话:Kubernetes only schedules the Pod onto nodes that have each of the labels you specify(Assigning Pods to Nodes · nodeSelector)。

apiVersion: v1
kind: Pod
metadata:
  name: gpu-job
spec:
  nodeSelector:
    accelerator: nvidia
    topology.kubernetes.io/zone: zone-a
  containers:
  - name: train
    image: example/train:1.0

含义:节点必须同时有 accelerator=nvidia topology.kubernetes.io/zone=zone-a。任意一个缺失 → 该节点在 Filter 阶段被否决。

2.2 它解决什么、不解决什么

官方明确:nodeSelector 是最简单的推荐形式;亲和性语言更表达力强,且可以声明 soft / preferred 规则(同上文档 Affinity and anti-affinity 一节)。

2.3 与 nodeAffinity 同时出现时

若同时指定 nodeSelectornodeAffinity两者都必须满足才能调度到该节点(官方 Note)。不要假设 affinity 会「覆盖」selector。


三、nodeAffinity:硬约束 + 软偏好 + 操作符

3.1 两种时机语义

节点亲和性概念上类似 nodeSelector,但多了表达力与软规则。官方定义两类(名称里的 IgnoredDuringExecution 表示:调度完成后节点标签变化,默认不因此驱逐已运行 Pod):

  1. requiredDuringSchedulingIgnoredDuringExecution:不满足则不能调度(硬,Filter)。
  2. preferredDuringSchedulingIgnoredDuringExecution:尽量满足;找不到匹配节点仍可调度(软,Score)。

3.2 合取与析取

官方规则(必须按此理解 Pending 原因):

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: topology.kubernetes.io/zone
          operator: In
          values: ["antarctica-east1", "antarctica-west1"]
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 1
      preference:
        matchExpressions:
        - key: another-node-label-key
          operator: In
          values: ["another-node-label-value"]

3.3 调度器插件落点(A 级:源码路径)

默认调度画像里,节点选择与亲和由 NodeAffinity 插件实现:

因此搜索词里的「nodeSelector」在实现上并不是一个孤立的小函数,而是调度框架里 Filter 否决 + Score 加权 的同一插件族职责。软偏好只影响排名,不单独保证落点;集群自动扩缩容(Cluster Autoscaler)对 soft 约束的模拟也不完整——这是运维上反复踩坑的一点(见第八节开放问题)。


四、污点与容忍:排斥,不是「另一种 nodeSelector」

4.1 方向相反

官方一句话对照(Taints and Tolerations):

常见混淆:给 GPU 节点打了 accelerator=nvidia 标签并写了 nodeSelector,但节点还有 NoSchedule 污点,而 Pod 没有对应 toleration → Filter 仍失败。标签匹配与污点过滤是两条独立否决链

4.2 三种 effect

effect 调度含义 对已运行 Pod
NoSchedule 无匹配 toleration 则不调度上去 不驱逐已在节点上的 Pod
PreferNoSchedule 尽量不调度;非保证 不驱逐
NoExecute 不调度;且可驱逐已运行且不匹配的 Pod 可立即驱逐或按 tolerationSeconds

多污点 / 多容忍的处理方式像过滤器:先去掉 Pod 能容忍的污点,看剩余污点的 effect(官方文档同页)。

4.3 专用节点与特殊硬件

官方用例组合往往是:污点把无关工作挡在外面 + 标签 / affinity 把目标工作吸进去。例如专用节点:taint dedicated=groupName:NoSchedule,再给对应工作负载加 toleration,并通常再加 dedicated=groupName 标签与 affinity,避免「有 toleration 的 Pod 仍铺满整集群」。GPU 场景同理:污点保护稀缺硬件,Extended Resource + 准入控制器可自动注入 toleration(官方 Nodes with Special Hardware)。


五、topologySpreadConstraints:打散,不是选标签字典

拓扑打散约束回答的问题不同:在某个拓扑键(如 topology.kubernetes.io/zonekubernetes.io/hostname)上,带某 labelSelector 的 Pod 分布是否够均匀。 官方文档:Pod Topology Spread Constraints

nodeSelector 的对比:

nodeSelector / required affinity topologySpread
输入 「节点必须长什么样」 「同类 Pod 在拓扑域里差多少」
失败形态 没有带齐标签的节点 DoNotSchedule 时域间 skew 超标
常见目标 绑 GPU / 绑机房 降单 AZ 故障面、防堆在同一主机

whenUnsatisfiable: DoNotSchedule 是硬约束;ScheduleAnyway 是软约束(仍参与打分)。maxSkewminDomainsnodeAffinityPolicy / nodeTaintsPolicy 等字段会改变「哪些节点算进拓扑计数」——写打散规则时若同时叠了很严的 nodeSelector,可行域可能被裁成空集。


六、Filter 之后还有容量:为什么「标签对了」仍 Pending

即使 nodeSelector 与亲和、污点全部通过,NodeResourcesFit 仍会否决 CPU / 内存 / 扩展资源(如 GPU)不够的节点;默认打分常偏向剩余资源更多的节点(LeastAllocated 等策略,属 NodeResourcesFit Score 配置)。官方调度框架文档与默认插件列表见 Scheduler Configuration

排查顺序建议:

  1. kubectl describe pod:看 FailedScheduling 事件里是 node(s) didn’t match Pod’s node affinity/selectorhad untolerated taintInsufficient cpu/memory,还是 didn’t match pod topology spread constraints
  2. kubectl get nodes --show-labels:核对选择器键值是否真的存在(拼写、zone 名、自定义标签是否被 NodeRestriction 管住)。
  3. kubectl describe node:污点、Allocatable、已分配 requests。
  4. 确认没有把「软偏好」误当成硬保证;也没有同时 AND 过多硬约束把可行集打空。

七、常见踩坑清单

  1. 把污点当成标签。 kubectl taint 不会自动产生可被 nodeSelector 匹配的标签;反之亦然。
  2. nodeSelectorrequired affinity 叠得过死。 两者都要满足;再叠加 DoNotSchedule 打散,Pending 概率陡增。
  3. 信任可被 kubelet 改写的标签做隔离。 官方建议隔离场景使用 kubelet 不能修改的键;node-restriction.kubernetes.io/ 前缀需配合 Node 鉴权与 NodeRestriction 准入(Assigning Pods to Nodes · Node isolation)。
  4. 手写 spec.nodeName 绕过调度器;即使节点有 NoSchedule 污点也可能被绑上;若还有 NoExecute,kubelet 仍可能驱逐(Taints 文档说明)。
  5. 用 preferred 表达「必须」。 Score 阶段失败不会让 Pod Pending;只会落到次优节点——直到你误以为策略生效。
  6. 忽略 IgnoredDuringExecution 节点标签事后被改掉,已运行 Pod 默认不搬迁;需要驱逐要用污点 NoExecute、驱逐 API 或工作负载控制器重建。

八、谱系、争论与开放问题

谱系:早期 Kubernetes 用简单的节点选择与优先级函数;调度框架(Scheduling Framework)把 Filter / Score / Bind 等扩展点插件化后,NodeAffinityTaintTolerationPodTopologySpreadNodeResourcesFit 成为默认可组合策略。标签选择来自 Borg / Omega 一脉的「约束声明 + 中心调度」传统,但具体插件集合与拓扑打散语义是 Kubernetes 社区独立演进的结果。

工程争论

开放问题

  1. 多租户下「标签可信根」:在 NodeRestriction 与云厂商自动标签之外,如何低成本证明节点隔离标签未被控制面配置错误扩大攻击面,仍缺统一标准答案。
  2. 拓扑打散与多调度器 / 队列:在 Gang scheduling、批量作业与默认调度器并存时,skew 的全局语义如何定义,仍是插件与 out-of-tree 调度器各自为政。
  3. 动态标签(例如随硬件热插拔变化)与 IgnoredDuringExecution 的间隙:何时应自动重调度,社区倾向显式驱逐而非静默搬迁,产品化策略未收敛。

九、和本系列其余篇的关系


参考资料

规范 / 官方文档

源码

工程判断(非 A 级单独结论)

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-03 · kubernetes / networking

Kubernetes 网络深度系列

从 Linux 网络栈的第一个字节到多集群联邦网络的最后一跳——用代码、抓包和内核源码把 Kubernetes 网络讲透


By .