负载感知调度

负载感知调度(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/v1
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. # supported Koordinator >= v1.7.0, calculates and stores reusable data
27. preFilter:
28. enabled:
29. - name: LoadAwareScheduling
30. ...
31. pluginConfig:
32. # configure the thresholds and weights for the plugin
33. - name: LoadAwareScheduling
34. args:
35. apiVersion: kubescheduler.config.k8s.io/v1
36. kind: LoadAwareSchedulingArgs
37. # whether to filter nodes where koordlet fails to update NodeMetric
38. filterExpiredNodeMetrics: true
39. # the expiration threshold seconds when using NodeMetric
40. nodeMetricExpirationSeconds: 300
41. # weights of resources
42. resourceWeights:
43. cpu: 1
44. memory: 1
45. # thresholds (%) of resource utilization
46. usageThresholds:
47. cpu: 75
48. memory: 85
49. # thresholds (%) of resource utilization of Prod Pods
50. prodUsageThresholds:
51. cpu: 55
52. memory: 65
53. # enable score according Prod usage
54. scoreAccordingProdUsage: true
55. # the factor (%) for estimating resource usage
56. estimatedScalingFactors:
57. cpu: 80
58. memory: 70
59. # enable resource utilization filtering and scoring based on percentile statistics
60. aggregated:
61. usageThresholds:
62. cpu: 65
63. memory: 75
64. usageAggregationType: "p99"
65. scoreAggregationType: "p99"

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

字段 说明 版本
--- --- ---
filterExpiredNodeMetrics filterExpiredNodeMetrics 表示是否过滤koordlet更新NodeMetric失败的节点。 默认情况下启用,但在 Helm chart 中,它被禁用。 >= v0.4.0
nodeMetricExpirationSeconds nodeMetricExpirationSeconds 表示 NodeMetric 过期时间(以秒为单位)。 当 NodeMetrics 过期时,节点被认为是异常的。 默认为 180 秒。 >= v0.4.0
enableScheduleWhenNodeMetricsExpired enableScheduleWhenNodeMetricsExpired 表示是否允许具有过期 nodeMetrics 的节点调度 Pod。 >= v1.5.0
resourceWeights resourceWeights 表示资源的权重。 CPU 和 Memory 的权重默认都是 1。 >= v0.4.0
dominantResourceWeight dominantResourceWeight 表示主导资源的权重。主导资源是利用率最大的资源,基于主导资源公平性概念。 >= v1.7.0
usageThresholds usageThresholds 表示整机的资源利用率阈值。 CPU 的默认值为 65%,内存的默认值为 95%。 >= v0.4.0
estimatedScalingFactors estimatedScalingFactors 表示估计资源使用时的因子。 CPU 默认值为 85%,Memory 默认值为 70%。 >= v0.4.0
prodUsageThresholds prodUsageThresholds 表示 Prod Pod 相对于整机的资源利用率阈值。 默认情况下不启用。 >= v1.1.0
prodUsageIncludeSys prodUsageIncludeSys 表示在汇总当前 prod pod 使用量时是否包含系统使用量(非 pod 使用)。 >= v1.7.0
scoreAccordingProdUsage scoreAccordingProdUsage 控制是否根据 Prod Pod 的利用率进行评分。 >= v1.1.0
aggregated aggregated 支持基于百分位数统计的资源利用率过滤和评分。 >= v1.1.0
estimatedSecondsAfterPodScheduled estimatedSecondsAfterPodScheduled 表示 Pod 条件 PodScheduled 转换为 True 后的强制估算持续时间(秒)。 >= v1.7.0
estimatedSecondsAfterInitialized estimatedSecondsAfterInitialized 表示 Pod 条件 Initialized 转换为 True 后的强制估算持续时间(秒)。 >= v1.7.0
allowCustomizeEstimation allowCustomizeEstimation 表示是否允许从 Pod 的元数据中读取估算参数。 >= v1.7.0
supportedResources supportedResources 是可以在负载感知调度中使用的额外资源名称列表。cpu、memory 和参数中出现的所有其他资源都默认支持。如果在收集过程中添加了更多资源,但未在插件参数中显示为过滤阈值或评分权重,仅在自定义节点注解中设置,我们应该在插件参数中显式传递这些资源名称。 >= v1.7.0

Aggregated 支持的字段:

字段 说明 版本
--- --- ---
usageThresholds usageThresholds 表示机器基于百分位统计的资源利用率阈值。 >= v1.1.0
usageAggregationType usageAggregationType 表示过滤时机器利用率的百分位类型。 目前支持 avg、p50、p90、p95 和 p99。 >= v1.1.0
usageAggregatedDuration usageAggregatedDuration 表示过滤时机器利用率百分位数的统计周期。不设置该字段时,调度器默认使用 NodeMetrics 中最大周期的数据。 >= v1.1.0
scoreAggregationType scoreAggregationType 表示评分时机器利用率的百分位类型。 目前支持 avg、p50、p90、p95 和 p99。 >= v1.1.0
scoreAggregatedDuration scoreAggregatedDuration 表示打分时 Prod Pod 利用率百分位的统计周期。 不设置该字段时,调度器默认使用 NodeMetrics 中最大周期的数据。 >= v1.1.0

按照节点配置过滤阈值

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

Annotation 定义如下:

1. const (
2. AnnotationCustomUsageThresholds = "scheduling.koordinator.sh/usage-thresholds"
3. )

5. // CustomUsageThresholds supports user-defined node resource utilization thresholds.
6. type CustomUsageThresholds struct {
7. // UsageThresholds indicates the resource utilization threshold of the whole machine.
8. UsageThresholds map[corev1.ResourceName]int64 `json:"usageThresholds,omitempty"`
9. // ProdUsageThresholds indicates the resource utilization threshold of Prod Pods compared to the whole machine
10. ProdUsageThresholds map[corev1.ResourceName]int64 `json:"prodUsageThresholds,omitempty"`
11. // AggregatedUsage supports resource utilization filtering and scoring based on percentile statistics
12. AggregatedUsage *CustomAggregatedUsage `json:"aggregatedUsage,omitempty"`
13. }

15. type CustomAggregatedUsage struct {
16. // UsageThresholds indicates the resource utilization threshold of the machine based on percentile statistics
17. UsageThresholds map[corev1.ResourceName]int64 `json:"usageThresholds,omitempty"`
18. // UsageAggregationType indicates the percentile type of the machine's utilization when filtering
19. UsageAggregationType slov1alpha1.AggregationType `json:"usageAggregationType,omitempty"`
20. // UsageAggregatedDuration indicates the statistical period of the percentile of the machine's utilization when filtering
21. UsageAggregatedDuration *metav1.Duration `json:"usageAggregatedDuration,omitempty"`
22. }

使用负载感知调度

感知整机负载进行调度

本文示例的集群有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 根据整机利用率水平进行调度。

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

字段 说明 版本
--- --- ---
prodUsageThresholds prodUsageThresholds 表示 Prod Pod 相对于整机的资源利用率阈值。 默认情况下不启用。 >= v1.1.0
prodUsageIncludeSys prodUsageIncludeSys 表示在汇总当前 prod pod 使用量时是否包含系统使用量(非 pod 使用)。 >= v1.7.0
scoreAccordingProdUsage scoreAccordingProdUsage 控制是否根据 Prod Pod 的利用率进行评分。 >= v1.1.0

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

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

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

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

Aggregated 支持的字段:

字段 说明 版本
--- --- ---
usageThresholds usageThresholds 表示机器基于百分位统计的资源利用率阈值。 >= v1.1.0
usageAggregationType usageAggregationType 表示过滤时机器利用率的百分位类型。 目前支持 avg、p50、p90、p95 和 p99。 >= v1.1.0
usageAggregatedDuration usageAggregatedDuration 表示过滤时机器利用率百分位数的统计周期。不设置该字段时,调度器默认使用 NodeMetrics 中最大周期的数据。 >= v1.1.0
scoreAggregationType scoreAggregationType 表示评分时机器利用率的百分位类型。 目前支持 avg、p50、p90、p95 和 p99。 >= v1.1.0
scoreAggregatedDuration scoreAggregatedDuration 表示打分时 Prod Pod 利用率百分位的统计周期。 不设置该字段时,调度器默认使用 NodeMetrics 中最大周期的数据。 >= v1.1.0

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

负载感知调度中的资源估算

仅基于实时使用率的负载感知对某些场景来说还不够好,因为使用率是持续变化的。一个常见的情况是,Pod 在启动时需要几分钟时间从网络加载远程数据,数据准备好后才开始执行算子,这意味着在启动阶段中 CPU 使用量被低估。

在 Koordinator 中,Pod 使用资源估算默认由 LoadAwareSchedulingArgs 中的 estimatedScalingFactors 控制。可以通过在 Pod 上设置 scheduling.koordinator.sh/load-estimated-scaling-factors 注解来自定义。结果是 estimated = max(factor * max(pod-requests[resource], pod-limits[resource]), pod-usage[resource])。

以下状态的 Pod 会激活使用量估算:

  1. 调度中的新 Pod。
  2. 节点上现有的未终止 Pod 且 NodeMetric 中未收集到此 Pod 的使用率(刚调度或其他原因)。
  3. 现有 Pod 的指标仍在报告间隔内(metric.updateTime - reportInterval < podScheduledTime),这意味着 Pod 不存在完整报告间隔,没有足够的指标点。
  4. 现有 Pod 在估算中配置:插件参数中的 estimatedSecondsAfterPodScheduled 和 estimatedSecondsAfterInitialized,以及如果允许自定义的话,在 Pod 上设置的 scheduling.koordinator.sh/load-estimated-seconds-after-pod-scheduled 和 scheduling.koordinator.sh/load-estimated-seconds-after-initialized 注解。这些配置强制 Pod 在启动时进行估算。