# Kubernetes 高级调度完全指南(适用于 Kubernetes v1.35)
Kubernetes 高级调度完全指南(适用于 Kubernetes v1.35)
文档说明
- 适用版本:Kubernetes v1.35.4
- 前置知识:了解 Pod、Node、Deployment 的基本概念
- 目标:从零掌握 Kubernetes 节点管理、亲和性、反亲和性、污点与容忍度的原理与使用
第一章:为什么需要高级调度?
1.1 默认调度的局限性
在默认情况下,Pod 运行在哪个 Node 上,是由 Scheduler 组件经过一系列算法计算得出的,这个过程不受人工控制。但在实际生产中,我们往往有更精细的控制需求:
| 场景 | 需求 |
|---|---|
| 硬件差异化 | GPU 节点只跑 AI 训练任务,SSD 节点只跑数据库 |
| 高可用 | 同一个应用的多个副本不能跑在同一台机器上 |
| 低延迟 | 前端 Pod 和后端 Pod 要尽量靠近(同一节点/可用区) |
| 节点隔离 | 业务 Pod 不能调度到 Master 节点 |
| 节点维护 | 维护节点前要驱逐所有 Pod,且不允许新 Pod 调度上来 |
一句话总结:Kubernetes 提供了亲和性(Attraction) 和污点(Repulsion) 两套机制,让你可以精确控制 Pod 该往哪儿跑、不该往哪儿跑。
1.2 调度机制全景图
Kubernetes 提供了四大类调度方式:
| 调度方式 | 说明 | 强制程度 |
|---|---|---|
| 自动调度 | Scheduler 自动计算,不受人工控制 | - |
| 定向调度 | nodeName / nodeSelector | 强制 |
| 亲和性调度 | NodeAffinity / PodAffinity / PodAntiAffinity | 强制/偏好 |
| 污点(容忍)调度 | Taints / Tolerations | 强制/偏好 |
第二章:Node(节点)管理基础
2.1 查看节点
# 查看所有节点
kubectl get nodes
# 查看节点详细信息(含标签、污点)
kubectl describe node <node-name>
# 查看节点标签
kubectl get nodes --show-labels
2.2 节点标签(Labels)—— 调度的基础
亲和性和反亲和性都依赖于 Label(标签) 机制。标签是键值对,可以附加到 Node 或 Pod 上。
给节点打标签:
# 给节点添加标签
kubectl label nodes <node-name> disktype=ssd
kubectl label nodes <node-name> gpu=true
kubectl label nodes <node-name> zone=us-west-1a
# 查看节点标签
kubectl get nodes --show-labels
# 删除标签(在键名后加 -)
kubectl label nodes <node-name> disktype-
常用的节点标签(Kubernetes 自动添加):
| 标签 | 说明 |
|---|---|
kubernetes.io/hostname | 节点主机名 |
topology.kubernetes.io/zone | 可用区 |
topology.kubernetes.io/region | 区域 |
kubernetes.io/os | 操作系统 |
kubernetes.io/arch | CPU 架构 |
2.3 节点维护操作:Cordon 与 Drain
在维护节点之前,需要先将节点上的 Pod 迁移走,并阻止新 Pod 调度上来。
Cordon(隔离) :将节点标记为不可调度,新 Pod 不会调度上来,但已有 Pod 不受影响。
# 隔离节点
kubectl cordon <node-name>
# 取消隔离
kubectl uncordon <node-name>
# 查看节点状态(SchedulingDisabled 表示被 cordon)
kubectl get nodes
Drain(排空) :驱逐节点上的所有 Pod(优雅终止),并将节点标记为不可调度。
# 排空节点(驱逐所有 Pod)
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
# 参数说明:
# --ignore-daemonsets: 忽略 DaemonSet 管理的 Pod
# --delete-emptydir-data: 删除 emptyDir 中的数据
# --force: 强制驱逐(即使 Pod 不受 Deployment 管理)
典型维护流程:
kubectl cordon <node>—— 阻止新 Pod 调度kubectl drain <node>—— 驱逐现有 Pod- 执行维护操作(升级内核、更换硬件等)
kubectl uncordon <node>—— 恢复调度
第三章:定向调度 —— 最简单粗暴的方式
3.1 nodeName —— 指定节点名称
nodeName 用于强制将 Pod 调度到指定名称的节点上。这种方式直接跳过 Scheduler 的调度逻辑,即使目标节点不存在,Pod 也会被创建(只是会失败)。
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
spec:
nodeName: node-01 # 直接指定节点名称
containers:
- name: nginx
image: nginx:1.25
# 查看节点名称
kubectl get nodes
3.2 nodeSelector —— 节点选择器
nodeSelector 通过标签匹配来选择节点,是强制约束。
apiVersion: v1
kind: Pod
metadata:
name: pod-selector
spec:
nodeSelector: # 必须匹配的标签
disktype: ssd # 只调度到有 disktype=ssd 标签的节点
containers:
- name: nginx
image: nginx:1.25
局限性:
nodeSelector只能做简单的“等于”匹配,无法表达更复杂的逻辑(如“不等于”、“存在”、“不在”等)。这些复杂需求需要用到 Node Affinity。
第四章:节点亲和性(Node Affinity)
Node Affinity 是 nodeSelector 的升级版,支持更丰富的匹配表达式(In、NotIn、Exists、DoesNotExist、Gt、Lt)。
4.1 两种类型
| 类型 | 关键字 | 说明 |
|---|---|---|
| 硬约束(Required) | requiredDuringSchedulingIgnoredDuringExecution | 必须满足,否则 Pod 不会被调度 |
| 软约束(Preferred) | preferredDuringSchedulingIgnoredDuringExecution | 尽量满足,找不到匹配节点时也会调度 |
4.2 硬约束(Required)示例
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-required
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
- nvme
- key: zone
operator: In
values:
- us-west-1a
containers:
- name: nginx
image: nginx:1.25
逻辑:Pod 必须调度到同时满足以下条件的节点:
- 标签
disktype的值是ssd或nvme - 标签
zone的值是us-west-1a
4.3 软约束(Preferred)示例
apiVersion: v1
kind: Pod
metadata:
name: node-affinity-preferred
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100 # 权重(1-100),越高越优先
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
- weight: 50
preference:
matchExpressions:
- key: zone
operator: In
values:
- us-west-1a
containers:
- name: nginx
image: nginx:1.25
逻辑:Scheduler 会优先选择有 disktype=ssd 的节点(权重 100),其次选择在 us-west-1a 可用区的节点(权重 50)。但如果所有节点都不满足,Pod 依然会被调度。
4.4 操作符详解
| 操作符 | 说明 | 示例 |
|---|---|---|
In | 标签值在列表中 | values: ["ssd", "nvme"] |
NotIn | 标签值不在列表中(反亲和) | values: ["hdd"] |
Exists | 标签存在(不管值是什么) | 不需要 values 字段 |
DoesNotExist | 标签不存在 | 不需要 values 字段 |
Gt | 标签值大于指定值(数值比较) | values: ["3"] |
Lt | 标签值小于指定值(数值比较) | values: ["3"] |
第五章:Pod 亲和性与反亲和性
Node Affinity 控制的是 Pod 往哪些节点跑。而 Pod Affinity / Anti-Affinity 控制的是 Pod 和哪些 Pod 一起跑(或不要一起跑)。
5.1 为什么需要 Pod 级别的调度?
| 场景 | 解决方案 |
|---|---|
| 低延迟 | 前端 Pod 和后端 Pod 部署在同一个节点上(Pod 亲和性) |
| 高可用 | 同一个应用的多个副本不要部署在同一个节点上(Pod 反亲和性) |
| 避免干扰 | 批处理任务不要和在线服务跑在一起(Pod 反亲和性) |
5.2 两种类型
与 Node Affinity 一样,Pod Affinity/Anti-Affinity 也分为硬约束(Required) 和软约束(Preferred) 。
5.3 Pod 亲和性(Pod Affinity)示例
场景:让 Web 前端 Pod 和后端缓存 Pod(带 app=redis 标签)部署在同一个节点上,降低网络延迟。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-frontend
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname # 在节点级别亲和
containers:
- name: web
image: nginx:1.25
关键字段 topologyKey :
| topologyKey | 含义 |
|---|---|
kubernetes.io/hostname | 在节点级别亲和(同一台机器) |
topology.kubernetes.io/zone | 在可用区级别亲和(同一个机房) |
topology.kubernetes.io/region | 在区域级别亲和(同一个地区) |
逻辑:Pod 会被调度到已经有带
app=redis标签的 Pod 在运行的节点上。
5.4 Pod 反亲和性(Pod Anti-Affinity)示例
场景:同一个应用的 3 个副本必须分散到不同节点上,避免单点故障。
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
spec:
replicas: 3
selector:
matchLabels:
app: payment
template:
metadata:
labels:
app: payment
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬约束
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- payment
topologyKey: kubernetes.io/hostname
containers:
- name: payment
image: payment:v1.0.0
效果:3 个副本会分别调度到 3 个不同的节点上。如果集群只有 2 个节点,第 3 个 Pod 会一直 Pending。
5.5 软约束反亲和性(Preferred)示例
使用软约束避免调度死锁:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: kubernetes.io/hostname
效果:Scheduler 会尽量将 Pod 分散到不同节点,但如果节点不够,也可以接受多个副本在同一节点。
5.6 可用区级别的反亲和性
场景:将 Pod 分散到不同的可用区(Zone),实现机房级别的容灾。
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- api
topologyKey: topology.kubernetes.io/zone # 可用区级别
5.7 组合使用:Node + Zone 双重反亲和
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- database
topologyKey: kubernetes.io/hostname # 节点级别
- weight: 50
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- database
topologyKey: topology.kubernetes.io/zone # 可用区级别
效果:优先分散到不同节点(权重 100),其次分散到不同可用区(权重 50)。
第六章:污点(Taints)与容忍(Tolerations)
6.1 基本概念
污点(Taint) 是节点的属性,用于排斥一类 Pod。
容忍(Toleration) 是Pod 的属性,表示 Pod 可以“容忍”某个污点。
类比:污点就像节点上的“刺”,只有携带“防护手套”(容忍)的 Pod 才能靠近。
污点和亲和性的区别:
| 机制 | 作用对象 | 方向 | 效果 |
|---|---|---|---|
| 亲和性 | Pod | Pod → Node | Pod 主动选择 Node(“我想去哪”) |
| 污点 | Node | Node → Pod | Node 排斥 Pod(“我不欢迎谁”) |
6.2 污点的三种效果(Effect)
| Effect | 对未调度 Pod | 对已运行 Pod |
|---|---|---|
NoSchedule | 不容忍则不调度 | 无影响,已运行的保留 |
PreferNoSchedule | 尽量不调度(软限制) | 无影响 |
NoExecute | 不容忍则不调度 | 立即驱逐不容忍的 Pod |
典型应用场景:
- Master 节点打污点,只允许系统组件运行
- GPU 节点打污点,只允许需要 GPU 的 Pod 调度
- 新节点打污点,测试通过后再移除
- 节点维护时,打
NoExecute污点驱逐 Pod
6.3 污点操作命令
# 1. 添加污点
kubectl taint nodes <node-name> <key>=<value>:<effect>
# 示例:给 node-01 添加污点,禁止新 Pod 调度
kubectl taint nodes node-01 dedicated=gpu:NoSchedule
# 示例:给 node-02 添加污点,驱逐已有 Pod
kubectl taint nodes node-02 maintenance=true:NoExecute
# 2. 查看节点的污点
kubectl describe node node-01 | grep Taints
# 或
kubectl get nodes -o json | jq '.items[].spec.taints'
# 3. 移除污点(在命令末尾加 -)
kubectl taint nodes node-01 dedicated=gpu:NoSchedule-
6.4 容忍(Toleration)配置
在 Pod 的 YAML 中添加 tolerations 字段:
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
containers:
- name: gpu-app
image: nvidia/cuda:12.0
容忍匹配规则:
| operator | 匹配条件 |
|---|---|
Equal(默认) | key、value、effect 全部相等才匹配 |
Exists | 只要 key 和 effect 匹配即可,忽略 value |
特殊用法:
# 匹配所有 key(operator 必须为 Exists)
tolerations:
- operator: "Exists"
effect: "NoSchedule"
# 匹配所有 effect(不指定 effect)
tolerations:
- key: "dedicated"
operator: "Exists"
# 没有 effect 字段,匹配所有 effect
6.5 NoExecute + tolerationSeconds —— 优雅驱逐
当节点被打上 NoExecute 污点时,不容忍的 Pod 会被立即驱逐。但可以通过 tolerationSeconds 给 Pod 一个优雅退出的时间窗口。
tolerations:
- key: "maintenance"
operator: "Equal"
value: "true"
effect: "NoExecute"
tolerationSeconds: 60 # 60 秒后才会被驱逐
用途:节点维护时,给 Pod 60 秒时间完成正在处理的请求,然后优雅退出。
6.6 内置污点
Kubernetes 会自动给节点添加一些内置污点:
| 污点 | 触发条件 |
|---|---|
node.kubernetes.io/not-ready | 节点未就绪 |
node.kubernetes.io/unreachable | 节点不可达 |
node.kubernetes.io/memory-pressure | 节点内存压力 |
node.kubernetes.io/disk-pressure | 节点磁盘压力 |
node.kubernetes.io/pid-pressure | 节点 PID 压力 |
node.kubernetes.io/network-unavailable | 节点网络不可用 |
默认容忍:kube-system 命名空间下的系统 Pod 通常都带有对这些污点的容忍,确保系统组件能在异常节点上继续运行。
第七章:实战场景组合
7.1 场景一:GPU 节点专用于 AI 训练
需求:集群中有 3 台 GPU 节点,只允许 AI 训练 Pod 调度到这些节点上。
Step 1:给 GPU 节点打标签和污点
# 打标签(用于亲和性)
kubectl label nodes gpu-node-01 gpu=true
kubectl label nodes gpu-node-02 gpu=true
kubectl label nodes gpu-node-03 gpu=true
# 打污点(排斥非 AI 的 Pod)
kubectl taint nodes gpu-node-01 gpu=专用:NoSchedule
kubectl taint nodes gpu-node-02 gpu=专用:NoSchedule
kubectl taint nodes gpu-node-03 gpu=专用:NoSchedule
Step 2:AI 训练 Pod 配置(亲和 + 容忍)
apiVersion: v1
kind: Pod
metadata:
name: ai-training
spec:
# 容忍 GPU 节点的污点
tolerations:
- key: "gpu"
operator: "Equal"
value: "专用"
effect: "NoSchedule"
# 只调度到有 gpu=true 标签的节点
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu
operator: In
values:
- "true"
containers:
- name: trainer
image: tensorflow/tensorflow:latest-gpu
7.2 场景二:高可用 —— 3 副本分散到不同节点
需求:关键服务的 3 个副本必须分散到不同节点,避免单点故障。
apiVersion: apps/v1
kind: Deployment
metadata:
name: critical-service
spec:
replicas: 3
selector:
matchLabels:
app: critical
template:
metadata:
labels:
app: critical
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- critical
topologyKey: kubernetes.io/hostname
containers:
- name: app
image: myapp:v1
7.3 场景三:低延迟 —— 前端和后端部署在同一节点
需求:Web 前端 Pod 要和 Redis 缓存 Pod 部署在同一个节点上,降低网络延迟。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-frontend
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
topologyKey: kubernetes.io/hostname
containers:
- name: web
image: nginx:1.25
7.4 场景四:Master 节点隔离
Kubernetes 默认行为:Master 节点默认带有污点 node-role.kubernetes.io/master:NoSchedule(或 control-plane:NoSchedule),业务 Pod 默认不会调度到 Master 节点上。
查看 Master 节点的污点:
kubectl describe node master-01 | grep Taints
# 输出:Taints: node-role.kubernetes.io/control-plane:NoSchedule
如果确实需要将某个 Pod 调度到 Master 节点(如监控组件):
spec:
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
第八章:最佳实践总结
8.1 选择指南
| 需求 | 推荐方案 |
|---|---|
| Pod 必须跑在特定节点 | nodeSelector 或 required NodeAffinity |
| Pod 最好跑在特定节点(但不强制) | preferred NodeAffinity |
| 多个 Pod 要在一起(低延迟) | PodAffinity |
| 多个 Pod 不要在一起(高可用) | PodAntiAffinity |
| 节点要排斥某些 Pod | Taints(污点) |
| Pod 要“无视”节点的排斥 | Tolerations(容忍) |
| 节点维护 | cordon + drain |
8.2 核心原则
- 硬约束要谨慎:
required类规则如果太严格,可能导致 Pod 一直 Pending - 优先使用软约束:能用
preferred就别用required,提高调度灵活性 - 污点 + 亲和性配合使用:污点负责“排斥”,亲和性负责“吸引”,两者结合效果最佳
- 维护节点先 cordon 再 drain:先阻止新 Pod,再驱逐旧 Pod,避免服务中断
- 合理设置 tolerationSeconds:给 Pod 足够的优雅退出时间
8.3 常见陷阱
| 陷阱 | 后果 | 解决方案 |
|---|---|---|
| 硬约束反亲和 + 副本数 > 节点数 | Pod 一直 Pending | 改用软约束或减少副本数 |
| 忘记给 Pod 加容忍 | Pod 无法调度到有污点的节点 | 检查 Pod 的 tolerations 配置 |
| 给节点打污点后忘记移除 | 节点永远无法接收新 Pod | 维护完成后移除污点 |
| 同时使用 nodeSelector 和亲和性 | 规则叠加,调度更困难 | 尽量只选一种方式 |
附录:快速索引
| 需求 | 命令/配置 |
|---|---|
| 查看节点 | kubectl get nodes |
| 查看节点标签 | kubectl get nodes --show-labels |
| 给节点打标签 | kubectl label nodes <node> <key>=<value> |
| 隔离节点(Cordon) | kubectl cordon <node> |
| 恢复节点(Uncordon) | kubectl uncordon <node> |
| 排空节点(Drain) | kubectl drain <node> --ignore-daemonsets |
| 添加污点 | kubectl taint nodes <node> <key>=<value>:<effect> |
| 移除污点 | kubectl taint nodes <node> <key>=<value>:<effect>- |
| 查看污点 | kubectl describe node <node> | grep Taints |
| NodeAffinity 硬约束 | requiredDuringSchedulingIgnoredDuringExecution |
| NodeAffinity 软约束 | preferredDuringSchedulingIgnoredDuringExecution |
| PodAffinity 硬约束 | podAffinity.requiredDuringSchedulingIgnoredDuringExecution |
| PodAntiAffinity 硬约束 | podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution |
文档版本:v1.0
适用环境:Kubernetes v1.35+
前置条件:已部署 Kubernetes 集群并配置kubectl
更多推荐
所有评论(0)