负载感知调度
负载感知调度(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 节点。
- 使用下面的 YAML 创建一个
stressPod
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
- 观察 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。
- 检查每个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 的负载最高。
- 使用下面的 YAML 文件部署
nginxdeployment。
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
- 检查
nginxPods 的调度结果。
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 会激活使用量估算:
- 调度中的新 Pod。
- 节点上现有的未终止 Pod 且 NodeMetric 中未收集到此 Pod 的使用率(刚调度或其他原因)。
- 现有 Pod 的指标仍在报告间隔内(
metric.updateTime - reportInterval < podScheduledTime),这意味着 Pod 不存在完整报告间隔,没有足够的指标点。 - 现有 Pod 在估算中配置:插件参数中的
estimatedSecondsAfterPodScheduled和estimatedSecondsAfterInitialized,以及如果允许自定义的话,在 Pod 上设置的scheduling.koordinator.sh/load-estimated-seconds-after-pod-scheduled和scheduling.koordinator.sh/load-estimated-seconds-after-initialized注解。这些配置强制 Pod 在启动时进行估算。
