摘要:理解 Scheduler 的筛选、评分与绑定流程,掌握 nodeSelector、节点与 Pod 亲和性、拓扑约束、优先级和调度故障排查。
1. Kubernetes 调度架构
1. 什么是 Scheduler?
Scheduler 是 Kubernetes 控制平面的核心组件之一。
它的主要职责是:
为尚未分配节点的 Pod 选择合适的 Node。
Kubernetes 调度器通常运行在 Control Plane 节点上。
核心流程:
flowchart TB
N0["用户创建 Pod"]
N1["API Server"]
N2["etcd 保存状态"]
N3["Scheduler 监听 Pod"]
N4["筛选符合条件的 Node"]
N5["对 Node 评分"]
N6["选择最合适的 Node"]
N7["绑定 Pod 与 Node"]
N8["kubelet 监听 Pod"]
N9["拉取镜像并启动容器"]
N0 --> N1
N1 --> N2
N2 --> N3
N3 --> N4
N4 --> N5
N5 --> N6
N6 --> N7
N7 --> N8
N8 --> N9
这里有几个重要组件:
| 组件 | 职责 |
|---|---|
| API Server | 接收和管理 Kubernetes API 请求 |
| etcd | 保存集群状态数据 |
| Scheduler | 为 Pod 选择 Node |
| kubelet | 在节点上管理 Pod |
| Container Runtime | 实际运行容器 |
注意:
Scheduler 负责选择节点,但不负责真正启动容器。
容器的启动工作由目标节点上的 kubelet 和容器运行时完成。
2. Pod 为什么会处于 Pending?
创建 Pod 后:
kubectl get pods可能看到:
NAME READY STATUSnginx 0/1 PendingPending 不一定代表调度失败。
Pod 从创建到正常运行,可能经历:
flowchart TB
N0["Pending"]
N1["等待调度"]
N2["分配 Node"]
N3["拉取镜像"]
N4["创建容器"]
N5["Running"]
N0 --> N1
N1 --> N2
N2 --> N3
N3 --> N4
N4 --> N5
如果无法找到合适节点,就可能一直处于 Pending。
查看原因:
kubectl describe pod nginx重点关注:
Events: Warning FailedScheduling例如:
0/5 nodes are available:2 Insufficient cpu,3 node(s) didn't match Pod's node affinity/selector.表示:
- 2 个节点 CPU 资源不足。
- 3 个节点不满足节点选择条件。
2. Scheduler 调度过程:Filter 与 Score
Kubernetes Scheduler 不是简单随机选择节点,而是经过一系列调度插件处理。
核心可以分为两个阶段:
- Filter:过滤不符合要求的节点。
- Score:给符合要求的节点评分。
1. Filter 阶段
假设集群有 5 个节点:
| Node | CPU 可分配资源 | 是否满足要求 |
|---|---|---|
| Node-1 | 2 CPU | 是 |
| Node-2 | 0.5 CPU | 否 |
| Node-3 | 8 CPU | 是 |
| Node-4 | 1 CPU | 是 |
| Node-5 | 0.2 CPU | 否 |
Pod 需要:
resources: requests: cpu: "1"Filter 会过滤掉资源不足的节点。
结果:
Node-1:通过Node-2:过滤Node-3:通过Node-4:通过Node-5:过滤2. Score 阶段
接下来对剩余节点评分。
例如:
| Node | 资源评分 | 其他调度评分 | 最终评分 |
|---|---|---|---|
| Node-1 | 70 | 80 | 75 |
| Node-3 | 90 | 85 | 88 |
| Node-4 | 60 | 70 | 65 |
最终选择:
Node-3注意:
实际 Scheduler 使用多个调度插件和权重计算得分,表格仅用于理解原理。
3. 调度的两个核心阶段
flowchart TB
N0["Filter<br/>过滤不符合条件的节点"]
N1["Score<br/>对符合条件的节点进行评分"]
N2["Bind<br/>将 Pod 绑定到选定节点"]
N0 --> N1
N1 --> N2
记忆:
- Filter:能不能运行?
- Score:运行在哪里更合适?
- Bind:最终绑定到哪个节点?
3. NodeSelector 节点选择
现在我们开始学习如何主动控制 Pod 的调度位置。
最简单的方式就是 NodeSelector。
1. 什么是 NodeSelector?
NodeSelector 是通过 Node 标签选择调度目标的机制。
例如:
集群中有三个节点:
worker-1worker-2worker-3我们希望某个 Pod 只运行在具有 disktype=ssd 标签的节点上。
首先给节点打标签:
kubectl label nodes worker-1 disktype=ssd查看:
kubectl get nodes --show-labels2. 在 Pod 中配置 NodeSelector
apiVersion: v1kind: Pod
metadata: name: nginx-ssd
spec: nodeSelector: disktype: ssd
containers: - name: nginx image: nginx:1.27Pod 只能被调度到满足:
disktype=ssd的节点。
3. NodeSelector 工作原理
flowchart TB
N0["Pod"]
N1["Scheduler"]
N2["Node-1<br/>ssd"]
N3["Node-2<br/>hdd"]
N4["Node-3<br/>ssd"]
N5["筛选 Node-1、Node-3"]
N6["选择其中一个节点"]
N0 -->|"nodeSelector: disktype: ssd"| N1
N1 --> N2
N1 --> N3
N1 --> N4
N2 --> N5
N4 --> N5
N5 --> N6
注意:
NodeSelector 只能表达简单的标签等值匹配。
如果需要复杂的匹配条件,就需要使用 Node Affinity。
4. Node Affinity 节点亲和性
NodeSelector 虽然简单,但在生产环境中有一个明显局限:
它只能进行简单的标签匹配,无法表达复杂的调度条件。
例如:
- 只允许调度到 SSD 节点。
- 允许调度到 SSD 或 NVMe 节点。
- 优先调度到高性能节点,但没有高性能节点时也可以使用普通节点。
- 不允许调度到某些特定节点。
这时就需要使用 Node Affinity。
1. 什么是 Node Affinity?
Node Affinity(节点亲和性)是一种更加灵活的节点选择机制。
它允许 Pod 根据 Node 的标签表达复杂的调度规则。
Node Affinity 有两种主要类型:
| 类型 | 含义 | 是否强制 |
|---|---|---|
| requiredDuringSchedulingIgnoredDuringExecution | 必须满足节点选择条件 | 是 |
| preferredDuringSchedulingIgnoredDuringExecution | 尽量满足节点选择条件 | 否 |
简单理解:
- required:必须满足,否则不能调度。
- preferred:优先满足,不满足也可以调度。
2. requiredDuringSchedulingIgnoredDuringExecution
这是硬性节点亲和性。
例如:
只允许 Pod 调度到 disktype=ssd 的节点。
apiVersion: v1kind: Pod
metadata: name: nginx-affinity
spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disktype operator: In values: - ssd
containers: - name: nginx image: nginx:1.27解释:
key: disktype匹配节点标签的键。
operator: In表示标签值必须属于指定集合。
values: - ssd指定允许的标签值。
因此:
Node-1:disktype=ssd 允许Node-2:disktype=hdd 不允许Node-3:disktype=nvme 不允许如果集群中没有符合条件的节点:
Pod:PendingReason:FailedScheduling3. preferredDuringSchedulingIgnoredDuringExecution
这是软性节点亲和性。
例如:
优先选择 SSD 节点,但如果没有 SSD 节点,也允许使用其他节点。
apiVersion: v1kind: Pod
metadata: name: nginx-preferred
spec: affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 80 preference: matchExpressions: - key: disktype operator: In values: - ssd
containers: - name: nginx image: nginx:1.27这里:
weight: 80表示该偏好规则的权重,取值范围是 1~100。
权重越高,该偏好对调度评分的影响通常越大。
注意:
weight: 80 不代表节点有 80% 的调度概率,也不代表节点最终得分就是 80。
如果没有满足 SSD 条件的节点,Pod 仍然可以调度到其他可用节点。
4. required 与 preferred 对比
硬性亲和性(required) 必须满足
Node-1 允许
Node-2 禁止
Node-3 允许
软性亲和性(preferred) 优先满足
Node-1 优先
Node-2 可选
Node-3 优先
5. Node Affinity 常用操作符
Node Affinity 的强大之处在于支持多种表达式。
常用操作符:
| Operator | 含义 | 示例 |
|---|---|---|
| In | 标签值属于指定集合 | ssd 或 nvme |
| NotIn | 标签值不属于指定集合 | 排除 hdd |
| Exists | 存在指定标签 | 存在 gpu |
| DoesNotExist | 不存在指定标签 | 不存在 gpu |
| Gt | 标签值大于指定值 | CPU 代数大于 8 |
| Lt | 标签值小于指定值 | CPU 代数小于 16 |
注意:Gt 和 Lt 比较的是可解析为整数的标签值。
1. In:匹配指定集合
matchExpressions: - key: disktype operator: In values: - ssd - nvme表示:
disktype=ssd或者disktype=nvme满足其中一个即可。
2. NotIn:排除指定集合
matchExpressions: - key: disktype operator: NotIn values: - hdd表示:
不允许调度到 disktype=hdd 的节点。
注意:如果节点根本没有 disktype 标签,NotIn 条件也可以满足。
3. Exists:标签必须存在
matchExpressions: - key: gpu operator: Exists表示:
节点必须存在 gpu 标签,不关心标签值。
例如:
gpu=nvidiagpu=amd都可以满足条件。
4. DoesNotExist:标签不能存在
matchExpressions: - key: gpu operator: DoesNotExist表示:
节点不能存在 gpu 标签。
5. Gt 和 Lt
例如:
matchExpressions: - key: cpu-generation operator: Gt values: - "8"表示:
节点标签 cpu-generation 的整数值必须大于 8。
6. NodeSelectorTerms 与 matchExpressions 的逻辑关系
这是 CKA 容易考查的细节。
Node Affinity 中有两层逻辑关系:
- 不同的
nodeSelectorTerms之间是 OR。 - 同一个
nodeSelectorTerm内的多个matchExpressions之间是 AND。
例如:
requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms:
- matchExpressions: - key: disktype operator: In values: - ssd
- key: region operator: In values: - east
- matchExpressions: - key: disktype operator: In values: - nvme逻辑表达式:
(disktype=ssd AND region=east) OR(disktype=nvme)也就是说:
满足以下任意一个条件就可以调度:
- SSD 磁盘,并且位于 east 区域。
- 使用 NVMe 磁盘。
记忆:
Node Affinity 逻辑关系
nodeSelectorTerms 不同 Term 之间 OR
Term 1 AND disktype=ssd region=east
Term 2 AND disktype=nvme
Term 1 OR Term 2
1. 逻辑关系总结
| 层级 | 逻辑关系 |
|---|---|
| 多个 nodeSelectorTerms | OR |
| 同一 Term 内多个 matchExpressions | AND |
| 同一 Term 内多个 matchFields | AND |
| matchExpressions 与 matchFields | AND |
2. 一个常见陷阱
如果这样写:
nodeSelectorTerms: - matchExpressions: - key: disktype operator: In values: [ssd]
- matchExpressions: - key: disktype operator: In values: [nvme]表示:
disktype=ssd OR disktype=nvme而不是要求节点同时具有两个值。
7. NodeSelector 与 Node Affinity 同时使用
在实际生产中,可以同时使用这两种机制。
例如:
apiVersion: v1kind: Pod
metadata: name: nginx-combined
spec: nodeSelector: environment: production
affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disktype operator: In values: - ssd
containers: - name: nginx image: nginx:1.27它的逻辑是:
environment=production ANDdisktype=ssd必须同时满足。
例如:
| Node | environment | disktype | 是否允许 |
|---|---|---|---|
| Node-1 | production | ssd | 是 |
| Node-2 | production | hdd | 否 |
| Node-3 | testing | ssd | 否 |
| Node-4 | testing | hdd | 否 |
注意:NodeSelector 与 required Node Affinity 是 AND 关系。如果两者条件冲突,Pod 无法调度。
8. IgnoredDuringExecution 到底是什么意思?
你可能注意到了,Node Affinity 的两个字段名称都包含:
IgnoredDuringExecution为什么叫这个名字?
因为 Kubernetes 将节点亲和性分成两个阶段:
- Pod 调度之前:检查节点是否满足条件。
- Pod 运行期间:节点标签发生变化时,是否驱逐 Pod。
例如:
Pod 要求:
disktype: ssdNode-1 原本具有:
disktype=ssdPod 被成功调度到 Node-1。
现在管理员修改节点标签:
kubectl label node worker-1 disktype=hdd --overwrite会发生什么?
Pod 不会因为这个标签变化而自动被驱逐。
因为:
requiredDuringSchedulingIgnoredDuringExecution只在调度阶段强制检查条件,不会在运行期间因为条件失效而自动驱逐 Pod。
目前 Kubernetes Node Affinity 没有对应的 requiredDuringSchedulingRequiredDuringExecution 原生配置。
如果业务需要节点标签变化后迁移 Pod,需要借助其他控制器或人工重新创建 Pod。
9. Pod Affinity(Pod 亲和性)
前面学习的 Node Affinity 是:
根据 Node 的标签决定 Pod 应该运行在哪个节点。
现在考虑另一个问题:
假设有一个 Web 应用和一个 Redis 缓存服务。
为了降低网络延迟,希望 Web Pod 尽量和 Redis Pod 部署在同一个节点。
这时,仅仅根据 Node 标签选择节点可能不够。
因为我们希望根据其他 Pod 的位置来决定当前 Pod 的调度位置。
这就是 Pod Affinity。
1. 什么是 Pod Affinity?
Pod Affinity(Pod 亲和性)是一种根据已有 Pod 的标签和拓扑位置决定新 Pod 调度位置的机制。
例如:
flowchart TB
N0["Node-1"]
N1["Redis Pod"]
N2["Web Pod"]
N0 --> N1
N0 --> N2
Web Pod 希望与 Redis Pod 尽可能靠近。
Pod Affinity 有两种主要类型:
| 类型 | 含义 |
|---|---|
| requiredDuringSchedulingIgnoredDuringExecution | 必须满足 Pod 亲和性条件 |
| preferredDuringSchedulingIgnoredDuringExecution | 尽量满足 Pod 亲和性条件 |
2. required Pod Affinity
假设 Redis Pod 有标签:
labels: app: redis现在创建 Web Pod,要求它必须与 Redis Pod 位于同一个 Node。
配置:
apiVersion: v1kind: Pod
metadata: name: web
spec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: redis
topologyKey: kubernetes.io/hostname
containers: - name: nginx image: nginx:1.27重点理解两个字段。
1. labelSelector
labelSelector: matchLabels: app: redis表示选择具有:
app=redis标签的 Pod。
2. topologyKey
topologyKey: kubernetes.io/hostname表示以节点主机名作为拓扑范围。
也就是说:
当前 Pod 必须调度到存在匹配 Redis Pod 的同一个 Node 上。
3. topologyKey 是什么?
topologyKey 是 Pod Affinity 和 Pod Anti-Affinity 的核心概念。
它定义了拓扑分组的依据。
例如:
| topologyKey | 含义 |
|---|---|
kubernetes.io/hostname | 同一个 Node |
topology.kubernetes.io/zone | 同一个可用区 |
topology.kubernetes.io/region | 同一个区域 |
举例:
flowchart TB
N0["Zone-A"]
N1["Node-1"]
N2["Redis"]
N3["Node-2"]
N4["Web"]
N0 --> N1
N1 --> N2
N0 --> N3
N3 --> N4
如果使用:
topologyKey: topology.kubernetes.io/zoneWeb 可以运行在 Zone-A 内的不同 Node 上,只要满足 Pod 选择条件即可。
如果使用:
topologyKey: kubernetes.io/hostnameWeb 必须和匹配的 Redis Pod 位于同一节点。
4. preferred Pod Affinity
如果只是希望 Web 尽量和 Redis 位于同一个节点,而不是强制要求:
affinity: podAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 80
podAffinityTerm: labelSelector: matchLabels: app: redis
topologyKey: kubernetes.io/hostname含义:
优先选择已经运行 Redis Pod 的节点,但如果不存在这样的节点,也可以调度到其他合适节点。
10. Pod Anti-Affinity(Pod 反亲和性)
现在反过来思考:
如果 Web 应用有三个副本,我们不希望它们全部运行在同一个节点。
为什么?
假设:
flowchart TB
N0["Node-1"]
N1["Web-1"]
N2["Web-2"]
N3["Web-3"]
N0 --> N1
N0 --> N2
N0 --> N3
如果 Node-1 宕机,三个 Web 副本会同时不可用。
生产环境通常希望将副本分散到不同节点,提升可用性。
这就是 Pod Anti-Affinity。
1. 什么是 Pod Anti-Affinity?
Pod Anti-Affinity(Pod 反亲和性)用于避免符合指定标签条件的 Pod 被调度到相同拓扑范围。
例如:
flowchart TB
N0["Node-1"]
N1["Node-2"]
N2["Node-3"]
N3["Web-1"]
N4["Web-2"]
N5["Web-3"]
N0 --> N3
N1 --> N4
N2 --> N5
2. required Pod Anti-Affinity
示例:
apiVersion: apps/v1kind: Deployment
metadata: name: web
spec: replicas: 3
selector: matchLabels: app: web
template: metadata: labels: app: web
spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: web
topologyKey: kubernetes.io/hostname
containers: - name: nginx image: nginx:1.27这里:
labelSelector: matchLabels: app: web表示选择已经存在的 Web Pod。
topologyKey: kubernetes.io/hostname表示同一台 Node 上不能再放置匹配的 Web Pod。
因此三个副本会尽量分布到三个不同节点。
但是注意:
如果集群只有两个符合条件的节点,那么第三个 Pod 就无法调度。
因为这是硬性反亲和性。
3. preferred Pod Anti-Affinity
如果希望尽量分散,但允许节点不足时共置,可以使用:
affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100
podAffinityTerm: labelSelector: matchLabels: app: web
topologyKey: kubernetes.io/hostname这种方式不会因为没有足够节点而直接阻止 Pod 调度。
4. Pod Affinity 与 Anti-Affinity 对比
| 类型 | 作用 | 典型场景 |
|---|---|---|
| Pod Affinity | 尽量或强制靠近指定 Pod | Web 与缓存服务靠近 |
| Pod Anti-Affinity | 尽量或强制远离指定 Pod | Web 副本分散部署 |
两种调度关系 Pod Affinity
Node-1 Redis Web
Node-2 其他应用
目标:Web 与 Redis 位于相同拓扑范围。
Pod Anti-Affinity
Node-1 Web-1
Node-2 Web-2
Node-3 Web-3
目标:Web 副本分散到不同节点。
11. Pod Topology Spread Constraints(拓扑分布约束)
前面我们学习了 Pod Anti-Affinity,可以让多个 Pod 尽量分散到不同节点。
但生产环境中还有一个问题:
假设 Kubernetes 集群有三个可用区:
Zone-A:Node-1、Node-2Zone-B:Node-3、Node-4Zone-C:Node-5、Node-6现在部署 6 个 Web Pod。
我们希望它们在三个可用区尽可能均匀分布:
Zone-A:2 个 PodZone-B:2 个 PodZone-C:2 个 Pod如果只使用 Pod Anti-Affinity,虽然可以控制 Pod 不在同一个节点,但不一定能够保证三个可用区的副本数量均衡。
这时可以使用 Pod Topology Spread Constraints。
1. 什么是拓扑分布约束?
它用于控制 Pod 在不同拓扑域之间的分布情况。
常见拓扑域:
- Node:节点。
- Zone:可用区。
- Region:区域。
例如:
topologySpreadConstraints: - maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector: matchLabels: app: web重点理解三个字段。
1. maxSkew
maxSkew: 1表示不同拓扑域之间,匹配 Pod 数量的最大允许差值为 1。
例如:
Zone-A:2 个Zone-B:2 个Zone-C:2 个满足要求。
如果:
Zone-A:3 个Zone-B:2 个Zone-C:1 个最大差值为:
[ 3-1=2 ]
超过 maxSkew=1。
在其他条件满足且使用硬性约束时,新 Pod 不能调度到进一步扩大不均衡的拓扑域。
2. topologyKey
topologyKey: topology.kubernetes.io/zone表示以可用区作为分布单位。
如果改为:
topologyKey: kubernetes.io/hostname则按照节点进行分布。
3. whenUnsatisfiable
有两种主要取值:
| 值 | 含义 |
|---|---|
| DoNotSchedule | 无法满足分布约束时,不允许调度 |
| ScheduleAnyway | 无法满足时,仍然调度,但优先选择更均衡的节点 |
2. 完整 Deployment 示例
apiVersion: apps/v1kind: Deployment
metadata: name: web
spec: replicas: 6
selector: matchLabels: app: web
template: metadata: labels: app: web
spec: topologySpreadConstraints: - maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector: matchLabels: app: web
containers: - name: nginx image: nginx:1.27这个配置希望 Web Pod 在不同可用区均匀分布。
注意:实际分布还受到节点标签、节点可用性、资源容量、调度条件和拓扑域资格条件影响。拓扑分布约束不是强制每个可用区都必须有 Pod。
3. minDomains
还可以使用:
minDomains: 3表示计算拓扑分布时,期望至少存在 3 个符合条件的拓扑域。
如果符合条件的拓扑域数量少于 minDomains,计算全局最小值时会按 0 处理。
它适合对可用区数量有明确要求的高可用部署。
4. Pod Anti-Affinity 与 Topology Spread 对比
| 对比 | Pod Anti-Affinity | Topology Spread |
|---|---|---|
| 主要目标 | 避免共置 | 控制数量均衡 |
| 节点分散 | 支持 | 支持 |
| 可用区均衡 | 可以实现部分约束 | 更适合 |
| 数量差控制 | 不直接提供 | maxSkew |
| 硬性约束 | required | DoNotSchedule |
| 软性约束 | preferred | ScheduleAnyway |
生产建议:如果你的目标是让应用副本均匀分布到不同节点和可用区,优先考虑 Topology Spread Constraints。
12. nodeName:直接指定节点
前面我们学习的 NodeSelector、Node Affinity 都是通过 Scheduler 进行节点选择。
但 Kubernetes 还提供了一种直接指定节点的方式:
nodeName: worker-1例如:
apiVersion: v1kind: Pod
metadata: name: nginx
spec: nodeName: worker-1
containers: - name: nginx image: nginx:1.27含义:
直接将 Pod 指定到 worker-1。
与 NodeSelector 的区别:
| 对比 | nodeName | nodeSelector |
|---|---|---|
| 指定方式 | 直接指定节点名称 | 根据标签选择 |
| 是否经过 Scheduler 选节点 | 不经过 | 经过 |
| 是否检查节点资源 | 不经过调度器筛选 | Scheduler 检查 |
| 是否灵活 | 较低 | 较高 |
注意:
nodeName 会绕过正常的 Scheduler 节点选择过程,并不代表 Pod 一定能正常运行。如果节点不存在或资源不足,Pod 可能无法启动。
生产环境通常优先使用 Node Affinity 或 NodeSelector,而不是直接指定 nodeName。
13. Taints 与 Tolerations:节点排斥机制
有时你不是希望某个 Pod 选择某台 Node,而是希望某台 Node 默认不接收普通 Pod。
这就是 Taints(污点)和 Tolerations(容忍)。
例如,GPU 节点只希望运行 GPU 任务:
kubectl taint nodes gpu-1 dedicated=gpu:NoSchedule此时,没有匹配容忍条件的普通 Pod 不会被调度到该节点。
GPU Pod 可以这样配置:
apiVersion: v1kind: Podmetadata: name: gpu-jobspec: tolerations: - key: dedicated operator: Equal value: gpu effect: NoSchedule containers: - name: job image: busybox:1.36 command: ["sh", "-c", "sleep 3600"]但这里有一个重要区别:
Toleration 只表示可以容忍污点,不代表 Pod 一定会被调度到这台节点。
如果还希望 Pod 只能运行在 GPU 节点上,需要配合 nodeSelector 或 Node Affinity。
三种常见污点效果:
| Effect | 含义 |
|---|---|
NoSchedule | 不调度不容忍该污点的新 Pod |
PreferNoSchedule | 尽量避免调度不容忍该污点的 Pod |
NoExecute | 不仅限制新调度,还可能驱逐已运行且不容忍该污点的 Pod |
删除污点:
kubectl taint nodes gpu-1 dedicated=gpu:NoSchedule-14. PriorityClass 与抢占机制
假设集群资源不足,有两个 Pod 等待调度:
- 普通业务 Pod:优先级 100。
- 关键业务 Pod:优先级 100000。
你可以使用 PriorityClass 定义优先级:
apiVersion: scheduling.k8s.io/v1kind: PriorityClassmetadata: name: high-priorityvalue: 100000globalDefault: falsedescription: "关键业务工作负载"然后在 Pod 模板中指定:
spec: priorityClassName: high-priority当高优先级 Pod 无法调度时,调度器可能尝试通过 Preemption(抢占) 驱逐低优先级 Pod,为其腾出资源。
但高优先级不意味着一定能运行。如果节点存在无法满足的硬性约束、污点不兼容或其他不可通过抢占解决的限制,调度仍可能失败。
15. 如何排查 Pod 一直 Pending?
这是生产环境中很常见的问题。
先执行:
kubectl get pods -A -o widekubectl describe pod <pod-name>kubectl get nodeskubectl describe nodes重点看 Pod 的 Events。
| 常见事件提示 | 可能原因 |
|---|---|
Insufficient cpu | 可分配 CPU 不足 |
Insufficient memory | 可分配内存不足 |
node(s) didn't match Pod's node affinity/selector | 节点标签或亲和性不匹配 |
node(s) had untolerated taint | Pod 无法容忍节点污点 |
pod has unbound immediate PersistentVolumeClaims | PVC 尚未绑定,阻碍调度 |
建议按以下顺序排查:
- 检查节点是否处于
Ready。 - 检查 Pod 的 CPU、内存 requests。
- 检查
nodeSelector和 Node Affinity。 - 检查节点 Taints 与 Pod Tolerations。
- 检查 PVC 是否满足绑定条件。
- 查看调度事件,确认真正阻止调度的原因。
16. 调度机制综合对比
现在把本节学习的所有调度机制整理起来。
| 调度机制 | 主要作用 | 约束对象 |
|---|---|---|
| nodeSelector | 根据节点标签选择 | Node |
| Node Affinity | 灵活选择节点 | Node |
| Pod Affinity | 靠近指定 Pod | Pod |
| Pod Anti-Affinity | 远离指定 Pod | Pod |
| Topology Spread | 控制跨拓扑域分布 | Pod |
| Taints | 限制节点接收 Pod | Node |
| Tolerations | 容忍节点污点 | Pod |
| nodeName | 直接指定节点 | Node |
整个调度过程可以理解为:
flowchart TB
N0["创建 Pod"]
N1["Scheduler 调度"]
N2["节点资源与调度条件检查"]
N3["Node Affinity"]
N4["Taints/Tolerations"]
N5["Pod Affinity 检查"]
N6["Topology Spread 检查"]
N7["Filter 过滤"]
N8["Score 评分"]
N9["Bind 节点绑定"]
N10["kubelet 启动 Pod"]
N0 --> N1
N1 --> N2
N2 --> N3
N2 --> N4
N3 --> N5
N4 --> N5
N5 --> N6
N6 --> N7
N7 --> N8
N8 --> N9
N9 --> N10
这张图是学习上的逻辑归纳,实际 Scheduler 的插件执行顺序由调度框架和插件配置决定。
17. CKA 必须掌握的调度命令
| 操作 | 命令 |
|---|---|
| 查看节点标签 | kubectl get nodes --show-labels |
| 添加节点标签 | kubectl label node worker-1 disktype=ssd |
| 修改节点标签 | kubectl label node worker-1 disktype=nvme --overwrite |
| 删除节点标签 | kubectl label node worker-1 disktype- |
| 查看节点污点 | kubectl describe node worker-1 |
| 添加节点污点 | kubectl taint node worker-1 dedicated=db:NoSchedule |
| 删除节点污点 | kubectl taint node worker-1 dedicated=db:NoSchedule- |
| 查看 Pod 调度节点 | kubectl get pods -o wide |
| 查看调度失败原因 | kubectl describe pod nginx |
| 查看 Pod YAML | kubectl get pod nginx -o yaml |