负载感知调度

负载感知调度(Load Aware Scheduling) 是 koord-scheduler 提供的一种调度能力,调度 Pod 时根据节点的负载情况选择合适的节点,均衡节点间的负载情况。

简介

负载均衡是资源调度中的常见问题。资源未充分利用的节点会带来很大的资源浪费,而过度使用的节点可能会导致性能下降。这些问题都不能高效的管理和使用资源。 原生 Kubernetes Scheduler 根据 Requests 和节点可分配总量来调度 Pod,既不考虑实时负载,也不估计使用量。 当我们期望使用原生调度器均匀的打散 Pod 并保持节点间的负载均衡,我们需要为应用程序设置精确的资源规格。此外,当 Koordinator 通过超卖机制提升资源使用效率时,我们需要一种机制尽量避免性能回退,并避免负载过高的问题。

koord-scheduler 参考 koordlet 上报的资源利用率数据平衡在线 Pod(LSE/LSR/LS)和离线 Pod(BE)的调度。

图片

想要了解更多信息,请参阅 设计:负载感知调度

设置

前提条件

  • Kubernetes >= 1.18
  • Koordinator >= 0.4

安装

请确保 Koordinator 组件已正确安装在你的集群中。 如果没有,请参考安装文档

配置全局策略

负载感知调度是默认启用的,不需要修改调度器的配置即可使用。

对于需要深入定制的用户,可以通过修改 Helm Chart 中的 ConfigMap koord-scheduler-config 规则来配置负载感知调度。

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: koord-scheduler-config
  5. ...
  6. data:
  7. koord-scheduler-config: |
  8. apiVersion: kubescheduler.config.k8s.io/v1beta2
  9. kind: KubeSchedulerConfiguration
  10. profiles:
  11. - schedulerName: koord-scheduler
  12. plugins:
  13. # enable the LoadAwareScheduling plugin
  14. filter:
  15. enabled:
  16. - name: LoadAwareScheduling
  17. ...
  18. score:
  19. enabled:
  20. - name: LoadAwareScheduling
  21. weight: 1
  22. ...
  23. reserve:
  24. enabled:
  25. - name: LoadAwareScheduling
  26. ...
  27. pluginConfig:
  28. # configure the thresholds and weights for the plugin
  29. - name: LoadAwareScheduling
  30. args:
  31. apiVersion: kubescheduler.config.k8s.io/v1beta2
  32. kind: LoadAwareSchedulingArgs
  33. # whether to filter nodes where koordlet fails to update NodeMetric
  34. filterExpiredNodeMetrics: true
  35. # the expiration threshold seconds when using NodeMetric
  36. nodeMetricExpirationSeconds: 300
  37. # weights of resources
  38. resourceWeights:
  39. cpu: 1
  40. memory: 1
  41. # thresholds (%) of resource utilization
  42. usageThresholds:
  43. cpu: 75
  44. memory: 85
  45. # thresholds (%) of resource utilization of Prod Pods
  46. prodUsageThresholds:
  47. cpu: 55
  48. memory: 65
  49. # enable score according Prod usage
  50. scoreAccordingProdUsage: true
  51. # the factor (%) for estimating resource usage
  52. estimatedScalingFactors:
  53. cpu: 80
  54. memory: 70
  55. # enable resource utilization filtering and scoring based on percentile statistics
  56. aggregated:
  57. usageThresholds:
  58. cpu: 65
  59. memory: 75
  60. usageAggregationType: "p99"
  61. scoreAggregationType: "p99"

koord-descheduler 是通过 Configmap 加载调度器配置的。因此需要通过重启调度器才能使用最新的配置。

字段说明版本
filterExpiredNodeMetricsfilterExpiredNodeMetrics 表示是否过滤koordlet更新NodeMetric失败的节点。 默认情况下启用,但在 Helm chart 中,它被禁用。>= v0.4.0
nodeMetricExpirationSecondsnodeMetricExpirationSeconds 指示 NodeMetric 过期时间(以秒为单位)。 当 NodeMetrics 过期时,节点被认为是异常的。 默认为 180 秒。>= v0.4.0
resourceWeightsresourceWeights 表示资源的权重。 CPU 和 Memory 的权重默认都是 1。>= v0.4.0
usageThresholdsusageThresholds 表示整机的资源利用率阈值。 CPU 的默认值为 65%,内存的默认值为 95%。>= v0.4.0
estimatedScalingFactorsestimatedScalingFactors 表示估计资源使用时的因子。 CPU 默认值为 85%,Memory 默认值为 70%。>= v0.4.0
prodUsageThresholdsprodUsageThresholds 表示 Prod Pod 相对于整机的资源利用率阈值。 默认情况下不启用。>= v1.1.0
scoreAccordingProdUsagescoreAccordingProdUsage 控制是否根据 Prod Pod 的利用率进行评分。>= v1.1.0
aggregatedaggregated 支持基于百分位数统计的资源利用率过滤和评分。>= v1.1.0

