In distributed deployments, a pod creation failure in one zone can concentrate too many replicas in surviving zones, violating your spread requirements and increasing cross-zone latency. In an ACK Serverless Pro cluster, you can use Kubernetes-native scheduling to distribute Elastic Container Instance (ECI)-based pods evenly across zones, or pin them to a specific zone for low-latency access.
Background
Kubernetes provides three scheduling primitives for zone-level pod placement:
| Primitive | Behavior | Use when |
|---|---|---|
topologySpreadConstraints |
Distributes pods evenly across zones based on a maximum skew | You need balanced distribution across multiple zones |
podAffinity |
Attracts pods to zones where matching pods already run | You need to co-locate pods in the same zone |
nodeAffinity |
Pins pods to specific zones based on node labels | You need to target a named zone (for example, cn-beijing-a) |
These features apply to ECI-based pods only when nodeAffinity, podAffinity, or topologySpreadConstraints is configured for the pods, or when the pods match an existing resource policy.
For Kubernetes documentation on these APIs, see Pod topology spread constraints, Assigning pods to nodes, Node affinity, Pod affinity, and maxSkew.
Prerequisites
Before you begin, make sure that:
-
An ACK Serverless Pro cluster exists and meets these requirements:
-
Kubernetes version 1.22 or later
-
ACK Virtual Node component version 2.10.0 or later
-
kube-scheduler component version 5.9 or later, with the virtual node-based pod scheduling feature enabled (see Enable the virtual node-based pod scheduling policy for an ACK cluster)
-
-
Multiple zones (vSwitches) are configured in the eci-profile so pods can be scheduled to multiple zones (see Configure an eci-profile)
-
The
nodeAffinity,podAffinity, ortopologySpreadConstraintsparameter is configured for the pods you want to schedule, or the pods match an existing resource policy
To schedule pods to ARM-based virtual nodes, add tolerations for the virtual node taints in the tolerations field of the pod spec.
Usage notes
-
Set
topologyKeytotopology.kubernetes.io/zone. -
The following annotations disable the zone spread and affinity features:
Annotation Effect k8s.aliyun.com/eci-schedule-strategy: "VSwitchOrdered"Enables a multi-zone scheduling strategy that follows a specified vSwitch order k8s.aliyun.com/eci-fail-strategy: "fail-fast"Sets the pod fault handling policy to fail-fast
Spread ECI-based pods across zones and configure affinities
The following examples use an ACK Serverless Pro cluster running Kubernetes 1.22.
Example 1: Spread pods evenly across zones using topology spread constraints
The topologySpreadConstraints field controls the maximum difference in pod count between zones (maxSkew). With maxSkew: 1 across three zones, no zone can have more than one extra pod compared to any other zone.
The field supports these parameters:
topologySpreadConstraints:
- maxSkew: <integer>
minDomains: <integer> # Optional. Beta in Kubernetes 1.25 and later.
topologyKey: <string>
whenUnsatisfiable: <string>
labelSelector: <object>
matchLabelKeys: <list> # Optional. Beta in Kubernetes 1.27 and later.
nodeAffinityPolicy: [Honor|Ignore] # Optional. Beta in Kubernetes 1.26 and later.
nodeTaintsPolicy: [Honor|Ignore] # Optional. Beta in Kubernetes 1.26 and later.
For parameter details, see topologySpreadConstraints field.
The following Deployment spreads 10 replicas evenly across available zones. whenUnsatisfiable: DoNotSchedule prevents scheduling a pod if placing it would violate the maxSkew: 1 constraint.
apiVersion: apps/v1
kind: Deployment
metadata:
name: with-pod-topology-spread
labels:
app: with-pod-topology-spread
spec:
replicas: 10
selector:
matchLabels:
app: with-pod-topology-spread
template:
metadata:
labels:
app: with-pod-topology-spread
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: with-pod-topology-spread
containers:
- name: with-pod-topology-spread
image: registry.k8s.io/pause:2.0
resources:
requests:
cpu: "1"
memory: "256Mi"
Deploy and verify
-
Save the YAML to
deployment.yaml, then apply it:kubectl apply -f deployment.yaml -
Query the nodes on which the pods are running:
kubectl get po -lapp=with-pod-topology-spread -ocustom-columns=NAME:.metadata.name,NODE:.spec.nodeName --no-headers | grep -v "<none>" -
Count pods per zone to confirm even distribution:
kubectl get po -lapp=with-pod-topology-spread -ocustom-columns=NODE:.spec.nodeName --no-headers | grep -v "<none>" | xargs -I {} kubectl get no {} -ojson | jq '.metadata.labels["topology.kubernetes.io/zone"]' | sort | uniq -c
Example 2: Pin pods to a specific zone using affinities
Use podAffinity to co-locate pods in the same zone as existing matching pods, or nodeAffinity to pin pods to a zone by node label.
Option A: Co-locate pods in one zone using pod affinity
The following Deployment uses podAffinity to schedule all replicas to the same zone as existing pods with the label app: with-affinity.
apiVersion: apps/v1
kind: Deployment
metadata:
name: with-affinity
labels:
app: with-affinity
spec:
replicas: 3
selector:
matchLabels:
app: with-affinity
template:
metadata:
labels:
app: with-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- with-affinity
topologyKey: topology.kubernetes.io/zone
containers:
- name: with-affinity
image: registry.k8s.io/pause:2.0
Option B: Pin pods to a specific zone using node affinity
To deploy pods exclusively in a specific zone, replace podAffinity with nodeAffinity. The following configuration pins pods to Beijing Zone A (cn-beijing-a).
apiVersion: apps/v1
kind: Deployment
metadata:
name: with-affinity
labels:
app: with-affinity
spec:
replicas: 3
selector:
matchLabels:
app: with-affinity
template:
metadata:
labels:
app: with-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- cn-beijing-a
containers:
- name: with-affinity
image: registry.k8s.io/pause:2.0
Deploy and verify
-
Save the YAML to
deployment.yaml, then apply it:kubectl apply -f deployment.yaml -
Query the nodes on which the pods are running:
kubectl get po -lapp=with-affinity -ocustom-columns=NAME:.metadata.name,NODE:.spec.nodeName --no-headers | grep -v "<none>" -
Confirm that all pods land in the expected zone:
kubectl get po -lapp=with-affinity -ocustom-columns=NODE:.spec.nodeName --no-headers | grep -v "<none>" | xargs -I {} kubectl get no {} -ojson | jq '.metadata.labels["topology.kubernetes.io/zone"]' | sort | uniq -c
Strict ECI pod topology spread
By default, kube-scheduler attempts to spread pods evenly across zones, but ECI-based pods may fail to be created in some zones. When pod creation fails, the pod count in the failed zone stays at zero, which can violate the maxSkew constraint. The following diagram shows the scheduling result with maxSkew: 1. For more information about maxSkew, see maxSkew.
If pod creation fails in Zone B and Zone C, two pods run in Zone A and none in the other zones — violating the maxSkew: 1 constraint.
ACK Serverless Pro supports strict ECI pod topology spread to prevent this. When enabled, kube-scheduler first schedules one pod to each zone and holds pending pods until the scheduled pods are successfully created, as shown below.
Even after Pod A1 is created, pending pods are held back. If Pod B1 or Pod C1 fails, scheduling more pods to Zone A would violate maxSkew. Only after Pod B1 is created does kube-scheduler proceed to schedule a pod to Zone C. Pods with green shading are successfully created.
To disable strict topology spread, set whenUnsatisfiable to ScheduleAnyway. For details, see Spread constraint definition.Spread Constraint Definition