在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数
比如:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
nodeSelector: prod=dev
evictLocalStoragePods: true
maxNoOfPodsToEvictPerNode: 40
ignorePvcPods: false
strategies:
...
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则不会被驱逐,比如:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"RemoveDuplicates":
enabled: true
params:
removeDuplicates:
excludeOwnerKinds:
- "ReplicaSet"
LowNodeUtilization
该策略主要是找到那些未充分利用的节点,将驱逐的Pod在这些节点上创建,该策略配置在nodeResourceUtilizationThresholds
下。
节点利用率低是由配置阈值决定的,配置在thresholds
下,thresholds
可以配置cpu、memory以及pods数量(百分比),如果节点利用率低于配置,则代表该节点未被充分利用。目前,Pod的请求资源需求被考虑用于计算节点资源利用率。
另外一个参数targetThresholds
,用于计算可能驱逐Pods的潜在节点,该参数也是配置cpu、memory以及Pods数量的百分比。如果超过该配置,则表示该节点被过度利用,上面的Pods就可能被驱逐。而在thresholds
和targetThresholds
之间的任何节点则认为是正常利用。
参数有:
参数名 | 类型 |
---|---|
thresholds | map |
targetThresholds | map |
numberOfNodes | int |
thresholdPriority | int |
thresholdPriorityClassName | string |
比如:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"LowNodeUtilization":
enabled: true
params:
nodeResourceUtilizationThresholds:
thresholds:
"cpu" : 20
"memory": 20
"pods": 20
targetThresholds:
"cpu" : 50
"memory": 50
"pods": 50
需要注意的是:
- 仅支持以下三种资源类型
- cpu
- memory
- pods
- thresholds和targetThresholds必须配置相同的类型
- 参数值的访问是0-100(百分制)
- 相同的资源类型,thresholds的配置不能高于targetThresholds的配置
如果未指定任何资源类型,则默认是100%,已避免节点从未充分利用变为过度利用。
与LowNodeUtilization策略关联的另一个参数称为numberOfNodes。 仅当未充分利用的节点数大于配置的值时,才可以配置此参数以激活策略。 这在大型群集中很有用,其中一些节点可能会频繁使用或短期使用不足。 默认情况下,numberOfNodes设置为零。
RemovePodsViolatingInterPodAntiAffinity
该策略可确保从节点中删除违反Interpod反亲和关系的pod。例如,如果某个节点上有podA
,并且podB
和podC
(在同一节点上运行)具有禁止它们在同一节点上运行的反亲和规则,则podA
将被从该节点逐出,以便podB
和podC
正常运行。当 podB
和 podC
已经运行在节点上后,反亲和性规则被创建就会发送这样的问题。
参数为:
参数名 | 类型 |
---|---|
thresholdPriority | int |
thresholdPriorityClassName | string |
namespaces | list(string) |
如下开启该策略:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"RemovePodsViolatingInterPodAntiAffinity":
enabled: true
RemovePodsViolatingNodeAffinity
启用后,该策略requiredDuringSchedulingRequiredDuringExecution
将用作kubelet 的临时实现并逐出该kubelet,不再考虑节点亲和力。
例如,在nodeA上调度了podA,该podA满足了调度时的节点亲缘性规则requiredDuringSchedulingIgnoredDuringExecution
。随着时间的流逝,nodeA停止满足该规则。当执行该策略并且有另一个可用的节点满足该节点相似性规则时,podA被从nodeA中逐出。
参数有:
参数名 | 类型 |
---|---|
thresholdPriority | int |
thresholdPriorityClassName | string |
namspaces | list(string) |
nodeAffinityType | list(string) |
例如:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"RemovePodsViolatingNodeAffinity":
enabled: true
params:
nodeAffinityType:
- "requiredDuringSchedulingIgnoredDuringExecution"
RemovePodsViolatingNodeTaints
该策略可以确保从节点中删除违反 NoSchedule
污点的 Pod
。例如,有一个名为 podA
的 Pod
,通过配置容忍 key=value:NoSchedule
允许被调度到有该污点配置的节点上,如果节点的污点随后被更新或者删除了,则污点将不再被 Pod
的容忍满足,然后将被驱逐。
参数:
参数名 | 类型 |
---|---|
thresholdPriority | int |
thresholdPriorityClassName | string |
namespaces | list(string) |
例如:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"RemovePodsViolatingNodeTaints":
enabled: true
RemovePodsViolatingTopologySpreadConstraint
该策略确保从节点驱逐违反拓扑扩展约束的Pods,具体来说,它试图驱逐将拓扑域平衡到每个约束的maxSkew内所需的最小pod数,不过次策略需要k8s版本高于1.18才能使用。
默认情况下,此策略仅处理硬约束,如果将参数includeSoftConstraints
设置为True
,也将支持软约束。
参数为:
参数名 | 类型 |
---|---|
thresholdPriority | int |
thresholdPriorityClassName | string |
namespaces | list(string) |
includeSoftConstraints | bool |
例如:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"RemovePodsViolatingTopologySpreadConstraint":
enabled: true
params:
includeSoftConstraints: false
RemovePodsHavingTooManyRestarts
该策略确保从节点中删除重启次数过多的Pods,例如,具有EBS / PD的Pod无法将卷/磁盘附加到实例,则应该将该Pod重新安排到其他节点。 它的参数包括podRestartThreshold
(这是应将Pod逐出的重新启动次数),以及包括InitContainers
,它确定在计算中是否应考虑初始化容器的重新启动。
参数为:
参数名 | 类型 |
---|---|
podRestartThreshold |
int |
includingInitContainers |
bool |
thresholdPriority |
int |
thresholdPriorityClassName |
string |
namespaces |
list(string) |
例如:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"RemovePodsHavingTooManyRestarts":
enabled: true
params:
podsHavingTooManyRestarts:
podRestartThreshold: 100
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) |
例如:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"PodLifeTime":
enabled: true
params:
podLifeTime:
maxPodLifeTimeSeconds: 86400
podStatusPhases:
- "Pending"
Filter Pods
在驱逐Pods的时候,有时并不需要所有Pods都被驱逐,Descheduler提供一些过滤方式。
Namespace filtering
该策略可以配置是包含还是排除某些名称空间。可以使用该策略的有:
PodLifeTime
RemovePodsHavingTooManyRestarts
RemovePodsViolatingNodeTaints
RemovePodsViolatingNodeAffinity
RemovePodsViolatingInterPodAntiAffinity
RemoveDuplicates
RemovePodsViolatingTopologySpreadConstraint
(1)只驱逐某些命令空间下的Pods,使用include
参数,如下:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"PodLifeTime":
enabled: true
params:
podLifeTime:
maxPodLifeTimeSeconds: 86400
namespaces:
include:
- "namespace1"
- "namespace2"
(2)排除掉某些命令空间下的Pods,使用exclude
参数,如下:
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"PodLifeTime":
enabled: true
params:
podLifeTime:
maxPodLifeTimeSeconds: 86400
namespaces:
exclude:
- "namespace1"
- "namespace2"
Priority filtering
所有策略都可以配置优先级阈值,只有在该阈值以下的Pod才能被驱逐。 您可以通过设置thresholdPriorityClassName
(将阈值设置为给定优先级类别的值)或thresholdPriority
(直接设置阈值)参数来指定此阈值。 默认情况下,此阈值设置为系统集群关键优先级类别的值。
例如:
(1)使用thresholdPriority
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"PodLifeTime":
enabled: true
params:
podLifeTime:
maxPodLifeTimeSeconds: 86400
thresholdPriority: 10000
(2)使用thresholdPriorityClassName
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"PodLifeTime":
enabled: true
params:
podLifeTime:
maxPodLifeTimeSeconds: 86400
thresholdPriorityClassName: "priorityclass1"
注意:不能同时配置thresholdPriority和thresholdPriorityClassName,如果给定的优先级类不存在,则descheduler不会创建它,并且会引发错误。
Pod Evictions
当使用descheduler驱除Pods的时候,需要注意以下几点:
- 关键性Pod不会为驱逐,比如
priorityClassName
设置为system-cluster-critical
或system-node-critical
的Pod - 不属于RC、RS、Deployment或Job管理的Pod不会被驱逐
- DS创建的Pods不会被驱逐
- 使用LocalStorage的Pod不会被驱逐,设置
evictLocalStoragePods: true
除外 - 除非设置
ignorePvcPods: true
,否正具有PVC的Pods会被驱逐 - 在
LowNodeUtilization
和RemovePodsViolatingInterPodAntiAffinity
策略下,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
$ wget https://github.com/kubernetes-sigs/descheduler/archive/v0.18.0.tar.gz
(2)创建RBAC
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: descheduler-cluster-role
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "watch", "list"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list", "delete"]
- apiGroups: [""]
resources: ["pods/eviction"]
verbs: ["create"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: descheduler-sa
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: descheduler-cluster-role-binding
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: descheduler-cluster-role
subjects:
- name: descheduler-sa
kind: ServiceAccount
namespace: kube-system
(3)创建ConfigMap,该配置文件主要配置驱逐策略,如下:
---
apiVersion: v1
kind: ConfigMap
metadata:
name: descheduler-policy-configmap
namespace: kube-system
data:
policy.yaml: |
apiVersion: "descheduler/v1alpha1"
kind: "DeschedulerPolicy"
strategies:
"RemoveDuplicates":
enabled: true
"RemovePodsViolatingInterPodAntiAffinity":
enabled: true
"LowNodeUtilization":
enabled: true
params:
nodeResourceUtilizationThresholds:
thresholds:
"cpu" : 20
"memory": 20
"pods": 20
targetThresholds:
"cpu" : 50
"memory": 50
"pods": 50
(4)使用Job来进行驱逐,配置文件如下:
---
apiVersion: batch/v1
kind: Job
metadata:
name: descheduler-job
namespace: kube-system
spec:
parallelism: 1
completions: 1
template:
metadata:
name: descheduler-pod
spec:
priorityClassName: system-cluster-critical
containers:
- name: descheduler
image: us.gcr.io/k8s-artifacts-prod/descheduler/descheduler:v0.10.0
volumeMounts:
- mountPath: /policy-dir
name: policy-volume
command:
- "/bin/descheduler"
args:
- "--policy-config-file"
- "/policy-dir/policy.yaml"
- "--v"
- "3"
restartPolicy: "Never"
serviceAccountName: descheduler-sa
volumes:
- name: policy-volume
configMap:
name: descheduler-policy-configmap
(5)如果需要配置定时任务进行驱逐,则使用CronJob,如下:
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: descheduler-cronjob
namespace: kube-system
spec:
schedule: "*/2 * * * *"
concurrencyPolicy: "Forbid"
jobTemplate:
spec:
template:
metadata:
name: descheduler-pod
spec:
priorityClassName: system-cluster-critical
containers:
- name: descheduler
image: us.gcr.io/k8s-artifacts-prod/descheduler/descheduler:v0.10.0
volumeMounts:
- mountPath: /policy-dir
name: policy-volume
command:
- "/bin/descheduler"
args:
- "--policy-config-file"
- "/policy-dir/policy.yaml"
- "--v"
- "3"
restartPolicy: "Never"
serviceAccountName: descheduler-sa
volumes:
- name: policy-volume
configMap:
name: descheduler-policy-configmap