Kubernetes 调度器负责将 Pod 分配到合适的节点。默认调度策略基于资源请求和节点可用资源,但有时你需要更精细的控制:例如将数据库 Pod 调度到 SSD 节点,或将特殊服务调度到特定区域。本文介绍调度相关的四种策略:nodeSelector、节点亲和性(Node Affinity)、Pod 亲和性与反亲和性,以及污点(Taint)与容忍度(Toleration)。掌握这些技巧,你可以实现复杂的生产级调度需求。

一、nodeSelector:最简单的节点选择
通过给节点打标签,然后在 Pod 中指定 nodeSelector,Pod 将只调度到匹配标签的节点。

1.1 给节点打标签

kubectl label node node1 disktype=ssd
kubectl label node node2 disktype=hdd

1.2 Pod 中使用 nodeSelector

apiVersion: v1
kind: Pod
metadata:
  name: nginx-ssd
spec:
  nodeSelector:
    disktype: ssd
  containers:
  - name: nginx
    image: nginx

如果没有节点具有 disktype=ssd,Pod 将保持 Pending。

1.3 查看节点标签

kubectl get nodes --show-labels

二、节点亲和性(Node Affinity)
nodeSelector 只能做“硬约束”(必须满足),而节点亲和性支持:

硬约束(required):必须满足,否则不调度。

软约束(preferred):优先满足,但如果没有匹配节点,也可以调度到其他节点。

支持 In、NotIn、Exists、DoesNotExist、Gt、Lt 等操作符。

2.1 硬约束示例

apiVersion: v1
kind: Pod
metadata:
  name: nginx-required
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
          - key: region
            operator: In
            values:
            - us-east-1
  containers:
  - name: nginx
    image: nginx

注意:IgnoredDuringExecution 表示调度时强制执行,但 Pod 运行后若节点标签变化,不会驱逐 Pod。

2.2 软约束示例

affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      preference:
        matchExpressions:
        - key: disktype
          operator: In
          values:
          - ssd
    - weight: 50
      preference:
        matchExpressions:
        - key: region
          operator: In
          values:
          - us-east-1

权重(weight)范围为 1-100,调度器会计算每个节点的匹配分数,选择总分最高的节点。

2.3 常用操作符
In:标签值在列表中。

NotIn:标签值不在列表中。

Exists:节点存在该标签(不关心值)。

DoesNotExist:节点不存在该标签。

Gt(大于)和 Lt(小于):仅适用于整数值。

三、Pod 亲和性与反亲和性
Pod 亲和性用于将某些 Pod 调度到同一节点或同一拓扑域(如可用区),反亲和性用于让 Pod 互相远离(如避免单点故障)。

3.1 Pod 亲和性示例(同一节点)

apiVersion: v1
kind: Pod
metadata:
  name: webapp
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - cache
        topologyKey: kubernetes.io/hostname
  containers:
  - name: webapp
    image: nginx

该 Pod 将与带有标签 app=cache 的 Pod 调度到同一个节点(topologyKey=kubernetes.io/hostname 表示节点级别)。

3.2 Pod 反亲和性示例(分散到不同节点)

affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: app
          operator: In
          values:
          - web
      topologyKey: kubernetes.io/hostname

这确保 app=web 的 Pod 不会调度到已有相同标签 Pod 的节点上,常用于高可用部署(如多个副本分散到不同节点)。

3.3 topologyKey 的高级用法
kubernetes.io/hostname:节点级别。

topology.kubernetes.io/zone:可用区级别。

也可使用自定义标签(如 rack)。

四、污点(Taint)与容忍度(Toleration)
污点应用于节点,表示该节点“排斥”某些 Pod。容忍度应用于 Pod,表示 Pod 可以“容忍”节点上的污点。两者配合使用,可以实现:

专用节点(只允许特定 Pod 调度)。

特殊硬件节点(如 GPU 节点,只允许需要 GPU 的 Pod 调度)。

节点维护(使用 NoExecute 污点驱逐现有 Pod)。

4.1 添加污点

# 添加 NoSchedule 污点:不调度新 Pod,不影响现有 Pod
kubectl taint nodes node1 dedicated=gpu:NoSchedule

# 添加 NoExecute 污点:不仅不调度新 Pod,还会驱逐不匹配容忍度的现有 Pod
kubectl taint nodes node2 maintenance=true:NoExecute

# 添加 PreferNoSchedule:尽量不调度(软)
kubectl taint nodes node3 special=true:PreferNoSchedule

4.2 查看污点

kubectl describe node node1 | grep Taints

4.3 删除污点

kubectl taint nodes node1 dedicated:NoSchedule-

4.4 Pod 添加容忍度

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:11.0-base

operator 可以是 Equal(值需匹配)或 Exists(仅检查 key 存在)。

effect 对应污点的 effect,如果不指定,表示容忍所有 effect。

4.5 常见场景:专用节点
标记节点:kubectl label node node1 node-role=monitoring

添加污点:kubectl taint nodes node1 dedicated=monitoring:NoSchedule

为监控 Pod 添加容忍度,且通过 nodeSelector 或亲和性确保调度到该节点。

五、节点维护操作:cordon / drain
cordon:将节点标记为不可调度(但已有 Pod 继续运行)。

drain:驱逐节点上的所有 Pod(优雅终止),并将其调度到其他节点,同时标记为不可调度。

# 封锁节点(新 Pod 不会调度到该节点)
kubectl cordon node1

# 驱逐节点上的所有 Pod(会优雅终止并重新调度)
kubectl drain node1 --ignore-daemonsets --delete-emptydir-data

# 解封节点
kubectl uncordon node1

六、调度策略优先级总结
当多个调度约束同时存在时,调度器按以下顺序处理:

硬约束(required 亲和性、nodeSelector)必须满足。

软约束(preferred 亲和性)作为评分依据。

污点容忍度:Pod 必须容忍节点上所有的 NoSchedule 污点,否则不能调度。

资源请求(requests)也必须满足。

七、实战:将数据库调度到 SSD 节点,并保证副本分散

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: disktype
                operator: In
                values:
                - ssd
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - mysql
            topologyKey: kubernetes.io/hostname
      containers:
      - name: mysql
        image: mysql:8.0

必须调度到 disktype=ssd 节点。

同一节点上不会有两个 mysql Pod。

八、小结
调度策略是实现生产级部署的关键。nodeSelector 简单但功能有限;节点亲和性提供软硬约束;Pod 亲和性/反亲和性控制 Pod 间分布;污点和容忍度实现节点隔离。合理组合这些策略,可以满足绝大多数调度需求。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