
两个节点都能运行同一个 Pod，A 的 CPU 请求余量更大，B 已缓存镜像，并且这个 Pod 更偏好 B 的节点标签。调度器如何在这些条件之间取舍？默认调度器会让每个适用的插件分别打分，再把分数乘上插件权重求和。CPU 余量只是其中一项；即使配置了软亲和，也可能被其他插件的分差抵消。

下文固定到 [Kubernetes v1.37.2][release]（提交 `c50e5395f1ae2d4a2c05f28fdcc38c0916cc1a32`），分析默认的 `default-scheduler` profile、v1.37 默认特性门控，以及没有外部 extender 的单 Pod 路径。完整的调度与绑定周期见[原生调度器](/posts/kubernetes-native-scheduler-scheduling-binding-cycle/)。

## 一轮打分的输入与输出

[调度入口][schedule]先从节点快照里找可行节点。资源请求超出可分配量、节点标签不符合硬约束、污点不被容忍等条件先由过滤插件处理。没有候选节点时进入失败路径；只有一个候选时直接选中，跳过完整的 `PreScore` 和 `Score`。有多个候选才执行 `prioritizeNodes`。

候选集合也不一定是集群中所有可行节点。[候选节点数量][sampling]由 `percentageOfNodesToScore` 控制，profile 设置优先于全局设置。默认值 0 表示自适应：节点数少于 100 时检查全部；更大时以 `50 - 节点数 / 125` 估算百分比，下限 5%，目标至少找到 100 个可行节点。过滤可在收集到足够候选后停止。后续最高分只在这批候选中比较。

[框架][framework]按以下顺序计算分数：

1. `PreScore` 针对当前 Pod 准备共享数据，例如解析软亲和规则或统计某个 zone 里的匹配 Pod。插件返回 `Skip` 时，同名的 `Score` 在本轮被跳过。
2. `Score` 并行遍历候选节点，每次调用接收一个节点的 `NodeInfo`。框架按插件收集所有候选节点的原始分。
3. 实现了 `NormalizeScore` 的插件调整自己的整组分数。框架检查归一化后或无需归一化的分数是否在 0–100，然后分别乘权重，再按节点求和。插件错误或分数越界会使本轮计算失败。
4. 调度器从[按总分排列的最大堆][heap]取出一个节点。堆有 `Randomizer` 作为次级比较项，但 v1.37.2 的普通打分路径只在[合并 extender 分数][extender]时为对应节点填入随机值。没有 extender 时，默认路径没有按节点名排序，也没有承诺在同分节点间均匀随机。

令 $C$ 为本轮候选节点集合，$P$ 为当前 profile 启用且本轮没有跳过的打分插件。对候选节点 $n$，$s_p(n)$ 是插件 $p$ 最终交给框架的 0–100 分，$W_p$ 是该插件的 profile 权重。没有 extender 时，整体公式是：

$$
\begin{aligned}
T(n)&=\sum_{p\in P} W_p\,s_p(n),\\
n^*&\in\operatorname*{arg\,max}_{n\in C} T(n).
\end{aligned}
$$

框架直接求和，不除以权重之和，也不再对总分做归一化。把跳过的项按贡献 0 记账，代入默认 profile 的九个权重，就得到：

$$
\begin{aligned}
T(n)={}&3s_{\mathrm{Taint}}+2s_{\mathrm{NodeAffinity}}\\
      &+s_{\mathrm{Fit}}+s_{\mathrm{Volume}}\\
      &+2s_{\mathrm{Spread}}+2s_{\mathrm{InterPodAffinity}}\\
      &+2s_{\mathrm{DRA}}+s_{\mathrm{Balanced}}+s_{\mathrm{Image}}.
\end{aligned}
$$

如配置了参与打分的 extender，还要为该节点加上各 extender 的 `返回分 × extender 权重 × (100 / 10)`；10 是 [extender API 的分值上限][extender-scale]。下文算例不使用 extender。

因此这里有三种不同的权重：Pod 软亲和条目的权重是插件输入；`NodeResourcesFit` 的资源权重用于合并 CPU、内存等分数；profile 的插件权重才用于合并各插件的分数。把软亲和条目的权重从 50 改成 100，如果该插件只有这一个条目，且候选中有匹配节点，归一化之后匹配节点仍是 100 分。

另一个容易漏掉的分支是默认启用的 [`OpportunisticBatching`][batch]。条件相符的连续 Pod 可以利用上轮候选排序：批处理逻辑检查缓存是否适用并刷新上轮选中节点的状态，给当前轮一个节点提示；提示通过相关过滤后，可走单候选路径。这是受条件限制的快路径，并非每个 Pod 都会重新打印所有节点的分项分数。

