Advanced Storage Classes
Advanced Storage Classes: Provisioning Models and Binding Behavior
TL;DR
- Static provisioning: admin manually creates PVs; PVC binds to pre-existing PV (for existing storage, no CSI).
- Dynamic provisioning: StorageClass auto-creates PV via CSI on PVC creation (responsive, no pre-work).
- Released PVs: Retain policy keeps PV alive after PVC deletion; claimRef must be manually cleared for reuse (safety against accidental data bind).
- allowVolumeExpansion: allows PVC size growth; filesystem may need separate resize (
resize2fs). - volumeBindingMode: Immediate (risky for zone storage) vs WaitForFirstConsumer (safe, defers provisioning until pod scheduled).
Core Concepts
Static vs Dynamic Provisioning
Static: Admin creates PV manually, PVC binds to it. For pre-existing storage or systems without CSI.
Dynamic: StorageClass auto-creates PV on PVC creation. Modern, responsive, standard expectation.
Configuration & Command Reference
Clear claimRef on Released PV for reuse:
kubectl patch pv <pv-name> --type json -p '[{"op": "remove", "path": "/spec/claimRef"}]'
Common Pitfalls
- Pitfall: Released PV won’t rebind to new PVC. Why: claimRef safety mechanism. Fix: clear claimRef.
- Pitfall: PVC expanded but filesystem still reports old size. Why: filesystem needs independent resize. Fix:
resize2fson ext4.
Interview Questions
- Why doesn’t a Released PV automatically rebind to a new PVC? — Tests understanding of data safety.
Static provisioning: an administrator manually creates PV objects ahead of time, describing storage that already exists (an NFS export, a pre-created cloud disk). PVCs then bind to a matching pre-existing PV.
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-manual
spec:
capacity:
storage: 100Gi
accessModes: ["ReadWriteMany"]
nfs:
server: nfs.internal.company.com
path: /exports/shared-data
persistentVolumeReclaimPolicy: Retain
Dynamic provisioning: a StorageClass with a provisioner (CSI driver) creates both the underlying storage and the PV object automatically, the moment a matching PVC is created — no administrator pre-work needed. This is the default expectation on any modern cloud-managed cluster.
Static provisioning remains necessary for storage systems with no CSI driver, or for pre-existing data you need pods to attach to (migrating an existing NFS share into Kubernetes, for example).
2. Reclaim Policies in Depth
Delete— PV and underlying storage are deleted when the PVC is released. Default for most dynamically-provisioned classes.Retain— PV persists in aReleasedstate (notAvailable) after PVC deletion; the underlying storage and its data are untouched, but the PV must be manually edited (clearclaimRef) or deleted+recreated before it can be reused by a new PVC.Recycle— deprecated; performed a basicrm -rfscrub before making the volumeAvailableagain. Removed in current Kubernetes versions in favor of dynamic provisioning entirely replacing this use case.
# Reclaim a manually Released PV for reuse
kubectl patch pv nfs-pv-manual --type=json -p '[{"op": "remove", "path": "/spec/claimRef"}]'
3. Volume Binding Modes Revisited
Immediate— PV provisioned as soon as PVC is created, before any pod references it. Risky for zone-aware storage (see the classic multi-zone scheduling deadlock).WaitForFirstConsumer— provisioning deferred until a pod actually claims the PVC, guaranteeing the storage lands in the same zone/node topology as the pod that will use it. The clear default choice for any topology-aware backend (EBS, GCE PD, Azure Disk).
4. Volume Expansion
allowVolumeExpansion: true
With this set on the StorageClass, existing PVCs can be resized larger (never smaller) by simply patching spec.resources.requests.storage — the CSI driver handles the underlying resize, and for many (not all) CSI drivers/filesystems, the pod doesn’t even need to restart (FileSystemResizePending condition clears automatically once the filesystem itself is grown, sometimes requiring the pod to at least be running for the final filesystem-level resize step).
5. Hands-on Command Cheat Sheet
Check a StorageClass’s full configuration including reclaim policy and binding mode:
kubectl get storageclass fast-ssd -o yaml
Find which StorageClass is marked as cluster default:
kubectl get sc -o jsonpath='{range .items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")]}{.metadata.name}{"\n"}{end}'
Check current PV/PVC binding and phase (Bound/Available/Released):
kubectl get pv,pvc
Watch a volume expansion resize progress:
kubectl get pvc db-data -w
kubectl describe pvc db-data | grep -A3 Conditions
6. Architectural Interview Q&A
Q: A PV shows Released status after its PVC was deleted, but a new PVC that should logically reuse it stays Pending. Why doesn’t Kubernetes automatically rebind it?
A: A Released PV still carries the previous claimRef referencing the now-deleted PVC by UID, and Kubernetes deliberately does not automatically clear this or rebind the PV to a different claim — this is a safety mechanism preventing a new, possibly unrelated workload from silently inheriting another application’s data without explicit administrator intent. You must manually clear spec.claimRef (or delete and recreate the PV) before it becomes genuinely Available for a new PVC to bind to.
Q: Why is allowVolumeExpansion alone sometimes not enough to actually see more usable space inside a running pod?
A: Volume expansion at the Kubernetes/CSI level resizes the underlying block device or cloud volume, but the filesystem on top of it (ext4, xfs) still needs its own resize operation to actually recognize and use the additional space — for most CSI drivers this filesystem-level resize is triggered automatically as part of the NodeExpandVolume CSI call, but it typically requires the volume to be actively mounted by a running pod for that final step to execute, meaning a completely stopped/unmounted PVC’s expansion can appear to “hang” at the block-device level until a pod actually mounts it again.
7. Common Pitfalls
[!WARNING] Immediate Binding Mode on Zone-Aware Storage Using
volumeBindingMode: Immediate(rather thanWaitForFirstConsumer) with zone-scoped block storage backends is one of the most common sources of mysterious, hard-to-diagnose “pod stuck Pending forever” incidents — the PV/disk gets provisioned in an essentially random zone before the scheduler has any say, and if the pod’s other constraints later force it into a different zone, the volume can never attach there. Always default toWaitForFirstConsumerfor any StorageClass backed by zone-scoped storage unless you have a specific, deliberate reason not to.