在Kubernetes中,kube-scheduler负责将Pod调度到合适的Node上,但是Kubernetes是一个非常动态的,高度弹性的环境,有时候会造成某一个或多个节点pod数分配不均,比如:

  • 一些节点利用率低下或过度使用
  • 添加删除标签或添加删除污点,pod或Node亲和性改变等造成原调度不再满足
  • 一些节点故障,其上运行的Pod调度到其他节点
  • 新节点加入集群

由于以上种种原因,可能导致多个Pod运行到不太理想的节点,而整个K8S集群也会处于一段时间不均衡的状态,这时候就需要重新平衡集群。Descheduler就是这样一个项目。

Descheduler

Descheduler可以根据一些规则配置来重新平衡集群状态,目前支持的策略有:

  • RemoveDuplicates
  • LowNodeUtilization
  • RemovePodsViolatingInterPodAntiAffinity
  • RemovePodsViolatingNodeAffinity
  • RemovePodsViolatingNodeTaints
  • RemovePodsViolatingTopologySpreadConstraint
  • RemovePodsHavingTooManyRestarts
  • PodLifeTime

这些策略可以启用,也可以关闭,默认情况下,所有策略都是启动的。

另外,还有一些通用配置,如下:

  • nodeSelector:限制要处理的节点
  • evictLocalStoragePods: 驱除使用LocalStorage的Pods
  • ignorePvcPods: 是否忽略配置PVC的Pods,默认是False
  • maxNoOfPodsToEvictPerNode:节点允许的最大驱逐Pods数

比如:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. nodeSelector: prod=dev
  4. evictLocalStoragePods: true
  5. maxNoOfPodsToEvictPerNode: 40
  6. ignorePvcPods: false
  7. strategies:
  8. ...

RemoveDuplicates

该策略确保只有一个Pod与在同一节点上运行的副本集(RS),Replication Controller(RC),Deployment或Job相关联。 如果有更多的Pod,则将这些重复的Pod逐出,以更好地在群集中扩展。 如果某些节点由于任何原因而崩溃,并且它们上的Pod移至其他节点,导致多个与RS或RC关联的Pod(例如,在同一节点上运行),则可能发生此问题。 一旦发生故障的节点再次准备就绪,便可以启用此策略以驱逐这些重复的Pod。

参数列表有:

参数名 类型
excludeOwnerKinds list(string)
namespaces list(string)
thresholdPriority int
thresholdPriorityClassName string

其中excludeOwnerKinds用于排除类型,这些类型下的Pod则不会被驱逐,比如:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "RemoveDuplicates":
  5. enabled: true
  6. params:
  7. removeDuplicates:
  8. excludeOwnerKinds:
  9. - "ReplicaSet"

LowNodeUtilization

该策略主要是找到那些未充分利用的节点,将驱逐的Pod在这些节点上创建,该策略配置在nodeResourceUtilizationThresholds下。

节点利用率低是由配置阈值决定的,配置在thresholds下,thresholds可以配置cpu、memory以及pods数量(百分比),如果节点利用率低于配置,则代表该节点未被充分利用。目前,Pod的请求资源需求被考虑用于计算节点资源利用率。

另外一个参数targetThresholds,用于计算可能驱逐Pods的潜在节点,该参数也是配置cpu、memory以及Pods数量的百分比。如果超过该配置,则表示该节点被过度利用,上面的Pods就可能被驱逐。而在thresholdstargetThresholds之间的任何节点则认为是正常利用。

参数有:

参数名 类型
thresholds map
targetThresholds map
numberOfNodes int
thresholdPriority int
thresholdPriorityClassName string

比如:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "LowNodeUtilization":
  5. enabled: true
  6. params:
  7. nodeResourceUtilizationThresholds:
  8. thresholds:
  9. "cpu" : 20
  10. "memory": 20
  11. "pods": 20
  12. targetThresholds:
  13. "cpu" : 50
  14. "memory": 50
  15. "pods": 50

需要注意的是:

  • 仅支持以下三种资源类型
    • cpu
    • memory
    • pods
  • thresholds和targetThresholds必须配置相同的类型
  • 参数值的访问是0-100(百分制)
  • 相同的资源类型,thresholds的配置不能高于targetThresholds的配置

如果未指定任何资源类型,则默认是100%,已避免节点从未充分利用变为过度利用。

