All Products
Search
Document Center

Container Service for Kubernetes:Implement zone-based pod spreading and affinity scheduling for virtual nodes

Last Updated:Mar 26, 2026

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)
Important

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:

  • 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, or topologySpreadConstraints parameter 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 topologyKey to topology.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

  1. Save the YAML to deployment.yaml, then apply it:

    kubectl apply -f deployment.yaml
  2. 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>"
  3. 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

  1. Save the YAML to deployment.yaml, then apply it:

    kubectl apply -f deployment.yaml
  2. 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>"
  3. 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.

image

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.

image

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.

image

To disable strict topology spread, set whenUnsatisfiable to ScheduleAnyway. For details, see Spread constraint definition.Spread Constraint Definition