CKA road trip — StorageClass + Provisioner end to end¶
Step 1 — Admin applies the StorageClass once¶
# storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
Step 2 — Developer submits a PVC¶
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
namespace: default
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
kubectl apply -f pvc.yaml
# persistentvolumeclaim/postgres-data created
kubectl get pvc
# NAME STATUS VOLUME CAPACITY STORAGECLASS AGE
# postgres-data Pending fast-ssd 2s
# STATUS is Pending because volumeBindingMode is WaitForFirstConsumer
# it will bind when a pod uses it
Step 3 — Provisioner sees the PVC and calls AWS¶
The provisioner (ebs.csi.aws.com) is a program already running inside the cluster. This is what it does internally when it sees the PVC:
# provisioner sees new PVC: postgres-data, storageClassName: fast-ssd
# it matches — so it runs:
response = ec2.create_volume(
Size=50,
VolumeType='gp3',
Iops=3000,
AvailabilityZone='us-east-1a'
)
# AWS responds: { 'VolumeId': 'vol-0abc1234def5678' }
# provisioner now creates the PV automatically in Kubernetes:
pv = V1PersistentVolume(
metadata=V1ObjectMeta(name='pvc-a1b2c3d4'),
spec=V1PersistentVolumeSpec(
capacity={'storage': '50Gi'},
access_modes=['ReadWriteOnce'],
storage_class_name='fast-ssd',
aws_elastic_block_store=V1AWSElasticBlockStoreVolumeSource(
volume_id='vol-0abc1234def5678',
fs_type='ext4'
)
)
)
k8s.create_persistent_volume(pv)
Step 4 — PV appears automatically (nobody wrote this)¶
kubectl get pv
# NAME CAPACITY ACCESS MODES STORAGECLASS STATUS CLAIM
# pvc-a1b2c3d4 50Gi RWO fast-ssd Bound default/postgres-data
This PV was never written by anyone. The provisioner created it after calling AWS.
kubectl get pvc
# NAME STATUS VOLUME CAPACITY STORAGECLASS AGE
# postgres-data Bound pvc-a1b2c3d4 50Gi fast-ssd 10s
# STATUS is now Bound
Step 5 — Developer deploys the pod using the PVC¶
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: postgres
spec:
containers:
- name: postgres
image: postgres:15
env:
- name: POSTGRES_PASSWORD
value: "password"
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-data # references the PVC
kubectl apply -f pod.yaml
# pod/postgres created
kubectl get pod postgres
# NAME READY STATUS RESTARTS AGE
# postgres 1/1 Running 0 15s
What just happened — full picture¶
Admin applied StorageClass (once, ever)
|
Developer submitted PVC (pvc.yaml)
|
Kubernetes told the provisioner (ebs.csi.aws.com)
|
Provisioner called AWS API → created real disk vol-0abc1234def5678
|
Provisioner created PV in Kubernetes pointing to that disk
|
Kubernetes bound PVC to PV
|
Developer deployed pod referencing the PVC
|
Pod is running, writing data to /var/lib/postgresql/data
which maps to vol-0abc1234def5678 in AWS
The developer only wrote pvc.yaml and pod.yaml. Nobody wrote a PV. Nobody logged into AWS. The StorageClass + provisioner handled everything in between.
Compare to the lab (no-provisioner)¶
You wrote PV manually ← provisioner would have done this
You wrote PVC ← same
You wrote pod ← same
StorageClass did nothing ← provisioner would have called AWS here
The lab skips the automatic part entirely. That's why StorageClass looked pointless — because in that example, it was.