与LowNodeUtilization策略关联的另一个参数称为numberOfNodes。 仅当未充分利用的节点数大于配置的值时,才可以配置此参数以激活策略。 这在大型群集中很有用,其中一些节点可能会频繁使用或短期使用不足。 默认情况下,numberOfNodes设置为零。

RemovePodsViolatingInterPodAntiAffinity

该策略可确保从节点中删除违反Interpod反亲和关系的pod。例如,如果某个节点上有podA,并且podBpodC(在同一节点上运行)具有禁止它们在同一节点上运行的反亲和规则,则podA将被从该节点逐出,以便podBpodC正常运行。当 podBpodC 已经运行在节点上后,反亲和性规则被创建就会发送这样的问题。

参数为:

参数名 类型
thresholdPriority int
thresholdPriorityClassName string
namespaces list(string)

如下开启该策略:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "RemovePodsViolatingInterPodAntiAffinity":
  5. enabled: true

RemovePodsViolatingNodeAffinity

启用后,该策略requiredDuringSchedulingRequiredDuringExecution将用作kubelet 的临时实现并逐出该kubelet,不再考虑节点亲和力。

例如,在nodeA上调度了podA,该podA满足了调度时的节点亲缘性规则requiredDuringSchedulingIgnoredDuringExecution。随着时间的流逝,nodeA停止满足该规则。当执行该策略并且有另一个可用的节点满足该节点相似性规则时,podA被从nodeA中逐出。

参数有:

参数名 类型
thresholdPriority int
thresholdPriorityClassName string
namspaces list(string)
nodeAffinityType list(string)

例如:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "RemovePodsViolatingNodeAffinity":
  5. enabled: true
  6. params:
  7. nodeAffinityType:
  8. - "requiredDuringSchedulingIgnoredDuringExecution"

RemovePodsViolatingNodeTaints

该策略可以确保从节点中删除违反 NoSchedule 污点的 Pod。例如,有一个名为 podAPod,通过配置容忍 key=value:NoSchedule 允许被调度到有该污点配置的节点上,如果节点的污点随后被更新或者删除了,则污点将不再被 Pod 的容忍满足,然后将被驱逐。

参数:

参数名 类型
thresholdPriority int
thresholdPriorityClassName string
namespaces list(string)

例如:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "RemovePodsViolatingNodeTaints":
  5. enabled: true

RemovePodsViolatingTopologySpreadConstraint

该策略确保从节点驱逐违反拓扑扩展约束的Pods,具体来说,它试图驱逐将拓扑域平衡到每个约束的maxSkew内所需的最小pod数,不过次策略需要k8s版本高于1.18才能使用。

默认情况下,此策略仅处理硬约束,如果将参数includeSoftConstraints 设置为True,也将支持软约束。

参数为:

参数名 类型
thresholdPriority int
thresholdPriorityClassName string
namespaces list(string)
includeSoftConstraints bool

例如:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "RemovePodsViolatingTopologySpreadConstraint":
  5. enabled: true
  6. params:
  7. includeSoftConstraints: false

RemovePodsHavingTooManyRestarts

该策略确保从节点中删除重启次数过多的Pods,例如,具有EBS / PD的Pod无法将卷/磁盘附加到实例,则应该将该Pod重新安排到其他节点。 它的参数包括podRestartThreshold(这是应将Pod逐出的重新启动次数),以及包括InitContainers,它确定在计算中是否应考虑初始化容器的重新启动。

参数为:

参数名 类型
podRestartThreshold int
includingInitContainers bool
thresholdPriority int
thresholdPriorityClassName string
namespaces list(string)

例如:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "RemovePodsHavingTooManyRestarts":
  5. enabled: true
  6. params:
  7. podsHavingTooManyRestarts:
  8. podRestartThreshold: 100
  9. includingInitContainers: true

PodLifeTime

该策略用于驱逐比maxPodLifeTimeSeconds更旧的Pods,可以通过podStatusPhases来配置哪类状态的Pods会被驱逐。

参数有:

参数名 类型
maxPodLifeTimeSeconds int
podStatusPhases list(string)
thresholdPriority int (see priority filtering)
thresholdPriorityClassName string (see priority filtering)
namespaces (see namespace filtering)