## 默认打分插件看什么

[默认配置][defaults]把插件声明在 `MultiPoint` 下。框架按插件实现把它挂到相应扩展点；只有实现 `Score` 的插件才参与下面的打分。没有显式设置插件权重的 `VolumeBinding` 使用 [默认权重 1][weight-default]。表中的权重适用于默认 profile。下面各节的 $r(n)$ 表示插件原始分，$s(n)$ 表示乘插件权重之前的分数。

| 插件 | 权重 | 依据 |
| --- | ---: | --- |
| [`TaintToleration`][taint] | 3 | 不被容忍的 `PreferNoSchedule` 污点数 |
| [`NodeAffinity`][node-affinity] | 2 | 节点标签匹配的软亲和条目 |
| [`NodeResourcesFit`][fit] | 1 | 放入 Pod 后的请求余量，默认 CPU、内存 |
| [`VolumeBinding`][volume] | 1 | 可绑定或可供给存储的容量 |
| [`PodTopologySpread`][spread] | 2 | hostname、zone 等域内的匹配 Pod 数 |
| [`InterPodAffinity`][inter] | 2 | 拓扑域内其他 Pod 的标签及亲和规则 |
| [`DynamicResources`][dra] | 2 | DRA claim 的设备子请求优先顺序 |
| [`NodeResourcesBalancedAllocation`][balance] | 1 | 放入 Pod 前后资源比例的均衡变化 |
| [`ImageLocality`][image] | 1 | 候选节点已缓存镜像的大小及其分布 |

### TaintToleration：未容忍的软污点越多，分数越低

节点上的 `PreferNoSchedule` 污点表示「尽量别把不容忍这个污点的 Pod 放过来」。插件把节点的软污点和 Pod 的 tolerations 逐一比对。每多一个未容忍的污点，节点的原始计数就加一，最后反过来给分：计数越少，分数越高。容忍一个污点只是让它不计入；所有候选都没有未容忍的软污点时，这个插件不会改变排名。

把未容忍的污点数记作 $r(n)$，候选中的最大计数记作 $M$。$M=0$ 时所有节点得 100 分；否则[反向归一化][normalize]：

$$
s(n)=100-\left\lfloor 100r(n)/M \right\rfloor.
$$

这里先做整数除法，再用 100 减去结果。比如未容忍数为 0、1、3 时，分别得到 100、67、0 分，乘权重 3 后是 300、201、0。`NoSchedule`、`NoExecute` 的硬性检查在 Filter 阶段完成。见 [TaintToleration 源码][taint]。

### NodeAffinity：匹配节点标签的软亲和条目加分

一个 Pod 可以偏好有 SSD 的节点，同时对某个可用区也有偏好。`NodeAffinity` 用节点标签逐条检查这些偏好，匹配一条就加上这条的权重。这样，一条更重要的偏好可以比几条次要偏好加起来还值钱。所有偏好都是软条件：没匹配的节点仍能参选，只是少拿分。换算到 0–100 分时，匹配权重之和最高的候选拿满分，其他候选按比例给分。

`PreScore` 解析 Pod 的 `preferredDuringSchedulingIgnoredDuringExecution`；profile 的 `addedAffinity` 如果也配置了软偏好，`Score` 会一起累计。把两组命中条目的权重之和记作 $r(n)$，候选中的最大值记作 $M$：

$$
s(n)=
\begin{cases}
0, & M=0,\\
\left\lfloor 100r(n)/M \right\rfloor, & M>0.
\end{cases}
$$

例如 A 匹配权重为 80 的条目，B 只匹配权重为 30 的条目，得到 100 和 37 分；乘插件权重 2 后是 200 和 74。没有可用的软亲和条目时，`PreScore` 会跳过该插件。`nodeSelector` 和 required node affinity 仍由 Filter 判断。见 [NodeAffinity 源码][node-affinity]。

### NodeResourcesFit：按放入 Pod 后的请求余量打分

先假设把新 Pod 放上去，再看节点的 CPU 和内存各剩多少可分配份额。默认的 [LeastAllocated][resource-defaults] 偏好余量比例更高的节点，避免在其他条件相近时继续把请求压到更满的节点上。它比较的是请求占预算的比例，所以不能只看节点还剩几个 CPU 核，也不能从监控上的实时使用率推断这项分数。默认 CPU 和内存权重相同。

