This topic describes best practices for VKE load-aware scheduling.
Load-aware scheduling is a VKE policy that schedules workloads based on actual node loads. During scheduling, the scheduler preferentially schedules Pods to nodes with lower load based on the historical node load metrics. This balances loads among nodes and reduces application or node failures caused by a high load on a single node. The recommended usage methods for different business scenarios are as follows:
Load-aware scheduling is not recommended to be enabled for short-lived Jobs by default. Jobs exit quickly after scheduling completes, and the node load observed at scheduling time may change quickly. Therefore, the benefit is usually lower than that for online services.
- The cluster is running and contains at least two nodes whose loads need to be balanced.
- The cluster Kubernetes version meets the requirements for load-aware scheduling, and the scheduler-plugin add-on has been installed.
- Load-aware scheduling has been enabled for scheduler-plugin. If a canary rollout is required, set the effective scope to specific Pods.
- Node pool labels are stable. You can use cluster.vke.volcengine.com/machinepool-name or custom workload node labels to distinguish node pools.
- You can check node CPU and memory utilization by using kubectl top nodes, monitoring dashboards, or node load metrics.
Load-aware scheduling takes effect through the scheduler-plugin add-on configuration, Pod annotations, node load metrics, and node-pool-level threshold overrides. The key parameters are as follows.
Scenario 1: Keep online services away from hotspot nodes
This scenario is suitable for long-lived Pods such as online services, gateways, and resident Workers. To reduce the impact of single-node hotspots on service latency and stability, preferentially schedule new Pods on low-load nodes and avoid nodes with CPU or memory hotspots.
Recommended configuration
- Enable the load-aware scheduling feature for the scheduler-plugin add-on.
- Set the effective scope to specific Pods and use an annotation to roll out the feature to target workloads in a canary manner.
- Set global default CPU and memory thresholds as a fallback safeguard when no node pool override is configured.
- Set node scoring resource weights based on the workload bottleneck. For CPU-intensive services, increase the CPU weight. For memory-sensitive services, increase the memory weight.
- Add vke.volcengine.com/load-aware-enabled: "true" to the target Deployment.
- Confirm that load-aware scheduling has been enabled for scheduler-plugin and that the effective scope meets expectations.
- Prepare hotspot nodes. You can use stress-test workloads or existing workloads to drive CPU/memory utilization to high levels. This ensures that at least one high-load node and one low-load node exist in the cluster.
Perform this operation in test clusters or isolated node pools. Do not create high load on nodes handling production traffic. Before starting, confirm monitoring and rollback paths, and immediately delete the stress-test workload after verification is complete.
- Deploy the sample application and confirm that the Pod template contains vke.volcengine.com/load-aware-enabled: "true". The sample application uses node affinity to limit the schedulable scope to the node pool corresponding to the node pool label cluster.vke.volcengine.com/machinepool-name=pd9ji9ullqoqnlv6sok7g. The scheduler selects a more suitable node within that node pool based on load.
- Observe Pod distribution. Newly created Pods are expected to be preferentially scheduled to low-load nodes.
The key risk in this scenario is a trade-off: too low a threshold reduces the number of schedulable nodes, while too high a threshold renders hotspot avoidance ineffective. First, perform a canary rollout on a single workload. Then, monitor Pending Pods, scheduling failure events, the number of hotspot nodes, and service P95/P99 latency. If newly added Pods still land on high-load nodes, check constraints such as the annotation, effective scope, node load data freshness, node affinity, taint toleration, and resource requests at the same time.
Scenario 2: Set different scheduling thresholds for different node pools
This scenario applies to clusters with multiple types of node pools, such as general-purpose compute pools, CPU-optimized pools, memory-optimized node pools, and co-located or low-priority node pools. Different node pools have different specifications, workload types, and resource bottlenecks. If only one set of global thresholds is used, issues such as general-purpose pool overload, delayed protection for memory-optimized nodes, and insufficient utilization of high-specification nodes may occur.
Recommended configuration
- Identify the purpose and stable labels of each node pool. We recommend using cluster.vke.volcengine.com/machinepool-name to match node pools.
- Set different CPU and memory thresholds for each type of node pool.
- Create a NodeSchedulingConfig for each node pool. Each node needs to match only one expected configuration so that the source of the final threshold is clear.
- Retain global default thresholds as a fallback for nodes that do not match any NodeSchedulingConfig.
- During the canary rollout, select one or a small number of node pools for verification. Expand the rollout to other node pools after confirming its effectiveness.
- Confirm that load-aware scheduling has been enabled for scheduler-plugin and that the effective scope meets expectations.
- Create or update NodeSchedulingConfig.
- Check node pool labels to confirm that each node matches only one expected configuration.
- Query the details of the NodeSchedulingConfig.
- Generate real load for the node pools.
- Deploy the workload to the node pool identified by load-threshold-pool-1. The scheduler is expected to reject the workload, confirming that the threshold configured for this node pool takes effect.
- Scale out the preceding workload, add the preceding three node pools to its node pool affinity, and observe whether Pods avoid nodes exceeding their respective node pool thresholds.
The greatest risk when rolling out per-pool thresholds is an unclear selector design. If the same node is matched by multiple NodeSchedulingConfigs at the same time, it is difficult to determine the final threshold source during troubleshooting. Use a unique, stable label for each node pool, and run kubectl get nodes --show-labels before rollout to verify which nodes match each label.
Configure the threshold based on historical monitoring data. For general-purpose compute pools, you can set CPU to 65% to 75% and memory to 70% to 80% from the start. For high-CPU pools, you can appropriately increase the CPU threshold. For memory-optimized node pools, we recommend setting the memory threshold more conservatively, such as 60% to 70%. After rollout, continue adjusting the values based on workload latency, Pending Pods, the number of node hotspots, and resource utilization.
- Select the target workload and confirm that Pod labels, resource requests, and scheduling constraints are stable.
- Enable the load-aware scheduling capability of the scheduler-plugin, and select specific Pods as the effective scope first.
- Add vke.volcengine.com/load-aware-enabled: "true" to the canary workload.
- Set global default CPU and memory thresholds and resource weights.
- To set thresholds by node pool, create NodeSchedulingConfig and verify the node matching relationship.
- Deploy or scale out the canary workload and observe Pod distribution, node load, and scheduling events.
- After the canary rollout is stable, expand the rollout to similar workloads. Consider a global rollout only after confirming that monitoring is stable, thresholds are clear, and the rollback path is well-defined.
Load-aware scheduling depends on node load data. If monitoring data is missing, delayed, or significantly deviates from the actual load, the scheduling performance will degrade. During troubleshooting, check the NodeMonitor update time, node monitoring metrics, Pod events, and scheduler-plugin configuration at the same time.
Load-aware scheduling does not bypass native Kubernetes scheduling constraints. Node affinity, taint tolerations, resource requests, and topology constraints still affect the final scheduling result. If these constraints have already narrowed the eligible nodes to a small set, load-aware scheduling may still be unable to avoid hotspot nodes even if it is enabled.
- Change the canary workload annotation to vke.volcengine.com/load-aware-enabled: "false", or delete the annotation.
- If a node-pool-level NodeSchedulingConfig has been created, first delete the newly created configuration or change the Selector to an empty match to restore the global default thresholds.
- If it has been enabled globally, change the effective scope back to specific Pods in the scheduler-plugin configuration or disable load-aware scheduling.
- Roll out the workload again, observe whether new Pods return to normal scheduling, and confirm that Pending Pods and hotspot node metrics return to the expected range.