17. Storage: Volumes, PVs, PVCs, StorageClasses
17.1 The abstraction stack
Pod ──uses──► PersistentVolumeClaim ("I need 10Gi RWO, class fast-ssd")
│ bound to
▼
PersistentVolume (an actual disk: EBS vol-123, GCE PD, local path)
▲ dynamically created by
StorageClass (provisioner = CSI driver + parameters)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com # kind uses rancher.io/local-path
parameters: { type: gp3, encrypted: "true" }
reclaimPolicy: Retain # keep the disk when the PVC is deleted (prod data!)
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer # create the disk in the zone where the pod is scheduled
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: data }
spec:
accessModes: [ReadWriteOnce]
storageClassName: fast-ssd
resources: { requests: { storage: 10Gi } }
| Access mode | Meaning | Typical backend |
|---|---|---|
ReadWriteOnce (RWO) |
one node mounts read-write | block storage: EBS, PD, Azure Disk |
ReadWriteOncePod |
one pod | same; stricter single-writer guarantee |
ReadOnlyMany (ROX) |
many nodes read-only | |
ReadWriteMany (RWX) |
many nodes read-write | NFS, EFS, Filestore, CephFS (slower, use sparingly) |
War story:
volumeBindingMode: Immediate(the old default in many classes) created a pod's EBS volume inus-east-1abefore the pod was scheduled. The scheduler then put the pod in1b, where the volume couldn't attach, and the pod stayedPendingforever with "volume node affinity conflict". Always useWaitForFirstConsumerfor zonal disks.
17.2 Ephemeral volumes
emptyDir: scratch space for the pod's lifetime;medium: Memorymakes it tmpfs (counts against the memory limit). Pair it withreadOnlyRootFilesystemfor/tmp.configMap,secret,downwardAPI,projected: config as files.- Never
hostPathin application workloads. It is a container-escape primitive and ties the pod to a node.