对每种资源 $i$，$C_i$ 是节点的 `allocatable`，$R_i$ 是节点已有请求与待调度 Pod 请求之和，$w_i$ 是配置中的资源权重。先算每种资源的余量分 $q_i$，再取加权平均：

$$
\begin{aligned}
q_i &= \left\lfloor 100\max(0,C_i-R_i)/C_i \right\rfloor,\\
s(n)&=\left\lfloor \frac{\sum_i w_i q_i}{\sum_i w_i}\right\rfloor.
\end{aligned}
$$

只对纳入评分且 $C_i>0$ 的资源求和；没有可计入的资源时得 0。两层除法分别取整。比如放入 Pod 后 CPU 还剩 50%、内存还剩 25%，插件分是 37，乘默认插件权重 1 后仍是 37。CPU 和内存的[打分路径][allocation]会为缺少请求的容器使用非零估算，常量是 100m 和 200 MiB；这个估算不会修改 Pod，也不代表 Filter 使用同一个数。见 [LeastAllocated 源码][fit]。

改用 [MostAllocated][most] 时，单项变为 $\lfloor 100\min(R_i,C_i)/C_i\rfloor$，仍按资源权重取整平均。[RequestedToCapacityRatio][ratio] 则先算整数利用率，在配置的分段线性曲线上取分，再对正分项做加权平均并四舍五入。这两种策略均需要配置，不属于默认打分。

### NodeResourcesBalancedAllocation：奖励放入 Pod 后的均衡改善

有的节点已经用了大半 CPU 请求预算，内存请求却占得很少。一个相对更吃内存的 Pod 放上去，可能正好让 CPU 和内存的请求比例更接近；一个相对更吃 CPU 的 Pod 则可能拉大差距。这个插件比较的是同一节点放入前后的变化：差距缩小就加分，差距拉大就减分。原本比较空闲、或者原本很均衡的节点，并不会因此自动拿高分。

这个插件用实际请求，不使用 `NodeResourcesFit` 的非零估算。对每种可分配量大于 0 的资源，计算请求比例 $f_i=\min(R_i/C_i,1)$。令 $\bar f$ 为这些比例的均值，$\sigma$ 为标准差。先计算放入前的均衡度 $B_0$ 和放入后的均衡度 $B_1$，再用差值打分：

$$
\begin{aligned}
\sigma &= \sqrt{\operatorname{mean}((f_i-\bar f)^2)},\\
B(f) &= \left\lfloor 100(1-\sigma)\right\rfloor,\\
s(n)&=50+\left\lfloor (50+B_1-B_0)/2 \right\rfloor.
\end{aligned}
$$

CPU 和内存两项的标准差就是两比例差的绝对值除以 2；零项或一项时，代码中的标准差为 0。均衡度不变时得 75 分；改善会加分，恶化会减分。待调度 Pod 对参与评分的资源均无请求时，`PreScore` 跳过。见 [BalancedAllocation 源码][balance]。

假设 A、B 都可分配 8 CPU、8 GiB；已有请求分别是 A 的 4 CPU、2 GiB，B 的 2 CPU、2 GiB；新 Pod 请求 2 CPU、4 GiB。只比较这两个插件：

| 节点 | 放入后的请求 | 余量分 | 均衡分 | 合计 |
| --- | --- | ---: | ---: | ---: |
| A | 6 CPU、6 GiB | 25 | 81（87 → 100） | 106 |
| B | 4 CPU、6 GiB | 37 | 68（100 → 87） | 105 |

B 的余量更大，A 却以 1 分领先：这个 Pod 让 A 的 CPU 和内存请求比例更接近。两节点都必须先通过资源过滤。

### InterPodAffinity：把 Pod 的亲和关系换算成拓扑域分数

如果新 Pod 希望和一组已有 Pod 在同一个可用区，插件先用亲和规则找出匹配的已有 Pod，再看它们所在节点的可用区标签。候选节点只要位于同一区，就能拿到这部分加分，即使这些已有 Pod 不在候选节点上。软反亲和做相反的事：匹配的已有 Pod 越多，所在域被扣的分越多。插件把这些关系累加起来，再按本轮候选的最高和最低原始分换算；候选之间没有差别时，这项不会改变排名。

