K8s 运维系列 | 第 8 天:调度机制——节点选择、污点与容忍度

在 Kubernetes 集群中,我们很少直接指定某个 Pod 运行在哪一台物理节点上。相反,kube-scheduler 这个核心组件会根据资源、策略与约束条件,自动为每一个 Pod 挑选一个合适的节点。理解调度机制,是做好生产环境资源隔离、故障隔离与性能优化的基础。今天这一篇,我们就深入讲解节点选择(nodeSelector)、亲和性(Affinity)、污点(Taints)与容忍度(Tolerations),并配合完整实战演练。

K8s 运维 第8天

一、调度器是如何工作的

kube-scheduler 的调度流程可以概括为两个阶段:过滤(Filtering)与打分(Scoring)。

  1. 过滤阶段:调度器遍历所有可用节点,排除掉那些不满足 Pod 硬性条件的节点,例如资源不足、节点带有 Pod 无法容忍的污点、节点选择器不匹配、端口冲突等。
  2. 打分阶段:调度器对通过过滤的节点进行评分,综合考虑资源空闲程度、亲和性偏好、拓扑分布等因素,最终选出得分最高的节点。

当没有任何节点能满足要求时,Pod 会进入 Pending 状态,直到条件发生变化。因此,遇到 Pod 一直无法调度的情况,我们首先要检查的就是调度约束是否过于严格。

二、nodeSelector:最简单的节点选择

nodeSelector 是最基础、最直观的节点选择方式。它的原理非常简单:给节点打上标签,再让 Pod 通过 nodeSelector 指定必须匹配的标签。下面我们先给节点打标签:

# 查看当前所有节点
kubectl get nodes

# 给节点 node-01 打上磁盘类型标签
kubectl label nodes node-01 disktype=ssd

# 查看节点标签是否生效
kubectl get nodes node-01 --show-labels

接着,在 Pod 的 YAML 中通过 nodeSelector 引用这个标签:

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

nodeSelector 的优点是简单易用,缺点也很明显:它只能做”精确等于”的硬性匹配,无法表达”优先选择”或”避免选择”这类更灵活的需求。这时候就需要引入亲和性与反亲和性。

三、亲和性:更灵活的选择策略

亲和性(Affinity)分为节点亲和性(nodeAffinity)与 Pod 亲和性(podAffinity / podAntiAffinity)。节点亲和性是对 nodeSelector 的增强,支持更多操作符和”软硬”两级策略。

apiVersion: v1
kind: Pod
metadata:
  name: nginx-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: disktype
                operator: In
                values:
                  - ssd
                  - nvme
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 1
          preference:
            matchExpressions:
              - key: zone
                operator: In
                values:
                  - zone-a
  containers:
    - name: nginx
      image: nginx:1.24

上面的配置中,requiredDuringSchedulingIgnoredDuringExecution 是硬性要求:节点必须有 disktype 为 ssd 或 nvme 的标签,否则无法调度;preferredDuringSchedulingIgnoredDuringExecution 是软性偏好:优先调度到 zone-a 区域的节点,但如果实在没有,也不会因此调度失败。

四、污点与容忍度:反向约束

如果说 nodeSelector 和亲和性是”把 Pod 吸引到某些节点”,那么污点(Taint)与容忍度(Toleration)就是”把 Pod 从某些节点赶走”。污点标记在节点上,容忍度配置在 Pod 上,只有当 Pod 能够容忍某个污点时,它才可能被调度到带有该污点的节点上。

污点的格式为 key=value:effect,其中 effect 有三种取值:

  • NoSchedule:不允许新 Pod 调度到该节点,但已运行的 Pod 不受影响。
  • PreferNoSchedule:尽量不调度到该节点,属于软性限制。
  • NoExecute:不允许调度,并且已运行的 Pod 如果不容忍该污点,会被驱逐。

下面演示如何给节点打污点,以及如何删除污点:

# 给 node-02 打上污点,禁止一般 Pod 调度
kubectl taint nodes node-02 dedicated=production:NoSchedule

# 查看节点上的污点
kubectl describe node node-02 | grep -i taint

# 删除污点(注意结尾的减号)
kubectl taint nodes node-02 dedicated=production:NoSchedule-

然后,在需要运行到该专用节点上的 Pod 中配置对应的容忍度:

apiVersion: v1
kind: Pod
metadata:
  name: dedicated-pod
spec:
  tolerations:
    - key: dedicated
      operator: Equal
      value: production
      effect: NoSchedule
  containers:
    - name: app
      image: busybox:1.36
      command: ["sleep", "3600"]

需要特别注意的是,容忍度并不代表 Pod 一定被调度到带污点的节点,它只是”允许”Pod 可以调度过去;真正决定调度到哪里的,仍然是调度器的打分逻辑。

五、实战:搭建一套专用节点

接下来我们做一个完整的实战演练。假设集群中有普通业务节点和一台专门跑 GPU 任务的节点,我们希望 GPU 任务只能跑到专用节点上,而普通业务不能占用它。首先给 GPU 节点打标签和污点:

# 打标签
kubectl label nodes gpu-node-01 accelerator=nvidia-tesla

# 打污点,普通业务无法调度
kubectl taint nodes gpu-node-01 accelerator=nvidia-tesla:NoSchedule

然后部署 GPU 任务,同时使用 nodeSelector 和容忍度:

apiVersion: v1
kind: Pod
metadata:
  name: cuda-job
spec:
  nodeSelector:
    accelerator: nvidia-tesla
  tolerations:
    - key: accelerator
      operator: Equal
      value: nvidia-tesla
      effect: NoSchedule
  containers:
    - name: cuda
      image: nvidia/cuda:12.0.0-base-ubuntu22.04
      command: ["nvidia-smi"]

这样,普通业务 Pod 因为没有容忍度,无法调度到 GPU 节点;GPU 任务通过 nodeSelector 精确落到专用节点上,实现了资源隔离。验证结果:

# 确认 Pod 调度到了正确的节点
kubectl get pods cuda-job -o wide

# 查看 Pod 的调度事件
kubectl describe pod cuda-job | grep -A 5 Events

六、常见问题与排查

  1. Pod 一直处于 Pending 状态:用 kubectl describe pod <name> 查看 Events,通常会看到 “0/3 nodes are available: 3 node(s) had untolerated taint…” 或 nodeSelector 不匹配的提示,据此调整配置即可。

  2. 打了污点后 Pod 被立即驱逐:这是因为使用了 NoExecute 且 Pod 没有对应容忍度,检查污点的 effect 类型是否符合预期。

  3. 容忍度配置了却不生效:注意 key、value、effect 三者必须与污点完全匹配,operator 使用 Equal 时 value 不能写错,使用 Exists 时则无需指定 value。

  4. 软性策略没有按预期调度:preferred 只是偏好,调度器会综合权重打分,如果硬性条件已经满足,软性偏好可能被其他因素覆盖。

七、总结

本文系统地梳理了 Kubernetes 调度机制的四大核心工具:nodeSelector 适合最简单的精确匹配;节点亲和性提供了硬性与软性两级的灵活选择;污点与容忍度则反向实现了节点隔离与专用资源保护。在实际生产中,这些机制通常会组合使用,例如给 GPU 节点打标签加污点,再给 GPU 任务同时配置 nodeSelector 与容忍度,从而实现清晰的资源边界。理解并熟练运用调度机制,是走向 K8s 生产级运维的关键一步。

下期预告:第 9 天我们将讲解资源管理——Request/Limit 与 HPA 弹性伸缩,看看如何为 Pod 合理分配资源,并让集群在流量波动时自动扩缩容,敬请期待。

微信二维码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容