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 运行的节点名称,优先级最高,调度器会完全跳过调度逻辑。

示例:

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
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 调度到哪里。

查看节点标签:

root@master30:~# kubectl get node --show-labels

给节点打标签:

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:

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
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

删除标签后:

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

完整示例:

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 的值为 L1 或 L2 的节点
  2. 软性偏好:在这些节点中,优先选择标签 MEM 的值为 L1 或 L2 的节点
  3. weight 取值范围 1-100,值越大优先级越高

实验验证:

# 阶段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 相同的拓扑域中。

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

实验:

# 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 分散到不同拓扑域,提高可用性。

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
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 组合使用——多条件调度
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 概念对比

概念设置方作用方向比喻
nodeAffinityPodPod 选 Node“我要去有钱的地方”
TaintNodeNode 选 Pod“我不要丑的”
TolerationPodPod 容忍 Node 的“挑剔”“我不丑,让我进去”

3.2 设置 Node Taint

语法:kubectl taint nodes <node> <key>=<value>:<effect>

effect 三种类型:

effect行为
NoSchedule新 Pod 不调度,已有 Pod 保留
PreferNoSchedule尽量不调度(软性)
NoExecute立即驱逐不匹配的 Pod

示例:

root@master30:~# kubectl taint nodes worker31.whisky.cloud CPU=L1:NoSchedule

移除污点:

root@master30:~# kubectl taint nodes worker31.whisky.cloud CPU:NoSchedule-

3.3 设置 Pod Toleration

语法:

tolerations:
- key: "CPU"
  operator: "Equal"   # Equal 或 Exists
  value: "L1"
  effect: "NoSchedule"

operator 两种类型:

operator匹配规则
Equal(默认)键、值、效果必须完全匹配
Exists只匹配键和效果(不检查值)

特殊用法:容忍所有污点

tolerations:
- operator: "Exists"

3.4 匹配规则详解

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

  1. 遍历节点的所有污点
  2. 过滤掉 Pod 有匹配容忍度的污点
  3. 检查剩余的污点:
    • 只要有 NoSchedule → Pod 不能调度
    • 只有 PreferNoSchedule → 尝试不调度
    • 只要有 NoExecute → 驱逐(如已在节点上)

实验一:单污点匹配

# 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(有容忍度)
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
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

实验二:多个污点匹配

# 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
tolerations:
- key: "CPU"
  operator: "Equal"
  value: "L1"
  effect: "NoSchedule"
- key: "CPU"
  operator: "Equal"
  value: "L1"
  effect: "NoExecute"
# 缺少 MEM=L2 的容忍度
# 3. Pod 容忍全部 3 个污点 → Pod 调度成功
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 可指定容忍时长。

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-pressurePID 压力
node.kubernetes.io/network-unavailable网络不可用

Kubernetes 自动添加的容忍度(针对普通 Pod):

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 调度到该节点。

root@master30:~# kubectl cordon worker31.whisky.cloud
root@master30:~# kubectl get nodes
worker31.whisky.cloud   Ready,SchedulingDisabled   <none>   9d

测试:

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——恢复节点调度

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 在其他节点重建
# 直接执行 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-ready300 秒节点未就绪
unreachable300 秒节点不可达
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
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