Repository Reading Site
高级调度 — 控制 Pod 去哪里
**实验结果:** **原理:** 硬约束。不匹配的节点直接排除,无商量余地。 **required vs preferred:** | 类型 | 含义 | 不满足时 | |------|------|---------| | `required` | 硬约束,必须满足 | Pod 永远 Pending | | `preferred` | 软约束,尽量满足
高级调度 — 控制 Pod 去哪里
调度器做决策的完整流程
Pod 创建 → Scheduler Watch 到
→ 过滤(哪些节点不行)
→ 打分(剩下的哪个最好)
→ 绑定(Pod → Node)
→ kubelet 拉起容器
四种调度控制手段
1. nodeSelector — 最简单的约束
spec:
nodeSelector:
topology.kubernetes.io/region: la # 只去 LA 节点
实验结果:
la-onlyPod → 调度到us590068728056(LA)hk-onlyPod → 调度到wk-1(HK)
原理: 硬约束。不匹配的节点直接排除,无商量余地。
2. nodeAffinity — 更灵活的节点选择
spec:
affinity:
nodeAffinity:
# 硬约束:必须不是 Master
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: DoesNotExist
# 软约束:优先选 LA(但 HK 也行)
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: topology.kubernetes.io/region
operator: In
values: ["la"]
required vs preferred:
| 类型 | 含义 | 不满足时 |
|---|---|---|
required |
硬约束,必须满足 | Pod 永远 Pending |
preferred |
软约束,尽量满足 | 调度到其他节点 |
operator 支持: In, NotIn, Exists, DoesNotExist, Gt, Lt
3. TopologySpreadConstraints — 跨拓扑域均匀分布
spec:
topologySpreadConstraints:
- maxSkew: 1 # 最大偏差
topologyKey: topology.kubernetes.io/region # 按什么维度分散
whenUnsatisfiable: DoNotSchedule # 不满足时:拒绝调度
labelSelector:
matchLabels:
app: spread-demo
实验结果(6 副本,maxSkew=1,按 region 分散):
LA: 3 个 Pod(cp-3 × 1, us590068728056 × 2)
HK: 3 个 Pod(wk-1 × 3)
偏差: |3-3| = 0 ≤ 1 ✓
maxSkew 的含义: 任意两个拓扑域的 Pod 数量差不能超过 maxSkew。如果设为 2,则允许 LA:4 HK:2 这样的不均匀分布。
whenUnsatisfiable 两种策略:
DoNotSchedule— 不满足就不调度(硬约束)ScheduleAnyway— 尽量满足但不强制(软约束)
面试深度题: "TopologySpreadConstraints 和 podAntiAffinity 的区别?"
- podAntiAffinity: "我不要和同类 Pod 在同一个节点/zone"(二元:同/不同)
- TopologySpread: "我要均匀分散,偏差不超过 N"(量化:允许多大偏差)
4. Taint/Toleration — "节点赶人"
Taint 是节点上的标记:"我有特殊情况,普通 Pod 别来"。 Toleration 是 Pod 上的声明:"我能容忍你的特殊情况"。
# 给节点加 Taint
kubectl taint nodes hk652699382121 region=hk:NoSchedule
# Pod 需要对应的 Toleration 才能调度上去
spec:
tolerations:
- key: "region"
operator: "Equal"
value: "hk"
effect: "NoSchedule"
三种 effect:
| Effect | 含义 |
|---|---|
NoSchedule |
新 Pod 不调度上来,已有 Pod 不影响 |
PreferNoSchedule |
尽量不调度,但没其他选择时可以 |
NoExecute |
新 Pod 不调度,已有 Pod 被驱逐 |
Master 为什么默认不跑业务 Pod? 因为 kubeadm 自动给 Master 加了 Taint:
node-role.kubernetes.io/control-plane:NoSchedule
模拟 GPU 调度
原理:Extended Resources
K8s 的 GPU 调度不是什么黑魔法——它使用 Extended Resources 机制:
- 设备插件(Device Plugin)向 kubelet 注册资源(如
nvidia.com/gpu: 2) - kubelet 上报给 API Server,写入 Node 的
status.capacity - Pod 声明
resources.limits: nvidia.com/gpu: 1 - Scheduler 像分配 CPU/内存一样分配 GPU
实验:手动注册 fake GPU
# 通过 kubectl proxy + PATCH API 给节点注册 GPU
curl --request PATCH \
--data '[{"op":"add","path":"/status/capacity/nvidia.com~1gpu","value":"2"}]' \
http://localhost:8099/api/v1/nodes/wk-1/status
实验结果:
| Pod | 请求 GPU | 调度到 | 原因 |
|---|---|---|---|
| gpu-job-1 | 1 | hk652699382121 | 有 1 GPU |
| gpu-job-2gpu | 2 | wk-1 | 唯一有 2 GPU 的节点 |
| gpu-job-impossible | 5 | (Pending) | 无节点有 5 GPU |
面试重点:GPU 不能超配。 CPU 有 requests/limits 之分(limits 可以大于 requests,允许 burst),但 GPU 只有 limits,且 limits = requests,不允许超配。原因:GPU 不像 CPU 可以时间分片,一个进程占了 GPU 就独占了。