Aggregated 支持的字段:

字段说明版本
usageThresholdsusageThresholds 表示机器基于百分位统计的资源利用率阈值。>= v1.1.0
usageAggregationTypeusageAggregationType 表示过滤时机器利用率的百分位类型。 目前支持 avgp50p90p95p99>= v1.1.0
usageAggregatedDurationusageAggregatedDuration 表示过滤时机器利用率百分位数的统计周期。不设置该字段时,调度器默认使用 NodeMetrics 中最大周期的数据。>= v1.1.0
scoreAggregationTypescoreAggregationType 表示评分时机器利用率的百分位类型。 目前支持 avgp50p90p95p99>= v1.1.0
scoreAggregatedDurationscoreAggregatedDuration 表示打分时 Prod Pod 利用率百分位的统计周期。 不设置该字段时,调度器默认使用 NodeMetrics 中最大周期的数据。>= v1.1.0

按照节点配置过滤阈值

通过插件的配置可以作为集群默认的全局配置,用户也可以通过在节点上附加 annotation 来设置节点维度的负载阈值。 当节点上存在 annotation 时,会根据注解指定的参数进行过滤。

Annotation 定义如下:

  1. const (
  2. AnnotationCustomUsageThresholds = "scheduling.koordinator.sh/usage-thresholds"
  3. )
  4. // CustomUsageThresholds supports user-defined node resource utilization thresholds.
  5. type CustomUsageThresholds struct {
  6. // UsageThresholds indicates the resource utilization threshold of the whole machine.
  7. UsageThresholds map[corev1.ResourceName]int64 `json:"usageThresholds,omitempty"`
  8. // ProdUsageThresholds indicates the resource utilization threshold of Prod Pods compared to the whole machine
  9. ProdUsageThresholds map[corev1.ResourceName]int64 `json:"prodUsageThresholds,omitempty"`
  10. // AggregatedUsage supports resource utilization filtering and scoring based on percentile statistics
  11. AggregatedUsage *CustomAggregatedUsage `json:"aggregatedUsage,omitempty"`
  12. }
  13. type CustomAggregatedUsage struct {
  14. // UsageThresholds indicates the resource utilization threshold of the machine based on percentile statistics
  15. UsageThresholds map[corev1.ResourceName]int64 `json:"usageThresholds,omitempty"`
  16. // UsageAggregationType indicates the percentile type of the machine's utilization when filtering
  17. UsageAggregationType slov1alpha1.AggregationType `json:"usageAggregationType,omitempty"`
  18. // UsageAggregatedDuration indicates the statistical period of the percentile of the machine's utilization when filtering
  19. UsageAggregatedDuration *metav1.Duration `json:"usageAggregatedDuration,omitempty"`
  20. }

使用负载感知调度

感知整机负载进行调度

本文示例的集群有3台 4核16GiB 节点。

  1. 使用下面的 YAML 创建一个 stress Pod
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: stress-demo
  5. namespace: default
  6. labels:
  7. app: stress-demo
  8. spec:
  9. replicas: 1
  10. selector:
  11. matchLabels:
  12. app: stress-demo
  13. template:
  14. metadata:
  15. name: stress-demo
  16. labels:
  17. app: stress-demo
  18. spec:
  19. containers:
  20. - args:
  21. - '--vm'
  22. - '2'
  23. - '--vm-bytes'
  24. - '1600M'
  25. - '-c'
  26. - '2'
  27. - '--vm-hang'
  28. - '2'
  29. command:
  30. - stress
  31. image: polinux/stress
  32. imagePullPolicy: Always
  33. name: stress
  34. resources:
  35. limits:
  36. cpu: '2'
  37. memory: 4Gi
  38. requests:
  39. cpu: '2'
  40. memory: 4Gi
  41. restartPolicy: Always
  42. schedulerName: koord-scheduler # use the koord-scheduler
  1. $ kubectl create -f stress-demo.yaml
  2. deployment.apps/stress-demo created
  1. 观察 Pod 的状态,直到它开始运行。
  1. $ kubectl get pod -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. stress-demo-7fdd89cc6b-gcnzn 1/1 Running 0 82s 10.0.3.114 cn-beijing.10.0.3.112 <none> <none>

Pod stress-demo-7fdd89cc6b-gcnzn 调度在 cn-beijing.10.0.3.112

  1. 检查每个node节点的负载。
  1. $ kubectl top node
  2. NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
  3. cn-beijing.10.0.3.110 92m 2% 1158Mi 9%
  4. cn-beijing.10.0.3.111 77m 1% 1162Mi 9%
  5. cn-beijing.10.0.3.112 2105m 53% 3594Mi 28%