`PreScore` 扫描相关已有 Pod。每当某条规则匹配，就把它的权重记到对应的 `(topologyKey, 拓扑值)`：软亲和取正，软反亲和取负。待调度 Pod 指向已有 Pod 的规则会计入；按插件配置，也会计入已有 Pod 指向它的规则。已有 Pod 的 required affinity 命中时可额外加 `hardPodAffinityWeight`（默认 1）。`Score` 用候选节点自己的拓扑标签取出这些累积值并相加，得到原始分 $r(n)$，可以为负。

令 $m$、$M$ 分别为候选原始分的最小值和最大值。$M=m$ 时所有节点得 0 分；否则：

$$
s(n)=\left\lfloor \frac{100(r(n)-m)}{M-m} \right\rfloor.
$$

源码先用 `float64` 算比例、乘 100，再截断为整数。例如三个候选原始分为 −20、0、30，换算后是 0、40、100；加权后是 0、80、200。相同 zone 的候选会共享该 zone 的累积值，但还可能因 hostname 等其他拓扑键而得分不同。见 [InterPodAffinity 源码][inter]。

### PodTopologySpread：统计域内副本，再反向换算

要把同一组副本分散到两个可用区，插件先数每个区里已有多少个匹配 selector 的 Pod。一个区副本多、另一个区副本少时，通常给副本少的区里的候选节点更高的分，这样新副本更有机会补到少的一侧。如果同时按节点和可用区分散，插件会把两种尺度的统计合起来，不能只看某台机器上有几个副本。`ScheduleAnyway` 表达这种偏好，其他打分项仍可改变落点；`DoNotSchedule` 的偏斜限制在 Filter 阶段处理。

对每条软约束 $c$，$N_c(n)$ 是候选节点所属域内、同命名空间且匹配 selector 的已有 Pod 数，$D_c$ 是候选节点覆盖的域数量。hostname 用未被忽略的候选节点数作 $D_c$，并在 `Score` 时统计该节点上的 Pod；其他拓扑域在 `PreScore` 聚合，计数时可纳入同域内的非候选节点，具体受节点纳入策略约束。

令 $m_c$ 为这条约束的 `maxSkew`。每个候选先算各条有对应拓扑标签的约束分 $v_c$，再求和并四舍五入：

$$
\begin{aligned}
v_c(n)&=N_c(n)\ln(D_c+2)+m_c-1,\\
r(n)&=\operatorname{round}\left(\sum_c v_c(n)\right).
\end{aligned}
$$

用未被忽略的候选原始分的最小值 $m$、最大值 $M$ 做反向换算。因缺失必要拓扑标签被忽略的候选得 0；其余候选在 $M=0$ 时得 100，否则：

$$
s(n)=\left\lfloor \frac{100(M+m-r(n))}{M}\right\rfloor.
$$

例如只有一条 `maxSkew: 1` 的 hostname 软约束，两个候选分别已有 1 个和 4 个匹配 Pod，$D_c=2$，原始分为 1 和 6，换算后是 100 和 16。较拥挤的节点仍可能得分；这里的分母是 $M$，不是 $M-m$。见 [PreScore 与归一化][spread]、[拓扑权重与单项公式][spread-count]。

没有显式约束时，[系统默认值][spread-defaults]是 hostname 的 `maxSkew: 3`、zone 的 `maxSkew: 5`，均为 `ScheduleAnyway`。推不出匹配 selector 时，例如没有匹配 Service 或受支持控制器选择器的裸 Pod，插件跳过这组默认规则。

### ImageLocality：按命中镜像的大小和分布打分

节点已经缓存了 Pod 要用的镜像，可能省掉一次下载；缓存的大镜像比小镜像更值钱。不过，某个镜像只缓存于极少数节点时，插件会压低这项加分，避免大量使用同一镜像的 Pod 都被少数有缓存的节点吸引。因此它既看本节点命中了什么，也看这些镜像在集群快照里有多普遍。

`Score` 从节点快照查找普通容器、init 容器和镜像卷所需的镜像。每个命中项 $j$ 的贡献是 $\lfloor \mathrm{size}_j \times K_j/K \rfloor$：$K_j$ 是快照中报告有该镜像的节点数，$K$ 是插件读取的快照节点总数。未命中项贡献 0。累加得到 $X$，令 $I$ 为 Pod 的镜像项数（包括未命中项），$L=23\,\mathrm{MiB}$，$H=1000\,\mathrm{MiB}\times I$，则：

$$
s(n)=\left\lfloor\frac{100(\operatorname{clamp}(X,L,H)-L)}{H-L}\right\rfloor.
$$

