Kubernetes 调度器详解:从 nodeName 到污点容忍

实验环境:Kubernetes v1.35.3,1 Master + 2 Node,Flannel 网络


一、nodeName:手动指定调度节点

默认情况下 Pod 由调度器自动分配节点,而 nodeName 可以直接指定 Pod 运行在哪个节点上。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx

不指定 nodeName 时,调度器自动选择节点:

bash 复制代码
kubectl apply -f nginx.yml
kubectl get pods -o wide
text 复制代码
NAME    READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          56s   10.244.166.131   node1   <none>           <none>

指定 nodeName: node2 后,Pod 固定调度到 node2:

yaml 复制代码
spec:
  nodeName: node2        # 指定调度节点
  containers:
  - image: nginx
    name: nginx
text 复制代码
NAME    READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          7s    10.244.104.2   node2   <none>           <none>

nodeName 是最强制的调度方式,绕过了调度器,直接由 kubelet 在目标节点创建 Pod。适用于特殊情况,一般不推荐用于日常调度。


二、nodeSelector:按节点标签选择

先给节点打标签,再用 nodeSelector 匹配标签调度。

yaml 复制代码
spec:
  nodeSelector:
    app: timinglee
  containers:
  - image: nginx
    name: nginx

没有节点带 app=timinglee 标签时,Pod 一直 Pending:

text 复制代码
NAME    READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
nginx   0/1     Pending   0          8s    <none>   <none>   <none>           <none>

给 node2 打上标签后,Pod 立即调度成功:

bash 复制代码
kubectl label nodes node2 app=timinglee
kubectl get nodes --show-labels
# node2 的 LABELS 出现 app=timinglee
text 复制代码
NAME    READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          96s   10.244.104.3   node2   <none>           <none>

删除标签:kubectl label nodes node2 app-,已运行的 Pod 不受影响(nodeSelector 只在调度时生效)。


三、节点亲和(nodeAffinity)

nodeSelector 只能做简单的等于匹配,nodeAffinity 支持更丰富的表达能力,有两种策略:

1. preferredDuringSchedulingIgnoredDuringExecution:倾向满足(软策略)

优先调度到满足条件的节点,没有满足条件的节点也会调度(只是权重偏好)。

yaml 复制代码
spec:
  containers:
  - image: nginx
    name: nginx
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - preference:
          matchExpressions:
          - key: disk
            operator: In
            values:
            - ssd
            - iscsi
        weight: 50

没有节点带 disk=ssd/iscsi 标签时,Pod 依然可以调度(落在 node1):

text 复制代码
NAME    READY   STATUS    RESTARTS   AGE    IP               NODE    NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          2m3s   10.244.166.132   node1   <none>           <none>

给 node2 打上 disk=ssd 标签后,Pod 倾向调度到 node2:

bash 复制代码
kubectl label nodes node2 disk=ssd
text 复制代码
NAME    READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          5s    10.244.104.4   node2   <none>           <none>

2. requiredDuringSchedulingIgnoredDuringExecution:必须满足(硬策略)

条件不满足时 Pod 不会被调度(一直 Pending)。

yaml 复制代码
spec:
  containers:
  - image: nginx
    name: nginx
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disk
            operator: In
            values:
            - ssd
            - iscsi

条件满足(node2 有 disk=ssd),调度成功:

text 复制代码
NAME    READY   STATUS    RESTARTS   AGE   IP             NODE    NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          50s   10.244.104.5   node2   <none>           <none>

配合 NotIn 反向选择 ------排除有 disk=ssd/iscsi 标签的节点,Pod 被调度到 node1:

yaml 复制代码
          - key: disk
            operator: NotIn      # 反向选择
            values:
            - ssd
            - iscsi
text 复制代码
NAME    READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          8s    10.244.166.133   node1   <none>           <none>

四、Pod 亲和(podAffinity)

Pod 亲和让新的 Pod 尽量/必须与满足条件的 Pod 调度到同一个拓扑域 (这里用 topologyKey: kubernetes.io/hostname,即同一节点)。