按照输出结果显示,节点 cn-beijing.10.0.3.111 负载最低,节点cn-beijing.10.0.3.112 的负载最高。

  1. 使用下面的 YAML 文件部署 nginx deployment。
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-with-loadaware
  5. labels:
  6. app: nginx
  7. spec:
  8. replicas: 6
  9. selector:
  10. matchLabels:
  11. app: nginx
  12. template:
  13. metadata:
  14. name: nginx
  15. labels:
  16. app: nginx
  17. spec:
  18. schedulerName: koord-scheduler # use the koord-scheduler
  19. containers:
  20. - name: nginx
  21. image: nginx
  22. resources:
  23. limits:
  24. cpu: 500m
  25. requests:
  26. cpu: 500m
  1. $ kubectl create -f nginx-with-loadaware.yaml
  2. deployment/nginx-with-loadawre created
  1. 检查 nginx Pods 的调度结果。
  1. $ kubectl get pods | grep nginx
  2. nginx-with-loadaware-5646666d56-224jp 1/1 Running 0 18s 10.0.3.118 cn-beijing.10.0.3.110 <none> <none>
  3. nginx-with-loadaware-5646666d56-7glt9 1/1 Running 0 18s 10.0.3.115 cn-beijing.10.0.3.110 <none> <none>
  4. nginx-with-loadaware-5646666d56-kcdvr 1/1 Running 0 18s 10.0.3.119 cn-beijing.10.0.3.110 <none> <none>
  5. nginx-with-loadaware-5646666d56-qzw4j 1/1 Running 0 18s 10.0.3.113 cn-beijing.10.0.3.111 <none> <none>
  6. nginx-with-loadaware-5646666d56-sbgv9 1/1 Running 0 18s 10.0.3.120 cn-beijing.10.0.3.111 <none> <none>
  7. nginx-with-loadaware-5646666d56-z79dn 1/1 Running 0 18s 10.0.3.116 cn-beijing.10.0.3.111 <none> <none>

现在我们可以看到 nginx pods 被调度在 cn-beijing.10.0.3.112 (负载最高的节点) 以外的节点上。

感知 Prod Pods 的负载进行调度

如果一个 Node 中调度了很多 BestEffort Pod,可能会因为节点的负载已达到使用限制而导致延迟敏感的 Pod 无法调度。 在 Koordinator v1.1.0 中,负载感知调度针对这种场景进行了优化。 对于延迟敏感(LSE/LSR/LS)的 Pod,优先调度到 Prod Pod 总利用率较低的节点,而 BestEffort(BE) Pod 根据整机利用率水平进行调度。

通过设置以下参数启用相关优化:

字段说明版本
prodUsageThresholdsprodUsageThresholds 表示 Prod Pod 相对于整机的资源利用率阈值。 默认情况下不启用。>= v1.1.0
scoreAccordingProdUsagescoreAccordingProdUsage 控制是否根据 Prod Pod 的利用率进行评分。>= v1.1.0

感知基于百分位数统计的利用率进行调度

Koordinator v1.0及以前的版本都是按照 koordlet 上报的平均利用率数据进行过滤和打分。但平均值隐藏了比较多的信息,因此在 Koordinator v1.1 中 koordlet 新增了根据百分位数统计的利用率聚合数据。调度器侧也跟着做了相应的适配。

通过设置以下参数启用相关优化:

字段说明版本
aggregatedaggregated 支持基于百分位数统计的资源利用率过滤和评分。>= v1.1.0

Aggregated 支持的字段:

字段说明版本
usageThresholdsusageThresholds 表示机器基于百分位统计的资源利用率阈值。>= v1.1.0
usageAggregationTypeusageAggregationType 表示过滤时机器利用率的百分位类型。 目前支持 avgp50p90p95p99>= v1.1.0
usageAggregatedDurationusageAggregatedDuration 表示过滤时机器利用率百分位数的统计周期。不设置该字段时,调度器默认使用 NodeMetrics 中最大周期的数据。>= v1.1.0
scoreAggregationTypescoreAggregationType 表示评分时机器利用率的百分位类型。 目前支持 avgp50p90p95p99>= v1.1.0
scoreAggregatedDurationscoreAggregatedDuration 表示打分时 Prod Pod 利用率百分位的统计周期。 不设置该字段时,调度器默认使用 NodeMetrics 中最大周期的数据。>= v1.1.0

aggregatedusageThresholds 参数是互斥的。 当两者都配置时,将使用 aggregated。此外,目前不支持 Pod 类型感知。