Stage 5 · Platform
Stateful Storage Operations
StorageClass Topology
Provisioners, volumeBindingMode, allowedTopologies, reclaimPolicy, and multi-zone placement.
StorageClass Review
StorageClass defines how storage is provisioned. It specifies the provisioner (CSI driver), parameters (volume type, IOPS), reclaim policy, and volume binding mode. Multiple StorageClasses provide different tiers of storage.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: zonal-ssd
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
- key: topology.kubernetes.io/zone
values:
- us-east-1a
- us-east-1b
- us-east-1c
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: trueWaitForFirstConsumer delays binding until a pod is scheduled. allowedTopologies restricts which zones can be used. This ensures volumes are created in the same zone as the pods.
Topology Constraints
In multi-zone clusters, volumes in zone A cannot be attached to nodes in zone B. This is a hard constraint for block storage (EBS, PD, Azure Disk). File storage (NFS, Azure Files) and object storage are zone-independent.
| Storage Type | Zone Limit | Multi-Attach |
|---|---|---|
| Block (EBS, PD) | Single zone | No (except io2 Multi-Attach) |
| File (NFS, CephFS) | Any zone | Yes |
| Object (S3, Blob) | Any zone | Yes |
Binding Modes
volumeBindingMode controls when PVs are created. Immediate creates the PV right away. WaitForFirstConsumer delays until a pod is scheduled. WaitForFirstConsumer is essential for multi-zone clusters to prevent zone mismatch.
# Immediate binding — volume created in provisioner's zone
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: immediate-binding
provisioner: ebs.csi.aws.com
volumeBindingMode: Immediate # Default
parameters:
type: gp3
---
# Wait for consumer — volume created in pod's zone
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: topology-aware
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
type: gp3With Immediate, the volume is created immediately in the provisioner's zone. If the pod lands in a different zone, attachment fails. With WaitForFirstConsumer, the volume is created in the pod's zone after scheduling.
Allowed Topologies
allowedTopologies restricts which zones or regions can be used for volume creation. This prevents volumes from being created in zones where workloads don't run, reducing cross-zone data transfer costs.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: regional-ssd
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
- key: topology.kubernetes.io/zone
values:
- us-east-1a
- us-east-1b
- us-east-1c
parameters:
type: gp3
encrypted: "true"allowedTopologies limits volume creation to us-east-1a, 1b, and 1c. Pods in us-east-1d cannot use this StorageClass. This ensures volumes are created only in zones where workloads exist.
Multi-Zone Storage
For multi-zone workloads, use storage that supports multi-attach or zone-independent access. Azure Files, NFS, and CephFS work across zones. For block storage, use one PVC per zone or use regional disks (GCP).
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
podManagementPolicy: Parallel
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: postgres
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: zonal-ssd
resources:
requests:
storage: 100GitopologySpreadConstraints ensures pods are distributed across zones. Each pod gets a PVC in its zone. The StorageClass with WaitForFirstConsumer creates volumes in the correct zone.
Topology Debugging
# Check PVC binding status
kubectl get pvc -o wide
# Check PV topology
kubectl get pv -o jsonpath='{.items[*].spec.topology}'
# Check node labels for topology
kubectl get nodes -L topology.kubernetes.io/zone
# Check StorageClass binding mode
kubectl get sc -o custom-columns=NAME:.metadata.name,BINDING:.volumeBindingMode
# Events for PVC issues
kubectl describe pvc my-claim
kubectl get events --field-selector reason=ProvisioningFailedPVC stuck in Pending with no PV means topology mismatch or insufficient capacity. Check node zones, StorageClass binding mode, and allowedTopologies. Events show the exact provisioning error.
Always use WaitForFirstConsumer for multi-zone clusters. Only use Immediate if you're certain all pods will run in the same zone as the provisioner. This prevents the most common topology issue — cross-zone attachment failure.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.