kubernetes(K8s)学习笔记(第九期):集群治理与控制(下篇):调度与节点管理——Scheduler + 污点容忍 + 节点维护

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

目录

  1. 调度概述
  2. 控制 Pod 运行位置
  3. 污点与容忍度(Taint & Toleration)
  4. 节点管理------cordon / drain / uncordon
  5. 总结与知识点一览表

一、调度概述

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 软性偏好,优先但不强制

操作符支持InNotInExistsDoesNotExistGtLt

完整示例

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

规则解读

  1. 硬性要求 :Pod 必须调度到标签 CPU 的值为 L1L2 的节点
  2. 软性偏好 :在这些节点中,优先选择标签 MEM 的值为 L1L2 的节点
  3. 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 匹配规则详解

多个污点与多个容忍度的过滤逻辑

  1. 遍历节点的所有污点
  2. 过滤掉 Pod 有匹配容忍度的污点
  3. 检查剩余的污点:
    • 只要有 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。

执行流程

  1. 自动执行 cordon
  2. 驱逐节点上所有 Pod(DaemonSet 除外)
  3. 等待 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