kubernetes – 利用descheduler均衡POD分布
kube-scheduler调度POD是一次性决策的,一旦POD选定node运行起来,除非人工干预否则POD将永远运行在该node。
那么一个典型的问题就来了,当我们向集群新加入一个node时,POD并不会自动均衡到这个node,除非我们主动滚动发布以便让kube-scheduler做出新的决策。
类似的问题还有,我们通常希望同一个deployment的POD可以分布到不同的node上,然而集群中的资源碎片很有可能导致2个POD分布到一个node上,随着集群资源的自然变动,我们希望能有合适的机会重新进行打散。
那么,上述实际问题就需要引入一个新的组件叫做descheduler,由它来辅助kube-scheduler,对POD进行定期的二次打散整理,确保集群尽量调度均衡。
使用方法
descheduler配置与使用非常简单,也是K8S官方的准预备项目,理论上是可靠的。
项目地址:https://github.com/kubernetes-sigs/descheduler,把项目下载下来即可。
创建rbac
因为descheduler需要获取集群的pod/node等资源信息,以便做出决策,所以第一步就是配置rbac相关的东西,简单说就是创建serviceaccount与role,然后做一下两者的binding。
执行如下命令直接生效:
1 |
kubectl apply -f kubernetes/rbac.yaml |
会得到一个kube-system命名空间下面的descheduler-sa账号,已经做好了资源授权。
创建configmap
descheduler的配置文件,通过configmap配置并挂载。
打开kubernetes/configmap.yaml,看一下descheduler支持的几个均衡策略:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
--- 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 |
它最多支持5种策略共同工作,每一个策略有独立的开关选项。
该配置文件只写了3个策略,也是比较好用的几个策略:
- RemoveDuplicates:如果deployment的多个pod在同1个node上,那么就试图打散。
- RemovePodsViolatingInterPodAntiAffinity:pod曾经与node亲和,而现在不再亲和了,那么对该pod尝试做迁移。
- LowNodeUtilization:如果node上的cpu/memory/pods的request超过targetThresholds阈值,则认为该node负载太高,会尝试将其上的pod进行迁移,迁移的目标node需要低于thresholds阈值,这些node被认为是负载太低。
针对LowNodeUtilization策略,根据github中的说明:
1)thresholds的判断是必须cpu+mem+pod三个条件(都是百分比)同时满足才算作”低负载node”,其cpu/memory依据是request值。
2)targetThresholds判断依据是cpu or mem or pod任意一个超过阈值才算作”高负载node”。
pod会从”高负载node” 迁移至 “低负载node”,不满足2个条件的node不会做均衡处理。
有需要则调整,然后直接提交即可:
1 |
kubectl apply -f kubernetes/configmap.yaml |
运行
理论上来说,descheduler还是凌晨业务低峰期执行比较靠谱,因此descheduler也被设计为一种一次性的JOB。
为了自动执行,我们使用cronjob来定时运行它。
打开kubernetes/cronjob.yaml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
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.18.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 |
可见,它挂载了configmap作为配置文件,配置了descheduler-sa的serviceaccount,并且禁止了并发执行。
默认是2分钟调度一次,我们根据自己修改cron表达式即可,比如改成每天凌晨执行。
镜像已经被墙,大家可以使用海外服务器下载镜像然后推送到dockerhub作替代,或者直接使用我备份的版本:owenliang1990/descheduler:v0.18.0。
直接提交它即可:
1 |
kubectl apply -f kubernetes/cronjob.yaml |
日志
cronjob会定时拉起一个Pod,我们可以修改Descheduler启动参数的日志级别调整–v 5,通过kubectl logs查看pod具体做了哪些决策。
调试
为了降低使用descheduler的风险(比如大面积的踢出POD),测试时使用启动参数–dry-run,令其仅做决策不做真正的POD驱逐,根据日志验证在不同集群上的具体表现,辅助我们调整驱逐的阈值参数。
我认为正常情况下,仅仅基于CPU指标来做均衡即可,不需要考虑Mem/POD个数,否则低负载节点的多个thresholds条件比较难以同时成立。
实测下来,descheduler会扫描每个node,先判断node的cpu/mem/pods是否同时小于thresholds,如果成立则认为该node是低负载的;如果不成立则进一步判断cpu/mem/pods是否任意指标大于targetThresholds,如果成立则认为是高负载的;其他情况node什么也不是,不参与调度。
后续如果可以上到生产环境,我再写另外一篇文章来介绍一下经验。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~
