CKA — Kubernetes Administrator
Tying the Three Storage Layers Together
Goal
You create the three layers, StorageClass, PV, and PVC, yourself to get a feel for the binding conditions, and you check how a StatefulSet handles volumes.
Why it matters
The reason a PVC is Pending is always one of three. The class name does not match, the access mode does not match, or the capacity is insufficient. To that you can add the case where there is no provisioner so dynamic provisioning does not happen. This lab environment has no real CSI driver, so you check these conditions one by one with static binding. You cannot write actual data, but what the exam asks is also mostly the object spec and the binding state.
It is also important to know in advance the fields that are useless to change later, such as allowVolumeExpansion. A PVC that came from a class created with false cannot be expanded even if you fix the class later.
Steps
- Create the namespace
cka-storageand the StorageClasscka-fast. provisionernfs.csi.k8s.io, reclaimPolicyDelete, volumeBindingModeWaitForFirstConsumer, allowVolumeExpansiontrue. - Create the StorageClass
cka-archive. provisionernfs.csi.k8s.io, reclaimPolicyRetain, volumeBindingModeImmediate. Then, oncka-fast, add the annotationstorageclass.kubernetes.io/is-default-class: "true"to make it the default class. There must be exactly one default class. - Create the PersistentVolume
cka-pv-1. capacity 2Gi, accessModes[ReadWriteOnce], persistentVolumeReclaimPolicyRetain, storageClassNamecka-manual, volume source hostPath/mnt/cka-data. - In
cka-storage, create the PVCcka-pvc-1. Request 1Gi, accessModes[ReadWriteOnce], storageClassNamecka-manual. It must be Bound tocka-pv-1. - Create the PersistentVolume
cka-pv-rwx. capacity 5Gi, accessModes[ReadWriteMany], storageClassNamecka-shared, volume source NFS (server10.0.0.109, path/volume1/k8s). Then create the PVCcka-pvc-rwx(5Gi, RWX,cka-shared) and bind it. - Create the headless Service
db-headless(clusterIP None, port 5432, selectorapp=db) and the StatefulSetdb. serviceNamedb-headless, 2 replicas, imagenginx:1.27, podManagementPolicyParallel, volumeClaimTemplates with namedata(accessModes[ReadWriteOnce], storageClassNamecka-fast, 1Gi), persistentVolumeClaimRetentionPolicy with whenDeletedDelete/ whenScaledRetain. The PVCsdata-db-0anddata-db-1must be created automatically. - In
cka-storage, create the Podvol-demo(imagenginx:1.27). The volumecacheis an emptyDir with mediumMemoryand sizeLimit128Mi, mounted at/cache. The volumehostlogsis a hostPath with path/var/logand typeDirectory, mounted read-only at/hostlogs. - Create the PersistentVolume
cka-pv-static(capacity 1Gi, accessModes[ReadWriteOnce], no storageClassName at all, hostPath/mnt/cka-static), and create the PVCcka-pvc-static(1Gi, RWO) with storageClassName explicitly set to an empty string, and bind it.
Reference
- The PVCs in step 6 stay Pending because there is no provisioner. This is normal, and grading looks at whether the PVC objects were created.
- If you look at the STATUS and VOLUME columns together with
kubectl get pvc -n cka-storage, the binding state is clear at a glance. - Common mistake 1: writing the PV's reclaim policy field as
reclaimPolicy. On a PV it ispersistentVolumeReclaimPolicy. - Common mistake 2: omitting
storageClassNamein step 8. If you omit it, the default classcka-fastis injected and the binding fails.
Create a StorageClass
Create the namespace cka-storage and the StorageClass cka-fast. provisioner nfs.csi.k8s.io, reclaimPolicy Delete, volumeBindingMode WaitForFirstConsumer, allowVolumeExpansion true.
provisioner is a string, and the object is created even if the driver does not actually exist. reclaimPolicy, volumeBindingMode, and allowVolumeExpansion are top-level fields of the StorageClass, not inside parameters.
A second class and designating the default class
Create the StorageClass cka-archive. provisioner nfs.csi.k8s.io, reclaimPolicy Retain, volumeBindingMode Immediate. Then, on cka-fast, add the annotation storageclass.kubernetes.io/is-default-class: "true" to make it the default class. There must be exactly one default class.
The default class is set with an annotation. If there is more than one default, you cannot predict which one will be used, so there must be exactly one.
Create a PersistentVolume yourself
Create the PersistentVolume cka-pv-1. capacity 2Gi, accessModes [ReadWriteOnce], persistentVolumeReclaimPolicy Retain, storageClassName cka-manual, volume source hostPath /mnt/cka-data.
A PV is cluster-scoped. Note that the name of the reclaim policy field is different from the one in a StorageClass.
Check static binding
In cka-storage, create the PVC cka-pvc-1. Request 1Gi, accessModes [ReadWriteOnce], storageClassName cka-manual. It must be Bound to cka-pv-1.
The binding conditions are three: the class name matches, the access modes are included, and the capacity is sufficient. If even one is off, it quietly stays Pending.
Create an RWX volume
Create the PersistentVolume cka-pv-rwx. capacity 5Gi, accessModes [ReadWriteMany], storageClassName cka-shared, volume source NFS (server 10.0.0.109, path /volume1/k8s). Then create the PVC cka-pvc-rwx (5Gi, RWX, cka-shared) and bind it.
The accessModes of the PV and the PVC must match for them to bind. The NFS volume source requires two fields, server and path.
Create PVCs automatically with volumeClaimTemplates
Create the headless Service db-headless (clusterIP None, port 5432, selector app=db) and the StatefulSet db. serviceName db-headless, 2 replicas, image nginx:1.27, podManagementPolicy Parallel, volumeClaimTemplates with name data (accessModes [ReadWriteOnce], storageClassName cka-fast, 1Gi), persistentVolumeClaimRetentionPolicy with whenDeleted Delete / whenScaled Retain. The PVCs data-db-0 and data-db-1 must be created automatically.
The PVC name is formed by combining the template name and the Pod name. You need to change the default behavior in which the next Pod is not created until the first Pod becomes Ready.
emptyDir and hostPath mounts
In cka-storage, create the Pod vol-demo (image nginx:1.27). The volume cache is an emptyDir with medium Memory and sizeLimit 128Mi, mounted at /cache. The volume hostlogs is a hostPath with path /var/log and type Directory, mounted read-only at /hostlogs.
Volume definitions go in spec.volumes, and mounts go in the container's volumeMounts. A memory-backed emptyDir is specified with the medium field.
Putting it together: reject the default StorageClass
Create the PersistentVolume cka-pv-static (capacity 1Gi, accessModes [ReadWriteOnce], no storageClassName at all, hostPath /mnt/cka-static), and create the PVC cka-pvc-static (1Gi, RWO) with storageClassName explicitly set to an empty string, and bind it.
Omitting a field is different from specifying it explicitly as an empty string. The former gets the default injected, and the latter is a refusal.