StorageClass

4 minutes read

In this exercise, you’ll create the simplest possible StorageClass, based on hostPath, for your kubeadm cluster and use it to provision storage.

  1. Check the existing StorageClasses

  2. Create a hostPath-based StorageClass

  3. Create a PersistentVolume manually

  4. Create a PersistentVolumeClaim using your StorageClass

  5. Create a Pod that uses the PVC and write some data to the volume

  6. Verify the data persists by deleting and recreating the Pod

  7. Clean up all resources

https://kubernetes.io/docs/concepts/storage/storage-classes/

https://kubernetes.io/docs/concepts/storage/persistent-volumes/

  1. Check the existing StorageClasses
kubectl get storageclass
kubectl get sc -o wide

You should see no StorageClasses:

No resources found
  1. Create a hostPath-based StorageClass

Since we don’t have a dynamic provisioner, we’ll create a simple StorageClass that we can use with manually created PersistentVolumes:

cat <<EOF | kubectl apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: manual-hostpath
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: false
reclaimPolicy: Delete
EOF

Verify the StorageClass was created:

kubectl get storageclass manual-hostpath
  1. Create a PersistentVolume manually

Let’s create the following PersistentVolume:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
  name: manual-pv-001
spec:
  capacity:
    storage: 1Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: manual-hostpath
  hostPath:
    path: /tmp/k8s-storage
    type: DirectoryOrCreate
EOF

Check the PV status:

kubectl get pv manual-pv-001

The PV should be in “Available” status.

  1. Create a PersistentVolumeClaim using your StorageClass
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: manual-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: manual-hostpath
  resources:
    requests:
      storage: 1Gi
EOF

Check the PVC status:

kubectl get pvc manual-pvc
  1. Create a Pod that uses the PVC and write some data to the volume
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: demo
spec:
  containers:
  - name: demo
    image: alpine:3.22
    command:
      - sleep
      - "3600"
    volumeMounts:
    - name: storage-volume
      mountPath: /data
  volumes:
  - name: storage-volume
    persistentVolumeClaim:
      claimName: manual-pvc
EOF

Wait for the Pod to be running:

kubectl wait --for=condition=Ready pod/demo --timeout=60s

Check that the PVC and the PV are now bound:

kubectl get pvc manual-pvc
kubectl get pv manual-pv-001

Write some data to the volume:

kubectl exec demo -- sh -c "echo Hello from manual hostPath storage > /data/test.txt"
kubectl exec demo -- sh -c "date >> /data/test.txt"
kubectl exec demo -- sh -c "hostname >> /data/test.txt"
kubectl exec demo -- cat /data/test.txt

Check which Node the Pod is running on:

kubectl get pod demo -o wide
  1. Verify the data persists by deleting and recreating the Pod

First, delete the demo Pod.

kubectl delete pod demo

Next, recreate the Pod using the same PVC.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: demo2
spec:
  containers:
  - name: demo
    image: alpine:3.22
    command:
      - sleep
      - "3600"
    volumeMounts:
    - name: storage-volume
      mountPath: /data
  volumes:
  - name: storage-volume
    persistentVolumeClaim:
      claimName: manual-pvc
EOF

Wait for the new Pod to be ready and verify the data persists:

kubectl wait --for=condition=Ready pod/demo2 --timeout=60s
kubectl exec demo2 -- cat /data/test.txt

You should see the same data that was written by the first Pod.

  1. Clean up all resources
kubectl delete pod demo2
kubectl delete pvc manual-pvc
kubectl delete pv manual-pv-001
kubectl delete storageclass manual-hostpath