Kubernetes 默认调度器只在 Pod 创建的那一刻做一次调度决策,之后无论集群状态如何变化,这个决定都不会再被修改。这就带来一个常见问题:随着节点扩缩容、Pod 重建、资源请求调整,集群里的负载分布会逐渐失衡。Descheduler 是 Kubernetes SIG-Scheduling 子项目提供的重调度器,它会根据预设策略周期性扫描集群,把不符合预期的 Pod 驱逐出去,让这些 Pod 重新经历调度流程,最终落到更合适的节点上。本文从原理、策略到部署实战,完整讲解它的使用方法。

Descheduler 的工作原理与适用场景
首先要澄清一个容易误解的概念:Descheduler 本身不做调度,它只负责“驱逐”。当它发现某个 Pod 违反了配置的策略,就会调用 Kubernetes 的 Eviction API 把 Pod 删除。被驱逐的 Pod 如果由 Deployment 或 ReplicaSet 管理,控制器会自动创建新的副本,新副本再由默认调度器 kube-scheduler 根据当前最新的集群状态重新分配节点。整个过程是两个组件协作完成的。
这种设计决定了 Descheduler 的适用场景。典型的有:集群做过多次扩缩容后部分节点负载极不均衡;节点资源预留长期偏高或偏低;Pod 反亲和性规则后来才加上,导致存量 Pod 违反规则;节点新增了污点或标签,存量 Pod 没有跟随调整。反过来,如果你的集群规模很小、Pod 对中断极度敏感、或者缺少控制器兜底(比如裸 Pod),就不太适合使用重调度。
还需要注意驱逐带来的连锁反应。Pod 被删除会触发滚动重建,如果应用启动慢、没有配置就绪探针,大量驱逐可能造成服务抖动。所以生产环境使用时,务必配合 PodDisruptionBudget 和合理的驱逐速率限制,这一点在后面的配置部分会详细展开。
核心策略详解
Descheduler 的能力完全由策略驱动,下面介绍几个最常用的策略。每个策略都可以在配置文件中独立开关,并设置参数阈值。
第一个是 LowNodeUtilization,这是使用频率最高的策略。它把节点分为两类:低利用率节点和高利用率节点。当低利用率节点的 CPU、内存、Pod 数量都低于设定的阈值时,策略会把高利用率节点上的 Pod 驱逐到低利用率节点上。配置时需要同时设置 lowThresholds 和 highThresholds 两档阈值,只有低于低阈值的节点才会被视为“可接收 Pod”的目标节点。
第二个是 RemoveDuplicates,它保证同一个 ReplicaSet、Job 或 StatefulSet 的副本不会被调度到同一个节点上。这在节点临时故障后副本挤到同一台机器的场景下非常有用,能提升整体可用性。
第三个是 RemovePodsViolatingInterPodAntiAffinity,用于驱逐违反 Pod 间反亲和性规则的存量 Pod。很多人遇到过这种情况:给 Deployment 补充了反亲和性配置,但只有新创建的 Pod 受影响,存量 Pod 依旧挤在一起。这个策略就是解决这类历史遗留问题的。
第四个是 RemovePodsViolatingNodeTaints,当节点后来新增了污点,而存量 Pod 没有对应的容忍度时,这些 Pod 会被驱逐。还有 RemovePodsHavingTooManyRestarts,针对重启次数超过阈值的异常 Pod,把它们驱逐后重建往往能恢复到健康节点。
部署与配置实战
推荐以 CronJob 方式运行 Descheduler,这样可以控制扫描频率,避免常驻带来的持续干扰。下面是一份完整的部署清单,包含一份典型的策略配置,直接通过 kubectl apply 即可部署。
apiVersion: v1
kind: ConfigMap
metadata:
name: descheduler-policy-configmap
namespace: kube-system
data:
policy.yaml: |
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
LowNodeUtilization:
enabled: true
params:
nodeResourceUtilizationThresholds:
thresholds:
cpu: 20
memory: 20
pods: 10
targetThresholds:
cpu: 50
memory: 50
pods: 30
RemoveDuplicates:
enabled: true
RemovePodsViolatingInterPodAntiAffinity:
enabled: true
RemovePodsHavingTooManyRestarts:
enabled: true
params:
podsHavingTooManyRestarts:
podRestartThreshold: 10
includingInitContainers: true
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: descheduler
namespace: kube-system
spec:
schedule: "*/30 * * * *"
concurrencyPolicy: "Forbid"
jobTemplate:
spec:
template:
metadata:
name: descheduler-pod
spec:
priorityClassName: system-cluster-critical
serviceAccountName: descheduler-sa
containers:
- name: descheduler
image: registry.k8s.io/descheduler/descheduler:v0.30.0
command:
- /bin/descheduler
- --policy-config-file
- /policy-dir/policy.yaml
- --v=3
volumeMounts:
- mountPath: /policy-dir
name: policy-volume
restartPolicy: "Never"
volumes:
- name: policy-volume
configMap:
name: descheduler-policy-configmap
镜像版本建议与集群大版本保持接近,避免 API 兼容性问题。concurrencyPolicy 设置为 Forbid 很重要,防止上一轮扫描未结束时新一轮任务重复启动。--v=3 是日志级别,初次调试时可以调高到 4 或 5,观察每个策略命中的具体 Pod。
此外,Descheduler 需要相应的 RBAC 权限才能驱逐 Pod、读取节点信息,官方仓库提供了完整的 ClusterRole 和 ClusterRoleBinding 清单,部署时不要遗漏。如果没有权限,任务会以 RBAC 报错退出,日志中会出现 forbid 之类的提示。
如何避免重调度引发服务抖动
重调度的副作用是 Pod 中断,生产环境必须做好防护。第一层防护是 nodeSelector 或标签过滤,Descheduler 支持通过 nodeFit 参数过滤目标节点,只有存在可行放置位置的 Pod 才会被驱逐,避免驱逐后陷入 Pending 状态。
第二层是命名空间过滤。可以在策略参数里指定 including 或 excluding 命名空间列表,把有状态服务、批处理任务所在的命名空间排除在外,只对无状态的在线服务做重调度。示例如下:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
LowNodeUtilization:
enabled: true
params:
nodeResourceUtilizationThresholds:
thresholds:
cpu: 20
memory: 20
targetThresholds:
cpu: 60
memory: 60
namespaces:
include:
- "web"
- "api"
第三层是 Pod 级别的豁免。给关键业务 Pod 加上 descheduler.alpha.kubernetes.io/evict: "false" 注解,Descheduler 会跳过它们。配合应用的 PodDisruptionBudget,把最大不可用副本数限制为一,即使发生驱逐也不会同时打挂多个副本。
最后建议在低峰期执行 CronJob。把 schedule 从每半小时调整为每天凌晨执行,对大多数集群已经足够,频率越高收益反而越有限,因为调度本身也需要时间收敛。
常见问题排查
部署后最常见的问题是“策略没生效”。先检查 kubectl logs 中的策略加载数量,如果配置文件路径或格式有误,Descheduler 启动时就会报错退出。其次是阈值设置过严或过宽:低阈值设得太高,几乎没有节点被判定为低利用率,自然无 Pod 可迁移;此时可以先用 kubectl describe node 对比各节点实际分配率再调整。
另一个高频问题是驱逐后 Pod 仍调度回原节点。这通常是因为集群资源确实紧张,其他节点放不下该 Pod。此时优先解决容量问题,而不是反复调大驱逐力度。也可以开启 RemovePodsViolatingTopologySpreadConstraint 策略,配合拓扑分布约束,让调度器在重建时有明确的方向性。
总结一下,Descheduler 是补足原生调度器“一次性决策”短板的利器,但它本质是主动制造可控的中断来换取长期均衡。先在测试集群验证策略组合,再引入生产环境并配合 PDB 与命名空间过滤,就能在稳定性与资源利用率之间找到平衡点。
Kubernetes Descheduler重调度器Pod调度优化修改时间:2026-09-09 05:32:39