一个只用 200 MiB 镜像的 Pod，若一半节点有缓存，命中节点的 $X$ 是 100 MiB，得 7 分；未命中节点得 0。插件不再做跨节点归一化；命中也不保证 kubelet 不拉取镜像。见 [ImageLocality 源码][image]。

### VolumeBinding：按可用卷方案计算容量分

两个节点都能满足 Pod 的卷请求时，默认曲线偏好这批请求占容量比例更低的卷方案。同一个 StorageClass 下，同样的请求放到容量更充裕的方案里，占用的比例更小，因而得分更高。插件先分 StorageClass 算占比，再平均各类的分数：一种存储类容量再大，也不能替另一种存储类承接请求。

Filter 先判断 PVC 在节点上能否绑定或供给。`Score` 用该节点的卷方案：有静态绑定时按 StorageClass 汇总 PVC 请求与对应 PV 容量；没有静态绑定时才读取动态供给的容量信息。同一类的多个动态 PVC 叠加请求，共用的容量不会按 PVC 数重复相加。没有可计入的卷方案或类时得 0。v1.37 的 [StorageCapacityScoring][features] 默认开启；关闭时评分器不启用。

对每个有数据的类 $c$，$R_c$ 为请求总量，$C_c$ 为容量。先算整数利用率 $u_c=\lfloor100R_c/C_c\rfloor$；$C_c=0$ 或请求超过容量时用 100。默认曲线为 $g(u)=100-u$，即 0% 对应 100 分，100% 对应 0 分。各类等权，$J$ 为纳入计算的 StorageClass 数量：

$$
s(n)=\operatorname{round}\left(\frac{\sum_c g(u_c)}{J}\right).
$$

例如两类的利用率为 33% 和 80%，类得分为 67 和 20，平均 43.5，最终得 44。自定义 `shape` 时，配置分数从 0–10 放大到 0–100，按利用率做分段线性插值。这里使用 PV 或 CSI 容量信息，不读节点磁盘实时空闲量。见 [VolumeBinding 取数][volume]、[默认曲线][volume-default]及[容量打分器][volume-scorer]。

### DynamicResources：设备分配越接近首选，分数越高

Pod 可以通过 `ResourceClaim` 给出一组有先后顺序的设备请求：先试首选，做不到再尝试后备选项。如果两个节点都能完成分配，一个能满足首选，另一个只能满足后备选项，这条请求会给前者更多分。这样后备设备仍然可用，用户声明的优先顺序也会反映在打分上。比较依据是每个节点实际能得到的分配方案，空闲设备多本身不会加分。

动态资源分配（DRA）的 `Score` 读取候选节点的 `ResourceClaim` 分配结果。令 $A(n)$ 为各 claim 的 `firstAvailable` 列表中实际分配到的子请求集合。每个子请求 $q$ 按它在所属列表中的下标 $i_q$ 计分，下标从 0 开始；8 是该版本允许的[列表最大长度][dra-limit]，不是实际列表长度。同一个子请求匹配多台设备也只计一次；`exactly` 请求不贡献这项偏好分。

$$
r(n)=\sum_{q\in A(n)} (8-i_q).
$$

令 $M$ 为所有候选的最高原始分。$M=0$ 时得 0 分，否则 $s(n)=\lfloor 100r(n)/M \rfloor$。比如 A 分配到首选、B 分配到第三选，原始分为 8 和 6，换算后是 100 和 75，加权后是 200 和 150。没有 claim、没有可计分分配，或优先列表打分不可用时，这项不产生偏好。它不读取 GPU 的实时利用率。见 [DynamicResources 源码][dra]。

## 实测：软污点抵消节点亲和

在本机隔离 kind 控制面里，API Server 和原版 `kube-scheduler` 都是 v1.37.2。实验在 API 中创建两个同容量的合成节点 A、B，只给 A 加 `PreferNoSchedule` 污点。两个 Pod 都用软亲和偏好 A；第一个容忍该污点，第二个不容忍。合成节点不运行 kubelet，因此只观察调度器日志和 Pod 的 `spec.nodeName`，不验证容器启动。

| Pod | A 总分 | B 总分 | 实际选中 |
| --- | ---: | ---: | --- |
| 容忍 A 的软污点 | 597 | 397 | A |
| 不容忍 A 的软污点 | 296 | 397 | B |

把第二个 Pod 的分项还原成乘权重前的分数，可以逐项复算总分：

