Note_Tech

All technological notes.


Project maintained by simonangel-fong Hosted on GitHub Pages — Theme by mattgraham

Kubernetes - Storage: Dynamic Volume Provisioning & StorageClass

Back


Dynamic Volume Provisioning

pic


StorageClass Object

Binding Mode


Declarative Manifest

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: google-storage
provisioner: kubernetes.io/gce-pd
volumeBindingMode: WaitForFirstConsumer # Binding Mode
parameters:
  type: pd-standard
  replication-type: none
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: google-storage # refer storage class
  resources:
    requests:
      storage: 500Mi
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - image: alpine
      name: alpine
      command: ["/bin/sh", "-c"]
      args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"]
      volumeMounts:
        - mountPath: /opt
          name: data-volume

  volumes:
    - name: data-volume
      persistentVolumeClaim:
        claimName: myclaim # refer pvc

Imperative Commands

Command Description
kubectl get storageclass / kubectl get sc List all StorageClasses in the cluster.
kubectl get sc <name> Show basic info for a specific StorageClass.
kubectl describe sc <name> Show detailed configuration, reclaim policy, parameters, and provisioner details.
kubectl delete sc <name> Delete a specific StorageClass.

Lab: StorageClass

kubectl get sc
# NAME                 PROVISIONER          RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
# hostpath (default)   docker.io/hostpath   Delete          Immediate           false                  49d

kubectl describe sc hostpath
# Name:            hostpath
# IsDefaultClass:  Yes
# Annotations:     kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"},"name":"hostpath"},"provisioner":"docker.io/hostpath","reclaimPolicy":"Delete","volumeBindingMode":"Immediate"}
# ,storageclass.kubernetes.io/is-default-class=true
# Provisioner:           docker.io/hostpath
# Parameters:            <none>
# AllowVolumeExpansion:  <unset>
# MountOptions:          <none>
# ReclaimPolicy:         Delete
# VolumeBindingMode:     Immediate
# Events:                <none>

kubectl get sc hostpath -o yaml
# apiVersion: storage.k8s.io/v1
# kind: StorageClass
# metadata:
#   annotations:
#     kubectl.kubernetes.io/last-applied-configuration: |
#       {"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"},"name":"hostpath"},"provisioner":"docker.io/hostpath","reclaimPolicy":"Delete","volumeBindingMode":"Immediate"}
#     storageclass.kubernetes.io/is-default-class: "true"
#   creationTimestamp: "2025-11-05T18:00:48Z"
#   name: hostpath
#   resourceVersion: "402"
#   uid: 1f503168-2cfb-4064-a51e-66605b6bdd4a
# provisioner: docker.io/hostpath
# reclaimPolicy: Delete
# volumeBindingMode: Immediate

Lab: Create PVC with Default SC

# demo-pvc-default-sc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: demo-pvc-default-sc
spec:
  # use default sc by omitting sc
  resources:
    requests:
      storage: 1Gi
  accessModes:
    - ReadWriteOnce
kubectl apply -f demo-pvc-default-sc.yaml
# persistentvolumeclaim/demo-pvc-default-sc created

# confirm: status=bound; sc=hostpath
kubectl get pvc
# NAME                  STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
# demo-pvc-default-sc   Bound    pvc-db934e53-321b-473c-8cd2-1dcf54ec288c   10Gi       RWO            hostpath       <unset>                 5s

# confirm pv created
kubectl get pv
# NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                         STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
# pvc-db934e53-321b-473c-8cd2-1dcf54ec288c   10Gi       RWO            Delete           Bound    default/demo-pvc-default-sc   hostpath       <unset>                          18s
# demo-pvc-default-sc-pod-writer.yaml
apiVersion: v1
kind: Pod
metadata:
  name: demo-pvc-default-sc-pod-writer
spec:
  volumes:
    - name: share-data
      persistentVolumeClaim:
        claimName: demo-pvc-default-sc
  containers:
    - name: writer
      image: busybox
      command:
        - sh
        - -c
        - |
          while true; do
            echo "<h1>Hello from the data writer container!$(date)</h1>" > /share/index.html;
            sleep 1;
          done
      volumeMounts:
        - name: share-data
          mountPath: /share
kubectl apply -f demo-pvc-default-sc-pod-writer.yaml
# pod/demo-pvc-default-sc-pod-writer created

# confirm pod status: running
kubectl get pvc
# NAME                             READY   STATUS    RESTARTS   AGE
# demo-pvc-default-sc-pod-writer   1/1     Running   0          22s

kubectl describe pod demo-pvc-default-sc-pod-writer
# Volumes:
#   share-data:
#     Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
#     ClaimName:  demo-pvc-default-sc
#     ReadOnly:   false

Lab: Expand PVC size 1G to 10G

# edit spec.resources.requests.storage=10Gi
kubectl apply -f demo-pvc-default-sc.yaml
# error when patching "demo-pvc-default-sc.yaml": persistentvolumeclaims "demo-pvc-default-sc" is forbidden: only dynamically provisioned pvc can be resized and the storageclass that provisions the pvc must support resize

kubectl delete pod demo-pvc-default-sc-pod-writer
# pod "demo-pvc-default-sc-pod-writer" deleted from default namespace

kubectl replace --force -f demo-pvc-default-sc.yaml
# persistentvolumeclaim "demo-pvc-default-sc" deleted from default namespace
# persistentvolumeclaim/demo-pvc-default-sc replaced

kubectl get pvc
# NAME                  STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
# demo-pvc-default-sc   Bound    pvc-4dd47494-6dfe-4cb0-8662-c63dbaf2bb31   10Gi       RWO            hostpath       <unset>                 17s

kubectl apply -f demo-pvc-default-sc-pod-writer.yaml
# pod/demo-pvc-default-sc-pod-writer created

kubectl get pod
# NAME                             READY   STATUS    RESTARTS   AGE
# demo-pvc-default-sc-pod-writer   1/1     Running   0          15s


# shrink size: # edit spec.resources.requests.storage=0.5Gi
kubectl apply -f demo-pvc-default-sc.yaml
# The PersistentVolumeClaim "demo-pvc-default-sc" is invalid: spec.resources.requests.storage: Forbidden: field can not be less than status.capacity

kubectl delete pod demo-pvc-default-sc-pod-writer
# pod "demo-pvc-default-sc-pod-writer" deleted from default namespace

kubectl replace --force -f demo-pvc-default-sc.yaml
# persistentvolumeclaim "demo-pvc-default-sc" deleted from default namespace
# persistentvolumeclaim/demo-pvc-default-sc replaced

kubectl get pvc
# NAME                  STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
# demo-pvc-default-sc   Bound    pvc-69d4dffc-94ed-47bd-b50e-3680a0a64150   512Mi      RWO            hostpath       <unset>                 15s

kubectl apply -f demo-pvc-default-sc-pod-writer.yaml
# pod/demo-pvc-default-sc-pod-writer created

kubectl get pod
# NAME                             READY   STATUS    RESTARTS   AGE
# demo-pvc-default-sc-pod-writer   1/1     Running   0          16s