第 37 篇 k8s之调度进阶:亲和性、污点与容忍

发布时间:2026/6/4 1:56:58

第 37 篇 k8s之调度进阶:亲和性、污点与容忍 IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章助你少走弯路。在第 36 篇中我们学会了用 Requests 和 Limits 控制 Pod 的资源使用Scheduler 会依据资源请求自动将 Pod 调度到合适的节点。但资源充足只是调度的最低要求。在生产环境中你往往需要更精细地决定 Pod 应该落在哪些节点上——比如Redis 这类磁盘敏感的服务能否强制调度到挂载了 SSD 的节点多个 Flask 副本能否让它们分布在不同的节点以避免单点故障某些节点是否为 GPU 计算专用普通业务 Pod 不得进入Docker Compose 完全不需要考虑这些问题因为它只在一台机器上运行。而 Kubernetes 管理的是一个节点池调度策略直接影响资源利用率、容灾能力和运维复杂度。今天这篇我们就把 K8s 调度的三大利器——亲和性、污点与容忍——彻底搞懂并将它们应用到贯穿案例的 Flask Redis 应用中。一、节点亲和性Node Affinity吸引 Pod 到指定节点1.1 为什么需要节点亲和性默认情况下Scheduler 会根据 Requests 与节点的可分配资源进行匹配Pod 可能落在任何符合条件的节点上。但如果你需要“让 Redis 始终使用 SSD 磁盘的节点”或者“让某些服务只在指定的 GPU 节点上运行”就需要节点亲和性。节点亲和性像是一种吸引力规则——节点上带有特定的标签Pod 则通过affinity声明“我喜欢带有这些标签的节点”。1.2 节点标签首先你需要给节点打上标签kubectl labelnodeminikubedisktypessd查看标签kubectl getnodeminikube --show-labels# NAME STATUS ROLES AGE VERSION LABELS# minikube Ready control-plane 1d v1.31.0 ...,disktypessd1.3 硬亲和required与软亲和preferred节点亲和性分为两种requiredDuringSchedulingIgnoredDuringExecution硬亲和必须满足。如果没有任何节点符合条件Pod 将无法调度一直处于 Pending。preferredDuringSchedulingIgnoredDuringExecution软亲和倾向满足。Scheduler 会优先调度到符合条件的节点但如果没有也可以落在其他节点上。两者的名字中都有IgnoredDuringExecution意思是“一旦 Pod 已经运行节点标签的变化不会驱逐正在运行的 Pod”。如果需要在标签变化时强制驱逐需要更复杂的机制如自定义控制器。1.4 示例将 Redis 强制调度到 SSD 节点apiVersion: v1 kind: Pod metadata: name: redis-ssd spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disktype operator: In values: - ssd containers: - name: redis image: redis:alpine解释matchExpressions可以有多个条件它们之间是 AND 关系。operator: In标签的值必须在values列表中。还有NotIn、Exists、DoesNotExist、Gt大于、Lt小于等。如果 Minikube 只有单节点并已打上disktypessdPod 会正常调度。部署后查看调度kubectl get pod redis-ssd-owide# NAME READY STATUS RESTARTS AGE IP NODE# redis-ssd 1/1 Running 0 10s 10.244.1.15 minikube如果没有匹配节点Pod 会 Pendingkubectl describe pod redis-ssd|grep-A5Events# Warning FailedScheduling ... 0/1 nodes are available: 1 node(s) didnt match node selector.1.5 软亲和示例preferredDuringSchedulingIgnoredDuringExecution: - weight:100preference: matchExpressions: - key: disktype operator: In values: - ssdweight是权重1-100Scheduler 会根据权重对节点打分。如果集群中有多个节点权重越高的条件越优先被满足。二、Pod 亲和性与反亲和性Pod Affinity/Anti-affinityPod 之间的拓扑关系2.1 什么是 Pod 亲和性节点亲和性解决了“Pod 与节点的关系”但实际中更常见的是“Pod 与 Pod 的关系”。例如Pod 亲和性把 Web 应用调度到缓存所在节点的附近降低延迟。Pod 反亲和性将同一服务的多个副本分散到不同节点上避免单节点故障导致服务全部不可用。Pod 亲和性与反亲和性通过podAffinity和podAntiAffinity实现规则基于 Pod 的标签并且可以根据拓扑域topologyKey来控制亲和性的粒度。常见的topologyKey有kubernetes.io/hostname节点级别、topology.kubernetes.io/zone可用区级别。2.2 示例让 Flask 靠近 RedisPod 亲和性假设你有一个 Redis Pod 带着标签appredis你希望 Flask Pod 尽量与它在同一个节点上。首先创建 Redis Pod 并打标签如果还未创建kubectl run redis--imageredis:alpine--labelsappredis现在创建一个 Flask Deployment声明 Pod 亲和性apiVersion: apps/v1 kind: Deployment metadata: name: flask-affinity spec: replicas:2selector: matchLabels: app: flask-counter template: metadata: labels: app: flask-counter spec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - redis topologyKey: kubernetes.io/hostname containers: - name: flask image: flask-redis-counter:3.0 ports: - containerPort:5000这里requiredDuringSchedulingIgnoredDuringExecution要求 Flask Pod 必须与拥有标签appredis的 Pod 调度在同一个hostname上。如果 Redis Pod 不存在或没有相同拓扑的节点Flask Pod 无法调度。在 Minikube 单节点中这始终成立。在多节点集群中你会看到 Flask Pod 被拉到 Redis 所在的节点。2.3 示例让 Flask 副本分散部署Pod 反亲和性为了高可用我们希望多个 Flask 副本不要挤在同一个节点上。使用 Pod 反亲和性affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - flask-counter topologyKey: kubernetes.io/hostname这段配置要求如果某节点上已存在带有appflask-counter标签的 Pod则新 Pod 不能再调度到同一hostname上。这就强制了每个节点最多只运行一个副本。优点彻底的故障隔离一个节点宕机只会影响一个副本。缺点如果你的副本数超过节点数多余的 Pod 将无法调度处于 Pending 状态。此时应考虑使用软反亲和性preferredDuringSchedulingIgnoredDuringExecution允许在必要时放宽限制。2.4 topologyKey 的选择kubernetes.io/hostname节点级分布。topology.kubernetes.io/zone可用区级分布需要云平台支持防止整个可用区故障。自定义标签如rack、failure-domain。在多副本有状态应用如数据库集群中反亲和性结合topologyKey: hostname可以确保每个副本都独占一个节点避免资源竞争和单点故障。三、污点与容忍Taints and Tolerations排斥与准入3.1 污点的概念如果说亲和性是“吸引”那么污点就是“排斥”。节点上的污点Taint会阻止没有相应容忍Toleration的 Pod 被调度到该节点上甚至驱逐已在运行的 Pod。污点有三个要素key、value、effect。effect有三种NoSchedule不调度新的 Pod但不影响已在运行的 Pod。PreferNoSchedule尽量不调度新的 Pod但不强制。NoExecute不仅不调度新的 Pod还会驱逐已经运行的、没有相应容忍的 Pod。3.2 给节点添加污点# 给节点 minikube 添加污点专用 GPUkubectl taintnodeminikubegputrue:NoSchedule查看污点kubectl describenodeminikube|grepTaints# Taints: gputrue:NoSchedule现在普通 Pod 将无法调度到这个节点上除非它们有容忍。这可以用来保护专用节点。3.3 Pod 容忍污点如果想让某个 Pod 能够调度到有污点的节点需要在 Pod 中配置容忍tolerations: - key:gpuoperator:Equalvalue:trueeffect:NoSchedule你也可以容忍所有污点tolerations: - operator:Exists但谨慎使用可能让 Pod 跑到不合适的节点上。3.4 污点的典型场景GPU 专用节点给节点打上gputrue:NoSchedule只有请求了 GPU 资源的 Pod 配置了对应容忍。控制平面隔离默认 master 节点有污点node-role.kubernetes.io/control-plane:NoSchedule防止普通业务 Pod 调度上去。节点维护在维护前给节点添加NoExecute污点Pod 会自动被驱逐迁移。3.5 移除污点kubectl taintnodeminikubegputrue:NoSchedule-注意末尾的-表示移除污点。四、实战为 Flask Redis 配置调度策略现在将亲和性和反亲和性应用到贯穿案例中。我们需要确保 Redis 调度到有标签tierbackend的节点节点亲和性。确保 Flask 副本尽量与 Redis 在同一可用区Pod 亲和性但副本之间要分散在节点上Pod 反亲和性。为 Redis 节点添加污点只允许 Redis 类 Pod 调度可选。4.1 准备节点Minikube 单节点做不了完整的多节点演示但我们可以练习配置。先给节点加标签和污点用于学习不要影响其他 Pod可使用PreferNoSchedule。kubectl labelnodeminikubetierbackend kubectl taintnodeminikubetierbackend:PreferNoSchedule4.2 创建 Redis Deployment 带节点亲和性和容忍apiVersion: apps/v1 kind: Deployment metadata: name: redis-backend spec: replicas:1selector: matchLabels: app: redis tier: backend template: metadata: labels: app: redis tier: backend spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: tier operator: In values: - backend tolerations: - key:tieroperator:Equalvalue:backendeffect:PreferNoSchedulecontainers: - name: redis image: redis:alpine4.3 创建 Flask Deployment 带 Pod 亲和性和反亲和性apiVersion: apps/v1 kind: Deployment metadata: name: flask-backend spec: replicas:3selector: matchLabels: app: flask-counter template: metadata: labels: app: flask-counter spec: affinity:# 亲和性靠近 RedispodAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight:100podAffinityTerm: labelSelector: matchExpressions: - key: tier operator: In values: - backend topologyKey: kubernetes.io/hostname# 反亲和性副本分散podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight:100podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - flask-counter topologyKey: kubernetes.io/hostname containers: - name: flask image: flask-redis-counter:3.0 ports: - containerPort:5000resources: requests: cpu:100mmemory:128Milimits: cpu:500mmemory:256Mi我们使用了preferredDuringSchedulingIgnoredDuringExecution软亲和来避免单节点环境中因为满足不了硬条件而无法调度。在生产多节点环境中可以切换为required。部署后查看 Pod 分布单节点全部在 minikube4.4 验证亲和性和容忍效果检查事件确认调度器考虑了亲和性kubectl describe podflask-pod|grep-A5Affinity由于 Minikube 单节点我们难以看到分散效果但配置可以无缝迁移到多节点集群。4.5 清理污点kubectl taintnodeminikubetierbackend:PreferNoSchedule-五、与 Docker Compose 的对比Compose 没有调度概念所有容器都在同一台宿主机上你可以通过depends_on控制启动顺序但不能控制容器在主机内的分布单机不存在分布问题。K8s 的亲和性和污点机制为多节点集群提供了细粒度的调度控制这是编排平台走向大规模生产的必然需求。六、命令速查表七、本篇总结节点亲和性通过nodeAffinity将 Pod 调度到带有特定标签的节点上支持硬性和软性两种策略。Pod 亲和性与反亲和性通过podAffinity和podAntiAffinity控制 Pod 之间的拓扑关系实现拉近或分散部署提高性能或可用性。污点与容忍通过Taint和Toleration实现节点的专属访问控制防止无关 Pod 调度支持NoSchedule、PreferNoSchedule和NoExecute效果。调度策略的实际价值合理的亲和性和反亲和性配置是实现高可用部署、资源隔离和性能优化的关键手段也是 K8s 集群运维中常被忽略的细节。通过本篇你获得了控制 Pod 落点的完整工具集。下一篇文章——第 38 篇安全RBAC 与 ServiceAccount 实战——我们将进入 K8s 安全领域学习如何通过基于角色的访问控制来保护集群资源。想了解更多还可以去各个平台搜索「IT策士」一起升级 IT 思维

相关新闻