kubernetes(K8s)学习笔记(第九期):集群治理与控制(下篇):调度与节点管理------Scheduler + 污点容忍 + 节点维护
本笔记为 Kubernetes 系列第九期,聚焦 Pod 调度与节点管理。涵盖:kube-scheduler 调度原理、四种控制 Pod 运行位置的方式(nodeName/nodeSelector/nodeAffinity/podAffinity & antiAffinity)、污点与容忍度、节点维护操作(cordon/drain/uncordon)。所有命令和 YAML 示例均已经过整理和注释。全文约 3700 字 ,包含 20+ YAML 示例 、50+ 命令示例 和 12 张对比表格,是 Kubernetes 调度与节点管理的完整指南。
知识衔接:上一期:kubernetes(K8s)学习笔记(第八期):集群治理与控制(上篇):网络策略------NetworkPolicy-CSDN博客
--- Compiled and Authored by Whisky --- July 1 st, 2026
目录
- 调度概述
- 控制 Pod 运行位置
- 污点与容忍度(Taint & Toleration)
- 节点管理------cordon / drain / uncordon
- 总结与知识点一览表
一、调度概述
1.1 kube-scheduler 是什么?
kube-scheduler 是 Kubernetes 集群的默认调度器,负责将新创建的或尚未调度的 Pod 分配到合适的节点上运行。
核心职责:
- 通过监测(Watch)机制发现集群中未被调度的 Pod
- 为每个 Pod 选择最优节点
- 将调度结果写入 API Server(绑定操作)
通俗理解:kube-scheduler 就像一个"房屋中介"------有新 Pod 要入住,中介根据房客的需求(资源、标签、亲和性)和房屋的现状(节点资源、标签、污点),帮房客找到最合适的房子。
1.2 调度过程
调度过程分为两个阶段:
| 阶段 | 名称 | 作用 |
|---|---|---|
| 第一阶段 | 过滤(Filtering) | 找出所有满足 Pod 基本需求的节点 |
| 第二阶段 | 打分(Scoring) | 对每个可调度节点打分,选择得分最高的节点 |
过滤阶段常见断言(Predicates):
| 断言 | 作用 |
|---|---|
PodFitsResources |
检查节点是否有足够的 CPU/内存资源 |
PodFitsHostPorts |
检查节点端口是否被占用 |
PodFitsHost |
检查是否匹配 nodeName |
MatchNodeSelector |
检查是否匹配 nodeSelector |
PodToleratesNodeTaints |
检查 Pod 能否容忍节点污点 |
CheckVolumeBinding |
检查 PVC 是否可绑定 |
NoVolumeZoneConflict |
检查存储卷的可用区限制 |
MaxCSIVolumeCount |
检查 CSI 卷数量是否超限 |
打分阶段常见优先级(Priorities):
| 优先级 | 作用 |
|---|---|
LeastRequestedPriority |
优先选择资源使用率较低的节点(负载均衡) |
MostRequestedPriority |
优先选择资源使用率较高的节点(节省资源) |
NodeAffinityPriority |
优先选择匹配节点亲和性的节点 |
InterPodAffinityPriority |
优先选择满足 Pod 间亲和性的节点 |
ImageLocalityPriority |
优先选择已有容器镜像缓存的节点 |
EvenPodsSpreadPriority |
优先选择满足 Pod 拓扑扩展约束的节点 |
说明 :如果过滤后没有可调度节点,Pod 将保持
Pending状态,并记录FailedScheduling事件。可通过kubectl describe pod <pod-name>查看具体失败原因。
1.3 两种调度器配置方式
Kubernetes 支持两种方式配置调度器的行为:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| 调度策略(Policy) | 配置过滤所用的断言和打分所用的优先级 | 旧版方式,已逐步淘汰 |
| 调度配置(Config) | 配置不同调度阶段的插件(Filter/Score/Bind 等) | 当前推荐方式 |
生产环境中,默认调度器配置已满足大多数需求,一般无需自定义。
调度器配置以静态 Pod 形式运行在 Master 节点,配置文件位于
/etc/kubernetes/manifests/kube-scheduler.yaml。
二、控制 Pod 运行位置
Kubernetes 提供了多种方式控制 Pod 调度到特定节点,按优先级从高到低排列如下:
| 方式 | 优先级 | 推荐度 |
|---|---|---|
| nodeName | 最高(直接指定) | 不推荐(仅测试) |
| nodeSelector | 高 | ✅ 推荐 |
| nodeAffinity | 高 | ✅ 推荐 |
| podAffinity / antiAffinity | 中 | 按需使用 |
2.1 nodeName------直接指定节点
nodeName 是 PodSpec 中的字段,直接指定 Pod 运行的节点名称,优先级最高,调度器会完全跳过调度逻辑。
示例:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 5
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
nodeName: worker32.whisky.cloud
containers:
- image: nginx
name: nginx
bash
root@master30:~# kubectl apply -f deploy-webapp.yaml
root@master30:~# kubectl get pods -o wide | awk '{print $1,$7}'
webapp-55c6b896bc-kdrcp worker32.whisky.cloud
webapp-55c6b896bc-l6kpt worker32.whisky.cloud
webapp-55c6b896bc-l8pld worker32.whisky.cloud
webapp-55c6b896bc-qrzqt worker32.whisky.cloud
webapp-55c6b896bc-tdr5h worker32.whisky.cloud
局限:
- 如果节点不存在,Pod 将无法运行,甚至被自动删除
- 云环境中节点名不稳定
- 无法利用调度器的资源优化能力
使用建议:仅用于单节点测试或调试场景,生产环境应使用 nodeSelector 或 nodeAffinity。
2.2 nodeSelector------基于标签选择
nodeSelector 是 PodSpec 中的字段,通过匹配节点的标签来决定 Pod 调度到哪里。
查看节点标签:
bash
root@master30:~# kubectl get node --show-labels
给节点打标签:
bash
root@master30:~# kubectl label node worker31.whisky.cloud disktype=ssd
root@master30:~# kubectl get node -L disktype
worker31.whisky.cloud Ready <none> 7d ssd
在 Pod 中使用 nodeSelector:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 3
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
nodeSelector:
disktype: ssd
containers:
- image: nginx
name: nginx
bash
root@master30:~# kubectl apply -f deploy-webapp.yaml
root@master30:~# kubectl get pods -o wide
webapp-7d675f9d9c-gm824 worker31.whisky.cloud
webapp-7d675f9d9c-rxds2 worker31.whisky.cloud
webapp-7d675f9d9c-v7b6c worker31.whisky.cloud
删除标签后:
bash
root@master30:~# kubectl label node worker31.whisky.cloud disktype-
# 现有 Pod 不会迁移(nodeSelector 只在调度时生效)
# 删除 Deployment 后重新创建,Pod 将因找不到匹配节点而 Pending
关键理解 :nodeSelector 只在 Pod 调度时 生效。节点标签变更不会导致已有 Pod 重新调度,这是
IgnoredDuringExecution的含义------调度后不再关注节点标签变化。
2.3 节点亲和性(nodeAffinity)
nodeAffinity 是 nodeSelector 的升级版,支持更灵活的选择规则。
两种类型:
| 类型 | 说明 |
|---|---|
requiredDuringSchedulingIgnoredDuringExecution |
硬性要求,必须满足 |
preferredDuringSchedulingIgnoredDuringExecution |
软性偏好,优先但不强制 |
操作符支持 :In、NotIn、Exists、DoesNotExist、Gt、Lt
完整示例:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: CPU
operator: In
values:
- L1
- L2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: MEM
operator: In
values:
- L1
- L2
containers:
- image: nginx
name: nginx
规则解读:
- 硬性要求 :Pod 必须调度到标签
CPU的值为L1或L2的节点 - 软性偏好 :在这些节点中,优先选择标签
MEM的值为L1或L2的节点 weight取值范围 1-100,值越大优先级越高
实验验证:
bash
# 阶段1:未打标签 → Pod Pending
root@master30:~# kubectl apply -f deploy-nodeaffinity.yaml
root@master30:~# kubectl get pods
web-8496b6959f-gphmk 0/1 Pending
# 查看事件确认原因
root@master30:~# kubectl describe pod web-8496b6959f-gphmk | grep -A5 Events
Events:
Type Reason Age Message
Warning FailedScheduling 5s 0/3 nodes available: 3 node(s) didn't match node selector
# 阶段2:打 CPU 标签
root@master30:~# kubectl label nodes worker31.whisky.cloud CPU=L1
root@master30:~# kubectl label nodes worker32.whisky.cloud CPU=L2
root@master30:~# kubectl get pods -o wide
web-8496b6959f-4lfb8 Running worker32.whisky.cloud
web-8496b6959f-qhfmv Running worker31.whisky.cloud
# 阶段3:打 MEM 标签(增加偏好)
root@master30:~# kubectl label nodes worker32.whisky.cloud MEM=L2
root@master30:~# kubectl rollout restart deployment web
# 重新调度后 Pod 全部到 worker32(同时满足 CPU 和 MEM 偏好)
2.4 Pod 间亲和性与反亲和性
Pod 间亲和性/反亲和性 基于已经运行在节点上的 Pod 标签来决定调度,而不是基于节点标签。
关键概念 :topologyKey 定义拓扑域,常见取值:
kubernetes.io/hostname:节点级别topology.kubernetes.io/zone:可用区级别topology.kubernetes.io/region:区域级别
2.4.1 Pod 间亲和性(podAffinity)
目标 :将 Pod 调度到与满足条件的 Pod 相同的拓扑域中。
yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-podaffinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web
topologyKey: topology.kubernetes.io/zone
containers:
- name: web
image: nginx
实验:
bash
# 1. 给节点打 zone 标签
root@master30:~# kubectl label node worker31.whisky.cloud topology.kubernetes.io/zone=v
root@master30:~# kubectl label node worker32.whisky.cloud topology.kubernetes.io/zone=v
# 2. 在 worker31 创建带 app=web 标签的 Pod
root@master30:~# kubectl run web --image=nginx --labels=app=web --node-name=worker31.whisky.cloud
# 3. 创建依赖亲和性的 Pod
root@master30:~# kubectl apply -f pod-with-podaffinity.yaml
root@master30:~# kubectl describe pod pod-with-podaffinity | grep Node:
Node: worker32.whisky.cloud/10.1.8.32
结果 :新 Pod 调度到与
web同一个 zone(worker32),因为 worker31 和 worker32 都在 zone=v 中。
2.4.2 Pod 间反亲和性(podAntiAffinity)
目标:将 Pod 分散到不同拓扑域,提高可用性。
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: store
spec:
replicas: 3
selector:
matchLabels:
app: store
template:
metadata:
labels:
app: store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-server
image: nginx
bash
root@master30:~# kubectl apply -f deploy-with-podAntiAffinity.yaml
root@master30:~# kubectl get pods -o wide
store-5596cf4c84-dswkq Running worker32.whisky.cloud
store-5596cf4c84-ww2l4 Running worker31.whisky.cloud
store-5596cf4c84-9z5ch Pending <none>
结果分析:
- 每个节点只能运行一个
app=store的 Pod(因为topologyKey=kubernetes.io/hostname) - 第三个 Pod 因无可用节点而处于
Pending状态 - 添加新节点后,第三个 Pod 会自动调度到新节点
2.4.3 组合使用------多条件调度
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-store
spec:
replicas: 1
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx
逻辑:
- 与
app=store的 Pod 放在同一个节点上(亲和性) - 但同一节点不能放两个
app=web-store的 Pod(反亲和性)
三、污点与容忍度(Taint & Toleration)
3.1 概念对比
| 概念 | 设置方 | 作用方向 | 比喻 |
|---|---|---|---|
| nodeAffinity | Pod | Pod 选 Node | "我要去有钱的地方" |
| Taint | Node | Node 选 Pod | "我不要丑的" |
| Toleration | Pod | Pod 容忍 Node 的"挑剔" | "我不丑,让我进去" |
3.2 设置 Node Taint
语法 :kubectl taint nodes <node> <key>=<value>:<effect>
effect 三种类型:
| effect | 行为 |
|---|---|
NoSchedule |
新 Pod 不调度,已有 Pod 保留 |
PreferNoSchedule |
尽量不调度(软性) |
NoExecute |
立即驱逐不匹配的 Pod |
示例:
bash
root@master30:~# kubectl taint nodes worker31.whisky.cloud CPU=L1:NoSchedule
移除污点:
bash
root@master30:~# kubectl taint nodes worker31.whisky.cloud CPU:NoSchedule-
3.3 设置 Pod Toleration
语法:
yaml
tolerations:
- key: "CPU"
operator: "Equal" # Equal 或 Exists
value: "L1"
effect: "NoSchedule"
operator 两种类型:
| operator | 匹配规则 |
|---|---|
Equal(默认) |
键、值、效果必须完全匹配 |
Exists |
只匹配键和效果(不检查值) |
特殊用法:容忍所有污点
yaml
tolerations:
- operator: "Exists"
3.4 匹配规则详解
多个污点与多个容忍度的过滤逻辑:
- 遍历节点的所有污点
- 过滤掉 Pod 有匹配容忍度的污点
- 检查剩余的污点:
- 只要有
NoSchedule→ Pod 不能调度 - 只有
PreferNoSchedule→ 尝试不调度 - 只要有
NoExecute→ 驱逐(如已在节点上)
- 只要有
实验一:单污点匹配
bash
# 1. 设置污点
root@master30:~# kubectl taint nodes worker31.whisky.cloud CPU=L1:NoSchedule
# 2. 创建 Deployment(无容忍度)
root@master30:~# kubectl apply -f deploy-without-toleration.yaml
root@master30:~# kubectl get pods
web-5bf7b7b98f-kh7hv 0/1 Pending
# 3. 创建 Deployment(有容忍度)
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
tolerations:
- key: "CPU"
operator: "Equal"
value: "L1"
effect: "NoSchedule"
containers:
- name: nginx
image: nginx
bash
root@master30:~# kubectl apply -f deploy-with-tolerations.yaml
root@master30:~# kubectl get pods -o wide
web-7c99d767-w9rzt Running worker31.whisky.cloud
web-7c99d767-wwjhk Running worker31.whisky.cloud
实验二:多个污点匹配
bash
# 1. 给 worker31 添加 3 个污点
root@master30:~# kubectl taint nodes worker31.whisky.cloud CPU=L1:NoSchedule
root@master30:~# kubectl taint nodes worker31.whisky.cloud CPU=L1:NoExecute
root@master30:~# kubectl taint nodes worker31.whisky.cloud MEM=L2:NoSchedule
# 2. Pod 只容忍前 2 个污点(缺少 MEM=L2 的容忍度)
# → Pod Pending
yaml
tolerations:
- key: "CPU"
operator: "Equal"
value: "L1"
effect: "NoSchedule"
- key: "CPU"
operator: "Equal"
value: "L1"
effect: "NoExecute"
# 缺少 MEM=L2 的容忍度
bash
# 3. Pod 容忍全部 3 个污点 → Pod 调度成功
yaml
tolerations:
- key: "CPU"
operator: "Equal"
value: "L1"
effect: "NoSchedule"
- key: "CPU"
operator: "Equal"
value: "L1"
effect: "NoExecute"
- key: "MEM"
operator: "Equal"
value: "L2"
effect: "NoSchedule"
3.5 tolerationSeconds------延迟驱逐
当污点的 effect 为 NoExecute 时,tolerationSeconds 可指定容忍时长。
yaml
tolerations:
- key: "node.kubernetes.io/not-ready"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 300 # 300 秒后开始驱逐
含义:节点变为 NotReady 后,Pod 在节点上继续运行 300 秒,之后才被驱逐。
3.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 |
网络不可用 |
Kubernetes 自动添加的容忍度(针对普通 Pod):
yaml
tolerations:
- key: "node.kubernetes.io/not-ready"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 300
- key: "node.kubernetes.io/unreachable"
operator: "Exists"
effect: "NoExecute"
tolerationSeconds: 300
这意味着:节点故障时,Pod 默认停留 5 分钟才被驱逐。
DaemonSet Pod 的特殊容忍度(无 tolerationSeconds):
- 不受 not-ready / unreachable 污点影响,永不驱逐
四、节点管理------cordon / drain / uncordon
4.1 cordon------标记节点不可调度
cordon 标记节点为 SchedulingDisabled,阻止新 Pod 调度到该节点。
bash
root@master30:~# kubectl cordon worker31.whisky.cloud
root@master30:~# kubectl get nodes
worker31.whisky.cloud Ready,SchedulingDisabled <none> 9d
测试:
bash
root@master30:~# kubectl create deployment web --image=nginx --replicas=2
root@master30:~# kubectl get pods -o wide
web-59b9bb7664-flhvx Running worker32.whisky.cloud # 不在 worker31
web-59b9bb7664-ss586 Running worker32.whisky.cloud
4.2 uncordon------恢复节点调度
bash
root@master30:~# kubectl uncordon worker31.whisky.cloud
worker31.whisky.cloud Ready <none> 9d
4.3 drain------安全驱逐节点所有 Pod
drain 用于节点维护前安全驱逐所有 Pod。
执行流程:
- 自动执行
cordon - 驱逐节点上所有 Pod(DaemonSet 除外)
- 等待 Pod 在其他节点重建
bash
# 直接执行 drain(会报错,因为 DaemonSet 未处理)
root@master30:~# kubectl drain worker31.whisky.cloud
error: cannot delete DaemonSet-managed Pods
(use --ignore-daemonsets to ignore): kube-system/calico-node-xxx, kube-system/kube-proxy-xxx
# 正确执行
root@master30:~# kubectl drain worker31.whisky.cloud --ignore-daemonsets
node/worker31.whisky.cloud cordoned
evicting pod whisky/web-59b9bb7664-jkb5q
pod/web-59b9bb7664-jkb5q evicted
node/worker31.whisky.cloud drained
常用参数:
| 参数 | 作用 |
|---|---|
--ignore-daemonsets |
忽略 DaemonSet 管理的 Pod(必须) |
--force |
强制驱逐无法正常终止的 Pod |
--delete-emptydir-data |
删除 emptyDir 数据 |
--grace-period=30 |
优雅终止宽限期 |
与 PodDisruptionBudget 配合:
- drain 操作会遵守 PodDisruptionBudget(PDB)
- 如果驱逐 Pod 会导致可用副本数低于 PDB 要求,drain 会等待
- 确保关键应用在节点维护期间保持可用
并行 drain 多个节点:
- 可同时在不同终端执行
kubectl drain - 受 PodDisruptionBudget 约束,确保应用可用性
五、总结与知识点一览表
5.1 调度方式优先级对比
| 方式 | 优先级 | 适用场景 | 推荐度 |
|---|---|---|---|
| nodeName | 最高 | 单节点测试 | ❌ 不推荐 |
| nodeSelector | 高 | 根据节点标签调度 | ✅ 推荐 |
| nodeAffinity(required) | 高 | 硬性约束 | ✅ 推荐 |
| nodeAffinity(preferred) | 中 | 软性偏好 | ✅ 推荐 |
| podAffinity | 中 | 就近部署 | 按需使用 |
| podAntiAffinity | 中 | 分散部署 | 按需使用 |
5.2 污点与容忍度匹配规则
| 条件 | 结果 |
|---|---|
| 无未容忍的 NoSchedule 污点 | Pod 可以调度 |
| 存在未容忍的 NoSchedule 污点 | Pod 不能调度 |
| 仅有未容忍的 PreferNoSchedule 污点 | 优先不调度 |
| 存在未容忍的 NoExecute 污点 | Pod 被驱逐 |
5.3 节点管理命令速查
| 命令 | 作用 | 适用场景 |
|---|---|---|
kubectl cordon <node> |
标记节点不可调度 | 节点维护前准备 |
kubectl uncordon <node> |
恢复节点调度 | 维护完成后 |
kubectl drain <node> --ignore-daemonsets |
驱逐所有 Pod | 节点维护 |
5.4 内置污点与默认容忍度
| 污点 | 默认容忍时间 | 说明 |
|---|---|---|
not-ready |
300 秒 | 节点未就绪 |
unreachable |
300 秒 | 节点不可达 |
memory-pressure |
无 | 不自动添加容忍度 |
disk-pressure |
无 | 不自动添加容忍度 |
5.5 常见错误排查
| 错误 | 原因 | 解决方法 |
|---|---|---|
| Pod 持续 Pending | 无可用节点 | kubectl describe pod 查看事件 |
| nodeSelector 不生效 | 节点标签不匹配 | 检查节点标签 kubectl get node --show-labels |
| drain 失败 | DaemonSet 未忽略 | 添加 --ignore-daemonsets |
| 节点已标记 SchedulingDisabled | 上次维护未恢复 | kubectl uncordon <node> |
| Pod 被驱逐后启动慢 | 镜像拉取慢或资源不足 | 检查镜像大小和节点资源 |
下一期预告:kubernetes(K8s)学习笔记(第十期):集群资源与自动扩缩------ResourceQuota + LimitRange + HPA。涵盖:资源请求与限制、LimitRange 默认值、ResourceQuota 命名空间配额、Horizontal Pod Autoscaler 自动扩缩容。
--- Compiled and Authored by Whisky --- July 1 st, 2026