bash 复制代码
kubectl create deployment webcluster --image nginx --replicas 2 --dry-run=client -o yaml > webcluster.yml
yaml 复制代码
spec:
  containers:
  - image: nginx
    name: nginx
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - webcluster
        topologyKey: "kubernetes.io/hostname"

两个副本都调度到了同一节点 node1:

text 复制代码
NAME                          READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
webcluster-65b86bcdbb-jhb2b   1/1     Running   0          35s   10.244.166.136   node1   <none>           <none>
webcluster-65b86bcdbb-z4mt8   1/1     Running   0          35s   10.244.166.135   node1   <none>           <none>

五、Pod 反亲和(podAntiAffinity)

Pod 反亲和与亲和相反:新的 Pod 必须与满足条件的 Pod 分布在不同的拓扑域(不同节点)。

yaml 复制代码
spec:
  replicas: 3
  ...
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - webcluster
        topologyKey: "kubernetes.io/hostname"

3 个副本分布在 node1、node2 各一个,第三个因为反亲和条件找不到可用节点而 Pending

text 复制代码
NAME                          READY   STATUS    RESTARTS   AGE   IP               NODE     NOMINATED NODE   READINESS GATES
webcluster-85f7868466-srhgf   1/1     Running   0          74s   10.244.104.7     node2    <none>           <none>
webcluster-85f7868466-t8gk6   1/1     Running   0          74s   10.244.166.137   node1    <none>           <none>
webcluster-85f7868466-x24wj   0/1     Pending   0          22s   <none>           <none>   <none>           <none>

Pending 的 Pod 注释会提示 0/2 nodes are available: 2 node(s) didn't match pod anti-affinity rules


六、节点的污点设定(Taint)

污点是打在节点上的排斥标记,默认 Pod 不会调度到带污点的节点。三种效果:

效果 行为
NoSchedule 新的 Pod 不调度到该节点(已运行的不受影响)
NoExecute 新的不调度 + 驱逐 已运行的 Pod
PreferNoSchedule 尽量不调度(软策略),没有其他节点时也会调度

NoExecute:立即驱逐

bash 复制代码
kubectl taint node node1 nodetype=badnode:NoExecute

原本在 node1 上的 Pod 被立即驱逐,重新调度到 node2:

text 复制代码
NAME                          READY   STATUS    RESTARTS   AGE     IP             NODE    NOMINATED NODE   READINESS GATES
webcluster-77f8775f47-4bgj6   1/1     Running   0          8m52s   10.244.104.8   node2   <none>           <none>
webcluster-77f8775f47-zrw2r   1/1     Running   0          5m6s    10.244.104.9   node2   <none>           <none>

NoSchedule:拒绝新调度

bash 复制代码
kubectl taint node node2 nodetype=badnode:NoSchedule

删除并重建 Deployment,两个 Pod 全部调度到没有污点的 node1:

text 复制代码
NAME                          READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
webcluster-77f8775f47-6mkkr   1/1     Running   0          16s   10.244.166.140   node1   <none>           <none>
webcluster-77f8775f47-c84z9   1/1     Running   0          16s   10.244.166.139   node1   <none>           <none>

PreferNoSchedule:尽量避免

bash 复制代码
kubectl taint node node2 nodetype=badnode:PreferNoSchedule

软策略下 Pod 仍倾向调度到 node1,但配合反亲和时可能容忍。


七、污点容忍(Toleration)

容忍是打在 Pod 上的标记,用来"忍受"节点的污点。先给两个节点设置污点:

bash 复制代码
kubectl taint node node1 name=lee:NoSchedule
kubectl taint node node2 nodetype=badnode:NoSchedule

不带容忍的 Pod 全部 Pending:

text 复制代码
NAME                          READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
webcluster-77f8775f47-fln2k   0/1     Pending   0          33s   <none>   <none>   <none>           <none>
webcluster-77f8775f47-k9bv8   0/1     Pending   0          33s   <none>   <none>   <none>           <none>

1. 精确容忍指定污点

operator: Equal 精确匹配 key=value 和 effect:

yaml 复制代码
spec:
  containers:
  - image: nginx
    name: nginx
  tolerations:               # 污点容忍
  - operator: Equal
    key: nodetype
    value: badnode
    effect: NoSchedule