| 插件 | A 分项 | B 分项 |
| --- | ---: | ---: |
| `TaintToleration` | 0 × 3 | 100 × 3 |
| `NodeAffinity` | 100 × 2 | 0 × 2 |
| `NodeResourcesFit` | 96 × 1 | 97 × 1 |
| `VolumeBinding`、`DynamicResources`、`ImageLocality` | 都是 0 | 都是 0 |
| `PodTopologySpread`、`InterPodAffinity`、`NodeResourcesBalancedAllocation` | 跳过 | 跳过 |
| 总分 | 0 + 200 + 96 = 296 | 300 + 0 + 97 = 397 |

两个候选都通过 Filter；B 领先来自 300 分的软污点分差超过了 A 的 200 分节点亲和优势。第一个 Pod 容忍污点，`TaintToleration` 在两边同得 300 分，`NodeAffinity` 才把 A 推到第一。第一轮资源分两边都是 97；第二轮 A 上已经计入第一个 Pod，虽然它没显式填写 requests，非零估算仍使资源分从 97 变为 96。

复现步骤、脚本与原始分项日志见 [k8sdev 打分实验](https://github.com/jimyag/k8sdev/tree/c23138bb7ebed5921dade14392606f690a9524f0/article-default-scheduler-scoring)。另外运行了 [v1.37.2 上游集成测试][integration]中的资源、节点亲和、污点三个用例组，以及默认配置、调度框架和八个相关插件包的测试。上面的 kind 对照验证的是两条默认偏好叠加后的落点；卷和 DRA 的非零分支以源码和上游测试为依据。

## 想改变落点，先确认改的是哪一层

- 节点不能运行这个 Pod：检查 Filter 的失败原因。需要隔离、硬件必需或存储拓扑限制时，配置硬约束，别试图用高分代替。
- 希望把请求集中到少数节点：评估 `NodeResourcesFit.scoringStrategy` 的 `MostAllocated` 或 `RequestedToCapacityRatio`，并单独考虑 `NodeResourcesBalancedAllocation`、拓扑分布等仍生效的偏好。
- 只是希望某条偏好赢过另一条：检查实际生效的 `KubeSchedulerConfiguration` 和 Pod 的 `spec.schedulerName`。用 profile 的 `plugins.score` 调整打分权重时，先确认未误关同一插件的过滤能力。
- 有多个可行节点却没选到预期节点：先看候选集合和 profile，再读调度器高日志级别下的 `Plugin scored node for pod`。日志里的分数已经乘过插件权重，200 或 300 都是正常值。不能从 Kubernetes Event 反推出所有分项；日志里没有分项时也要检查单候选、插件跳过和批处理快路径。

[release]: https://github.com/kubernetes/kubernetes/releases/tag/v1.37.2
[schedule]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/schedule_one.go#L572-L627
[sampling]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/schedule_one.go#L866-L892
[framework]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/runtime/framework.go#L1354-L1583
[heap]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/sorted_nodes.go#L31-L64
[extender]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/schedule_one.go#L1038-L1053
[extender-scale]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/staging/src/k8s.io/kube-scheduler/extender/v1/types.go#L24-L31
[batch]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/runtime/batch.go
[defaults]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/apis/config/v1/default_plugins.go#L32-L94
[weight-default]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/runtime/framework.go#L589-L617
[resource-defaults]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/apis/config/v1/defaults.go#L231-L247
[allocation]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/noderesources/resource_allocation.go#L165-L260
[fit]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/noderesources/least_allocated.go
[most]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/noderesources/most_allocated.go
[ratio]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/noderesources/requested_to_capacity_ratio.go
[balance]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/noderesources/balanced_allocation.go#L204-L254
[taint]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/tainttoleration/taint_toleration.go#L178-L214
[node-affinity]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/nodeaffinity/node_affinity.go#L250-L299
[normalize]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/helper/normalize_score.go
[spread]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go#L118-L267
[spread-count]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/podtopologyspread/scoring.go#L291-L307
[spread-defaults]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/podtopologyspread/plugin.go#L46-L57
[inter]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/interpodaffinity/scoring.go
[image]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/imagelocality/image_locality.go
[volume]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/volumebinding/volume_binding.go#L453-L520
[volume-default]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/apis/config/v1/defaults.go#L194-L210
[volume-scorer]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/volumebinding/scorer.go#L29-L54
[dra]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources.go#L1386-L1470
[dra-limit]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/staging/src/k8s.io/api/resource/v1/types.go#L1654-L1657
[features]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/pkg/features/kube_features.go#L2195-L2198
[integration]: https://github.com/kubernetes/kubernetes/blob/v1.37.2/test/integration/scheduler/scoring/priorities_test.go

