Distributing workloads evenly across multiple zones is a core strategy for building a high-availability architecture. As your workload increases, a multi-zone balanced scheduling policy automatically scales out instances across zones to meet cluster capacity demands.
Prerequisites
You have created at least one vSwitch in each zone where you want to scale out instances. For more information, see Create and manage vSwitches. After creation, you can select the corresponding vSwitch when you create and manage node pools.
Background
The cluster autoscaler can determine if a service can be deployed on a scaling group and send a scale-out request to a specified ESS scaling group to create new instances. However, a problem arises when you configure vSwitches from multiple zones within a single ESS scaling group. When workload pods cannot be scheduled due to insufficient cluster resources, ACK triggers the ESS scaling group to scale out. But without a mechanism to associate the scale-out request with a specific zone, the ESS scaling group does not know which zone needs more capacity. This can cause new instances to be concentrated in a single zone instead of being distributed across multiple zones, failing to meet the demand for concurrent, multi-zone scaling.
To address this, ACK provides the ack-autoscaling-placeholder component. Using a small amount of reserved resources, this component transforms generic multi-zone auto scaling into targeted, concurrent scaling of specific node pools. For more information, see Achieve Container Scaling in Seconds Using ack-autoscaling-placeholder. Here's how it works:
-
Create a separate node pool for each zone and apply a unique zone label to each node pool.
-
Use the ack-autoscaling-placeholder component to create placeholder pods in each zone by using a
nodeSelectorthat targets the zone-specific labels. By default, these placeholder pods have a low-priorityPriorityClass, giving them a lower priority than your application pods. -
When a high-priority workload pod becomes pending, it preempts a placeholder pod in one of the zones. The preempted placeholder pod, which has a zone-specific
nodeSelector, then becomes pending. This signals the cluster autoscaler to scale out a new node specifically in that zone, transforming a general scaling need into a targeted request.
The following example uses two zones to illustrate how to achieve concurrent scaling across multiple zones.
-
The ack-autoscaling-placeholder component acts as a bridge between your applications and the cluster autoscaler by creating a placeholder pod in each zone. The scheduling priority of these placeholder pods is lower than your application pods.
-
When an application pod becomes pending, it immediately preempts a placeholder pod and runs on an existing node in one of the zones. The preempted placeholder pod then enters the Pending state.
-
Because each placeholder pod is configured with a zone-specific
nodeSelector, the cluster autoscaler knows exactly which zone to scale, enabling concurrent scaling in the correct zones.
Step 1: Create node pools and add labels
-
Log on to the ACK console. In the left navigation pane, click Clusters.
-
On the Clusters page, click the name of your target cluster. In the navigation pane on the left, choose .
-
Click Create Node Pool and configure the parameters as prompted.
This example creates a node pool named auto-zone-I with auto scaling enabled in zone I. The following table describes the key parameters. For more information, see Create and manage node pools.
Parameter
Description
Node Pool Name
auto-zone-I
Scaling Mode
Select Auto to enable auto scaling.
vSwitch
Select the vSwitch for zone I.
Node Labels
Set the Key to
available_zoneand the Value toi.In the node pool list, the node pool is successfully created when its status changes to Active.
-
Repeat these steps for each zone where you want to enable auto scaling.
After creation, you can see the node pools for each zone (for example, auto-zone-I, auto-zone-K, and auto-zone-H) in the node pool list. You can select different instance types as needed (such as
ecs.hfc7.xlarge,ecs.c7.xlarge, orecs.s6-c1m2.xlarge). The billing method is pay-as-you-go, and the status of each node pool is Active with auto scaling enabled.
Step 2: Deploy the placeholder component and Deployments
In the left navigation pane, choose .
-
Search for ack-autoscaling-placeholder, click the component card, and then click Deploy.
-
Select a Cluster and Namespace, click Next, select a Chart Version, edit the Parameter, and then click OK.
After the application is created, navigate to the page. The status of the application is Deployed.
In the left navigation pane of the cluster management page, choose .
-
On the Helm page, find ack-autoscaling-placeholder-default and click Update in the Actions column.
-
In the Update Release panel, update the YAML configuration as shown in the following example and click OK. You must define a placeholder Deployment for each zone.
This example shows how to create placeholder Deployments in zones I, K, and H.
deployments: - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # CPU request for the placeholder pod. memory: 6 # Memory request for the placeholder pod. imagePullSecrets: {} labels: {} name: ack-place-holder-I # The name of the placeholder Deployment. nodeSelector: {"available_zone":"i"} # The zone label. It must match the label configured for the node pool in Step 1. replicaCount: 10 # The number of pods to create in each scale-out event. tolerations: [] - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # CPU request for the placeholder pod. memory: 6 # Memory request for the placeholder pod. imagePullSecrets: {} labels: {} name: ack-place-holder-K # The name of the placeholder Deployment. nodeSelector: {"available_zone":"k"} # The zone label. It must match the label configured for the node pool in Step 1. replicaCount: 10 # The number of pods to create in each scale-out event. tolerations: [] - affinity: {} annotations: {} containers: - image: registry-vpc.cn-beijing.aliyuncs.com/acs/pause:3.1 imagePullPolicy: IfNotPresent name: placeholder resources: requests: cpu: 3500m # CPU request for the placeholder pod. memory: 6 # Memory request for the placeholder pod. imagePullSecrets: {} labels: {} name: ack-place-holder-H # The name of the placeholder Deployment. nodeSelector: {"available_zone":"h"} # The zone label. It must match the label configured for the node pool in Step 1. replicaCount: 10 # The number of pods to create in each scale-out event. tolerations: [] fullnameOverride: "" nameOverride: "" podSecurityContext: {} priorityClassDefault: enabled: true name: default-priority-class value: -1A successful update creates a placeholder Deployment for each zone.
Step 3: Create a workload PriorityClass
-
Create a file named priorityClass.yaml by using the following YAML template.
apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 # Set a priority value higher than the default priority of the placeholder pods from Step 2. globalDefault: false description: "This priority class should be used for XYZ service pods only."If you do not need to configure a specific PriorityClass for each pod, you can set a global default. Once configured, any pod without a specified
priorityClassNameautomatically uses this priority.apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: global-high-priority value: 1 # Set a priority value higher than the default priority of the placeholder pods from Step 2. globalDefault: true description: "This priority class should be used for XYZ service pods only." -
Create the PriorityClass.
kubectl apply -f priorityClass.yamlExpected output:
priorityclass.scheduling.k8s.io/high-priority created
Step 4: Create the actual workload
The following example creates a workload in zone I.
-
Create a file named workload.yaml by using the following YAML template.
apiVersion: apps/v1 kind: Deployment metadata: name: placeholder-test labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: nodeSelector: # Node selector. available_zone: "i" priorityClassName: high-priority # The name of the PriorityClass created in Step 3. This field is optional if you have configured a global default. containers: - name: nginx image: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6 ports: - containerPort: 80 resources: requests: cpu: '3' # Resource requests for the actual workload. memory: 5Gi -
Deploy the actual workload.
kubectl apply -f workload.yamlExpected output:
deployment.apps/placeholder-test createdAfter the workload is deployed, go to the page. You will see that because the workload has a higher PriorityClass, it preempts a placeholder pod and runs on its node. The preempted placeholder pod, in turn, triggers the cluster autoscaler to perform a concurrent scale-out, preparing capacity for future workload scaling.
On the page, observe that the workload pod now runs on the node previously occupied by a placeholder pod. You can also verify that the cluster has scaled out new worker nodes. For example, you might see two new
ecs.c6e.xlarge(4 vCPU, 8 GiB) nodes in a running state, confirming that the cluster autoscaler successfully performed the concurrent scale-out.