例如:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "PodLifeTime":
  5. enabled: true
  6. params:
  7. podLifeTime:
  8. maxPodLifeTimeSeconds: 86400
  9. podStatusPhases:
  10. - "Pending"

Filter Pods

在驱逐Pods的时候,有时并不需要所有Pods都被驱逐,Descheduler提供一些过滤方式。

Namespace filtering

该策略可以配置是包含还是排除某些名称空间。可以使用该策略的有:

  • PodLifeTime
  • RemovePodsHavingTooManyRestarts
  • RemovePodsViolatingNodeTaints
  • RemovePodsViolatingNodeAffinity
  • RemovePodsViolatingInterPodAntiAffinity
  • RemoveDuplicates
  • RemovePodsViolatingTopologySpreadConstraint

(1)只驱逐某些命令空间下的Pods,使用include参数,如下:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "PodLifeTime":
  5. enabled: true
  6. params:
  7. podLifeTime:
  8. maxPodLifeTimeSeconds: 86400
  9. namespaces:
  10. include:
  11. - "namespace1"
  12. - "namespace2"

(2)排除掉某些命令空间下的Pods,使用exclude参数,如下:

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "PodLifeTime":
  5. enabled: true
  6. params:
  7. podLifeTime:
  8. maxPodLifeTimeSeconds: 86400
  9. namespaces:
  10. exclude:
  11. - "namespace1"
  12. - "namespace2"

Priority filtering

所有策略都可以配置优先级阈值,只有在该阈值以下的Pod才能被驱逐。 您可以通过设置thresholdPriorityClassName(将阈值设置为给定优先级类别的值)或thresholdPriority(直接设置阈值)参数来指定此阈值。 默认情况下,此阈值设置为系统集群关键优先级类别的值。

例如:
(1)使用thresholdPriority

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "PodLifeTime":
  5. enabled: true
  6. params:
  7. podLifeTime:
  8. maxPodLifeTimeSeconds: 86400
  9. thresholdPriority: 10000

(2)使用thresholdPriorityClassName

  1. apiVersion: "descheduler/v1alpha1"
  2. kind: "DeschedulerPolicy"
  3. strategies:
  4. "PodLifeTime":
  5. enabled: true
  6. params:
  7. podLifeTime:
  8. maxPodLifeTimeSeconds: 86400
  9. thresholdPriorityClassName: "priorityclass1"

注意:不能同时配置thresholdPriority和thresholdPriorityClassName,如果给定的优先级类不存在,则descheduler不会创建它,并且会引发错误。

Pod Evictions

当使用descheduler驱除Pods的时候,需要注意以下几点:

  • 关键性Pod不会为驱逐,比如priorityClassName设置为system-cluster-criticalsystem-node-critical的Pod
  • 不属于RC、RS、Deployment或Job管理的Pod不会被驱逐
  • DS创建的Pods不会被驱逐
  • 使用LocalStorage的Pod不会被驱逐,设置evictLocalStoragePods: true除外
  • 除非设置ignorePvcPods: true,否正具有PVC的Pods会被驱逐
  • LowNodeUtilizationRemovePodsViolatingInterPodAntiAffinity策略下,Pods按优先级从低到高进行驱逐,如果优先级相同,Besteffort类型的Pod要先于Burstable和Guaranteed类型被驱逐
  • annotations中带有descheduler.alpha.kubernetes.io/evict字段的Pod都可以被驱逐,该注释用于覆盖阻止驱逐的检查,用户可以选择驱逐哪个Pods

如果Pods驱逐失败,可以设置--v=4或者从Descheduler日志中查找原因。

如果驱逐违反PDB约束,则不会驱逐这类Pods。

版本兼容

Descheduler Supported Kubernetes Version
v0.20 v1.20
v0.19 v1.19
v0.18 v1.18
v0.10 v1.17
v0.4-v0.9 v1.9+
v0.1-v0.3 v1.7-v1.8

实践

K8S集群版本:v1.18.9

(1)下载对应版本的Descheduler

  1. $ wget https://github.com/kubernetes-sigs/descheduler/archive/v0.18.0.tar.gz

