✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes Volume Aware Scheduling

Kubernetes Volume Aware Scheduling places workloads based on storage availability, enhancing reliability and resource efficiency in Kubernetes environments.

Kubernetes Volume Aware Scheduling is the coordination between the scheduler and the storage subsystem that ensures a Pod referencing persistent storage is placed only on nodes actually capable of accessing that storage — accounting for volume topology constraints (a volume that only exists in one zone), attachment limits (how many volumes a node can have attached simultaneously), and the timing of volume provisioning relative to Pod placement. Because storage and compute are not always co-located or freely interchangeable the way ordinary CPU and memory resources are, volume-aware scheduling exists specifically to prevent the scheduler from placing a Pod somewhere its required storage cannot actually follow it.

The central mechanism enabling this coordination is delaying volume binding until scheduling has already determined a Pod's node, rather than binding storage first and hoping a compatible node happens to be available afterward.


Volume Binding Modes

Immediate Binding

volumeBindingMode: Immediate (the historical default for many storage classes) provisions or binds the volume as soon as the PersistentVolumeClaim is created, before any Pod referencing it has been scheduled — this can result in a volume being provisioned in a zone or on infrastructure that later turns out to be a poor fit for wherever the scheduler eventually decides to place the consuming Pod.

WaitForFirstConsumer

volumeBindingMode: WaitForFirstConsumer delays volume binding and provisioning until a Pod referencing the claim has actually been scheduled, allowing the volume to be created with full knowledge of which node (and by extension, which zone) the Pod was placed on — this is the modern, generally recommended default for topology-sensitive storage classes, since it lets the scheduler's placement decision inform storage provisioning rather than the reverse.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: codartium-ssd
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer

How the Scheduler Accounts for Volumes

Filtering Nodes by Volume Topology

For a Pod referencing an already-bound volume with topology constraints (an existing EBS volume in a specific zone), the scheduler filters out any node outside that volume's accessible zone during the candidate evaluation phase, treating volume topology as a hard scheduling constraint exactly like a required node affinity rule.

Attach Limits per Node

Many cloud providers impose a maximum number of volumes that can be attached to a single node simultaneously (a common limit on AWS, for instance, is around 25-39 EBS volumes depending on instance type). The scheduler accounts for this limit as part of its fit evaluation, filtering out nodes that have already reached their attachment capacity, preventing a Pod from being placed somewhere its volume could never actually be attached.

CSI Node Plugin Awareness

The scheduler's volume-aware behavior relies on information reported by CSI (Container Storage Interface) drivers about each node's storage capabilities and current attachment state, meaning volume-aware scheduling is only as accurate as the CSI driver's own reporting — a misbehaving or outdated CSI driver can produce incorrect scheduling decisions around storage fit.


Storage Capacity Tracking

CSIStorageCapacity

For storage backends with genuinely limited local capacity (as opposed to effectively unlimited cloud-provisioned volumes), the CSIStorageCapacity API allows a CSI driver to report available capacity per node/topology segment, which the scheduler then factors into its filtering — a Pod requesting more storage than is available in the relevant segment is filtered out of that segment's nodes, avoiding a placement that would later fail at provisioning time.

kubectl get csistoragecapacities -A

Common Volume-Aware Scheduling Failures

Zone Mismatch Between Pod and Volume

A Pod scheduled (through affinity, taints, or resource fit) onto a node in a zone that does not match its bound volume's zone will fail to start, remaining stuck with a mount failure rather than a scheduling failure per se — this specific failure mode is largely what WaitForFirstConsumer binding is designed to prevent by ensuring the volume is provisioned to match the Pod's actual placement, not the reverse.

kubectl describe pod codartium-stateful-app | grep -A 5 "Events"

StatefulSet Pods and Pre-Existing Volumes

For StatefulSet Pods with existing bound PersistentVolumeClaims (from a previous Pod deletion and recreation with the same ordinal), the scheduler must place the Pod back into the volume's existing zone — this is a hard constraint that can produce a Pending Pod if the original zone has since lost all viable capacity, requiring either restoring capacity in that zone or migrating the underlying data to a new volume in a currently viable zone.


Example

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: codartium-data
spec:
  storageClassName: codartium-ssd
  accessModes: ["ReadWriteOnce"]
  resources:
    requests:
      storage: 50Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: codartium-volume-aware-example
spec:
  containers:
    - name: app
      image: codartium/app:latest
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: codartium-data