Pod 精准调度到带 nodetype=badnode:NoSchedule 污点的 node2:

text 复制代码
NAME                          READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
webcluster-597586678c-9qvp8   1/1     Running   0          12s   10.244.104.14   node2   <none>           <none>
webcluster-597586678c-d2wvv   1/1     Running   0          12s   10.244.104.13   node2   <none>           <none>

2. 容忍所有 key 的 NoSchedule 污点

operator: Exists + 指定 effect,容忍所有 NoSchedule 类型的污点(不管 key 是什么):

yaml 复制代码
  tolerations:
  - operator: Exists
    effect: NoSchedule

3 个副本分布在 node1、node2、master:

text 复制代码
NAME                          READY   STATUS    RESTARTS   AGE   IP               NODE     NOMINATED NODE   READINESS GATES
webcluster-549c6784f6-58zh9   1/1     Running   0          8s    10.244.104.15    node2    <none>           <none>
webcluster-549c6784f6-d88tz   1/1     Running   0          8s    10.244.219.67    master   <none>           <none>
webcluster-549c6784f6-zv2pm   1/1     Running   0          8s    10.244.166.144   node1    <none>           <none>

3. 容忍所有污点

只写 operator: Exists,不限定 effect,容忍一切污点(慎用,通常只在系统级 DaemonSet 使用):

yaml 复制代码
  tolerations:
  - operator: Exists

3 个副本分布到全部节点:

text 复制代码
NAME                          READY   STATUS    RESTARTS   AGE   IP               NODE     NOMINATED NODE   READINESS GATES
webcluster-5df57d554b-6gdcc   1/1     Running   0          12s   10.244.219.68    master   <none>           <none>
webcluster-5df57d554b-cj8fd   1/1     Running   0          12s   10.244.166.145   node1    <none>           <none>
webcluster-5df57d554b-mwcfx   1/1     Running   0          12s   10.244.104.16    node2    <none>           <none>


图 1:污点与容忍实验------容忍后 Pod 分布到全部节点


总结

调度方式 特点 适用场景
nodeName 强制指定节点,绕过调度器 特殊单节点需求
nodeSelector 按标签简单匹配 标签调度入门
nodeAffinity 软/硬策略 + In/NotIn 复杂节点选择
podAffinity 与指定 Pod 同拓扑域 就近调度、应用内聚
podAntiAffinity 与指定 Pod 异拓扑域 高可用分散部署
Taint 节点排斥 Pod 节点隔离、专用节点
Toleration Pod 容忍污点 允许特定 Pod 调度到污点节点

调度是 Kubernetes 把 Pod 放到合适节点的核心机制。nodeSelector 管 "要什么节点",Taint/Toleration 管 "不要哪些 Pod 进来",亲和与反亲和则负责更精细的拓扑分布------理解这几者的组合,是设计高可用、高性能工作负载的基础。

相关推荐
吴长建先生重名了1 小时前
ElasticSearch 检索系统性能优化实战:基准测试
java
西索斯coding1 小时前
doubao-seed-2.1-turbo 调用一直 401 怎么办?pro 版同样的 Key 却正常——5 分钟排查定位指南
java·服务器·数据库·ai
旧梦95271 小时前
Java SortedMap 接口详解:从入门到实战
java·开发语言
莫陌尛.1 小时前
Java_this构造方法
java·开发语言
2601_962297252 小时前
C# vs Java vs Python:YOLO工业部署性能对比实战
java·python·c·工业视觉·性能对比
计算机毕设定制辅导-无忧学长2 小时前
《基于SpringBoot的马术俱乐部管理系统》
java·spring boot·后端
Dreams°1232 小时前
【Java后端+Vue前后端分离:内网正常、公网访问异常|5个高频经典踩坑完整复盘】
java·开发语言·vue.js
名字还没想好☜2 小时前
Docker buildx 多架构镜像实战:一次构建 amd64+arm64,告别 exec format error
运维·docker·eureka·架构·kubernetes
学长毕业设计2 小时前
基于SpringBoot的民间艺术传承管理系统(源码+文档+讲解视频)
java·spring boot·后端