(2)创建RBAC

  1. ---
  2. kind: ClusterRole
  3. apiVersion: rbac.authorization.k8s.io/v1
  4. metadata:
  5. name: descheduler-cluster-role
  6. namespace: kube-system
  7. rules:
  8. - apiGroups: [""]
  9. resources: ["events"]
  10. verbs: ["create", "update"]
  11. - apiGroups: [""]
  12. resources: ["nodes"]
  13. verbs: ["get", "watch", "list"]
  14. - apiGroups: [""]
  15. resources: ["pods"]
  16. verbs: ["get", "watch", "list", "delete"]
  17. - apiGroups: [""]
  18. resources: ["pods/eviction"]
  19. verbs: ["create"]
  20. ---
  21. apiVersion: v1
  22. kind: ServiceAccount
  23. metadata:
  24. name: descheduler-sa
  25. namespace: kube-system
  26. ---
  27. apiVersion: rbac.authorization.k8s.io/v1
  28. kind: ClusterRoleBinding
  29. metadata:
  30. name: descheduler-cluster-role-binding
  31. namespace: kube-system
  32. roleRef:
  33. apiGroup: rbac.authorization.k8s.io
  34. kind: ClusterRole
  35. name: descheduler-cluster-role
  36. subjects:
  37. - name: descheduler-sa
  38. kind: ServiceAccount
  39. namespace: kube-system

(3)创建ConfigMap,该配置文件主要配置驱逐策略,如下:

  1. ---
  2. apiVersion: v1
  3. kind: ConfigMap
  4. metadata:
  5. name: descheduler-policy-configmap
  6. namespace: kube-system
  7. data:
  8. policy.yaml: |
  9. apiVersion: "descheduler/v1alpha1"
  10. kind: "DeschedulerPolicy"
  11. strategies:
  12. "RemoveDuplicates":
  13. enabled: true
  14. "RemovePodsViolatingInterPodAntiAffinity":
  15. enabled: true
  16. "LowNodeUtilization":
  17. enabled: true
  18. params:
  19. nodeResourceUtilizationThresholds:
  20. thresholds:
  21. "cpu" : 20
  22. "memory": 20
  23. "pods": 20
  24. targetThresholds:
  25. "cpu" : 50
  26. "memory": 50
  27. "pods": 50

(4)使用Job来进行驱逐,配置文件如下:

  1. ---
  2. apiVersion: batch/v1
  3. kind: Job
  4. metadata:
  5. name: descheduler-job
  6. namespace: kube-system
  7. spec:
  8. parallelism: 1
  9. completions: 1
  10. template:
  11. metadata:
  12. name: descheduler-pod
  13. spec:
  14. priorityClassName: system-cluster-critical
  15. containers:
  16. - name: descheduler
  17. image: us.gcr.io/k8s-artifacts-prod/descheduler/descheduler:v0.10.0
  18. volumeMounts:
  19. - mountPath: /policy-dir
  20. name: policy-volume
  21. command:
  22. - "/bin/descheduler"
  23. args:
  24. - "--policy-config-file"
  25. - "/policy-dir/policy.yaml"
  26. - "--v"
  27. - "3"
  28. restartPolicy: "Never"
  29. serviceAccountName: descheduler-sa
  30. volumes:
  31. - name: policy-volume
  32. configMap:
  33. name: descheduler-policy-configmap

(5)如果需要配置定时任务进行驱逐,则使用CronJob,如下:

  1. ---
  2. apiVersion: batch/v1beta1
  3. kind: CronJob
  4. metadata:
  5. name: descheduler-cronjob
  6. namespace: kube-system
  7. spec:
  8. schedule: "*/2 * * * *"
  9. concurrencyPolicy: "Forbid"
  10. jobTemplate:
  11. spec:
  12. template:
  13. metadata:
  14. name: descheduler-pod
  15. spec:
  16. priorityClassName: system-cluster-critical
  17. containers:
  18. - name: descheduler
  19. image: us.gcr.io/k8s-artifacts-prod/descheduler/descheduler:v0.10.0
  20. volumeMounts:
  21. - mountPath: /policy-dir
  22. name: policy-volume
  23. command:
  24. - "/bin/descheduler"
  25. args:
  26. - "--policy-config-file"
  27. - "/policy-dir/policy.yaml"
  28. - "--v"
  29. - "3"
  30. restartPolicy: "Never"
  31. serviceAccountName: descheduler-sa
  32. volumes:
  33. - name: policy-volume
  34. configMap:
  35. name: descheduler-policy-configmap

原文:https://github.com/kubernetes-sigs/descheduler