MTI TEK
  • Home
  • LLMs
  • Docker
  • Kubernetes
  • Java
  • All
Kubernetes | Volumes: Persistent Volumes, Persistent Volumes Claims, Storage Classes
  1. Notes
  2. Volumes
  3. Persistent Volumes (persistentvolumes|pv)
  4. Persistent Volumes Claims (persistentvolumeclaims|pvc)
  5. Configure a Pod to use a Persistent Volume Claim
  6. Storage Classes (storageclasses|sc)
  7. Mount ConfigMaps as volumes
  8. Volumes of type emptyDir

  1. Notes
    See these pages for more details about Volumes:
    https://kubernetes.io/docs/concepts/storage/volumes/
    https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/
  2. Volumes
    Volumes allow applications to store and use data from external storage systems.

    The data is managed by there systems: storage providers, storage plugins, Kubernetes.

    • Storage providers manage the backend system that allow reading/writing data.
      Cloud providers: Amazon EBS, Azure File/Disk, GCE
      On-premises providers: NFS.

    • Storage plugins allow kubernetes to integrate with the storage systems.

      CSI (Container Storage Interface) provides an open interface that allow storage providers to implement plugins that seamlessly integrate with kubernetes.

      You define the storage plugin by specifying the provisioner in the storage class or the Persistent Volume (i.e. provisioner: kubernetes.io/aws-ebs).

    • Kubernetes allows creating the objects (Persistent Volume, Persistent Volume Claim, Storage Class) that will let applications to read/write data stored in the backend system.

      • The Storage Class (SC) allows defining the class of storage to dynamically provision Persistent Volumes.
      • The Persistent Volume (PV) allows accessing the external data of the external storage system.
      • The Persistent Volum Claim (PVC) allows linking Pods to the Persistent Volumes (the Persistent Volumes will be mounted as volumes in the Pods).
      • The Persistent Volumes and Persistent Volume Claims must be created and bound before they can be used by a Pod.

    A Pod will interact with the data stored in the backend system through the storage plugin defined in the Persistent Volume:
    Pod <-> Persistent Volume Claim <-> Persistent Volume <-> Storage Plugin (i.e Amazon EBS plugin) <-> Storage System (i.e Amazon EBS)
  3. Persistent Volumes (persistentvolumes|pv)
    In the following example, we will use Docker Desktop Kubernetes cluster, and we will create a hostPath Persistent Volume. A hostPath Persistent Volume allows mounting files and directories on the Kubernetes Node. This is helpful for development and testing on a single-node cluster.

    Defines the Persistent Volume:
    $ vi persistentVolume1.yaml
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv1
      labels:
        type: local
    spec:
      storageClassName: hostpath
      accessModes:
        - ReadWriteOnce
      capacity:
        storage: 1Gi
      hostPath:
        path: /run/desktop/mnt/host/c/dev/dockerdesktop/k8s/storage/pv1/
    On Docker Desktop the path should be: /run/desktop/mnt/host/c/CUSTOME_STORAGE_PATH

    Persistent Volumes are defined in the core API group, so the apiVersion field can skip the api group.

    A Persistent Volume can be created in one access mode (you cannot combine RWO and RWM). The Persistent Volume Claim will mount/bind the Persistent Volume with the same access mode.

    The "spec.accessModes" field defines the access mode to the Persistent Volume (how it will be mounted): ReadWriteOnce (RWO), ReadWriteMany (RWM), ReadOnlyMany (ROM).

    • ReadWriteOnce (RWO):
      Defines a Persistent Volume with the read/write access mode and can be mounted/bound by one single Persistent Volume Claim. If multiple Persistent Volume Claims tries to use the Persistent Volume, then only one will be bound and the remaining PVCs will be in status pending.

    • ReadWriteMany (RWM):
      Defines a Persistent Volume with the read/write access mode and can be mounted/bound by multiple Persistent Volume Claims (NFS storage).

    • ReadOnlyMany (ROM):
      Defines a Persistent Volume with the read only access mode and can be mounted/bound by multiple Persistent Volume Claims.

    The "spec.storageClassName" field defines the name of the storage class.

    The "spec.capacity.storage" field defines size of the volume (must not be larger than the size of the backend storage).

    The "spec.persistentVolumeReclaimPolicy" field defines how the data will managed when the Persistent Volume Claim is deleted: Retain, Delete.

    • Retain:
      Deleting the Persistent Volume Claims won't delete the associated data stored in the external storage system.

    • Delete:
      Deleting the Persistent Volume Claims will also delete the associated data stored in the external storage system.

    To apply the file:
    $ kubectl apply -f persistentVolume1.yaml
    persistentvolume/pv1 created
    View the Persistent Volume:
    $ kubectl get pv
    NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
    pv1    1Gi        RWO            Retain           Available           hostpath                4s
    The "STATUS" of the Persistent Volume is "Available", which means it has not yet been bound to a Persistent Volume Claim.
  4. Persistent Volumes Claims (persistentvolumeclaims|pvc)
    Defines the Persistent Volume Claim:
    $ vi persistentVolumeClaim1.yaml
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: pvc1
    spec:
      storageClassName: hostpath
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi
    Kubernetes will try to match the Persistent Volume Claim to an existing Persistent Volume that satisfies the claim's requirements of the PVC.

    The spec field of the Persistent Volume Claim must match the .spec field of the Persistent Volume.

    If k8s finds a match, it binds the Persistent Volume Claim to the Persistent Volume. If there's no match, then if your cluster is configured to provision dynamically a Persistent Volume, Kubernetes will create a Persistent Volume and bind it to the Persistent Volume Claim.

    Note that the storage size of the Persistent Volume Claim can be set to a lower size than the one set for the Persistent Volume.

    To apply the file:
    $ kubectl apply -f persistentVolumeClaim1.yaml
    persistentvolumeclaim/pvc1 created
    View the Persistent Volume Claim (STATUS should show Bound):
    $ kubectl get pvc
    NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    pvc1   Bound    pv1      1Gi        RWO            hostpath       11s
    View the Persistent Volume (STATUS should show Bound):
    $ kubectl get pv
    NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE
    pv1    1Gi        RWO            Retain           Bound    default/pvc1   hostpath                74s
  5. Configure a Pod to use a Persistent Volume Claim
    To use a volume, the Pod needs to define two fields: volumes, volumeMounts
    The volumes field defines the volumes that can be used by the containers in the Pod (including the init-containers).
    The volumeMounts field allow the container to mount a specific volume in a specific path in that container.
    The container is not required to mount any volume defined by the volumes field.

    Defines the Pod:
    $ vi hello-busybox-pvc-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: hello-busybox-pvc
    spec:
      containers:
        - name: hello-busybox-pvc
          image: busybox:latest
          command: ['sh', '-c', 'sleep 300;']
          volumeMounts:
            - name: pvc1
              mountPath: /tmp/pvc1
      volumes:
        - name: pvc1
          persistentVolumeClaim:
            claimName: pvc1
    To apply the file:
    $ kubectl apply -f hello-busybox-pvc-pod.yaml
    pod/hello-busybox-pvc created
    To use the volume:
    $ kubectl exec -ti hello-busybox-pvc -- sh
    / # echo "hello pvc!" > /tmp/pvc1/file1.txt
    / # cat /tmp/pvc1/file1.txt
    hello pvc!
    You can validate the file directly in the Kubernetes node:
    $ docker run -it --rm --privileged --pid=host alpine nsenter -t 1 -m -u -n -i sh
    / # ls -1 /containers/services/docker/rootfs/mnt/mydata/pv1/
    file1.txt
  6. Storage Classes (storageclasses|sc)
    Storage classes allow providing custom settings to provision volumes dynamically.

    The settings of the storage classes include defining the type of the provisioner (the storage plugin) and providing specific configuration of the plugin (parameters).

    Once a storage class is defined and created (assuming that both the storage system and its plugin are configured properly), the Persistent Volume Claim can reference the storage class (by its name). The volume will be provisioned dynamically once the Persistent Volume Claim is created.

    To provision dynamically volumes, the storage class loop will watch the API server, and once a Persistent Volume Claim is created and referencing its name, it will create the corresponding Persistent Volume in Kubernetes and instruct the backend system to provision the volume.

    Storage classes are immutable, and once deployed they won't allow any changes.

    Storage classes are defined as resources in the "storage.k8s.io" API group.

    If you have installed Kubernetes on Docker Desktop, you will find a default storage class already created (hostpath):
    $ kubectl get sc
    NAME                 PROVISIONER          RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
    hostpath (default)   docker.io/hostpath   Delete          Immediate           false                  22h
    $ 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"
      name: hostpath
    provisioner: docker.io/hostpath
    reclaimPolicy: Delete
    volumeBindingMode: Immediate
  7. Mount ConfigMaps as volumes
    See details in Kubernetes ConfigMaps (configmaps|cm)

    Here's an example how to mount a ConfigMap as a volume:

    Create the ConfigMap:
    $ kubectl create configmap configmapkeyvalue --from-literal="key1=value1" --from-literal="key2=value2"
    configmap/configmapkeyvalue created
    Defines the Pod:
    $ vi hello-busybox-cm.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: hello-busybox-cm
    spec:
      containers:
        - name: hello-busybox-cm
          image: busybox:latest
          command: ['sh', '-c', 'sleep 300;']
          volumeMounts:
            - name: configmapkeyvalue
              mountPath: /tmp/configmapkeyvalue
      volumes:
        - name: configmapkeyvalue
          configMap:
            name: configmapkeyvalue
    To apply the file:
    $ kubectl apply -f hello-busybox-cm.yaml
    pod/hello-busybox-cm. created
    Check the mounted ConfigMap:
    $ kubectl exec -ti hello-busybox-cm -- sh -c 'ls -1 /tmp/configmapkeyvalue'
    key1
    key2
  8. Volumes of type emptyDir
    You can define a special volume of type emptyDir.
    The content of the volume will last for the life of the Pod (including when the Pod get restarted).
    The volume can be shared by all the containers of the Pod.

    Here's an example that uses an emptyDir volumes:
    $ vi hello-busybox-empty-dir-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: hello-busybox-empty-dir
    spec:
      containers:
        - name: hello-busybox-empty-dir
          image: busybox:latest
          command: ['sh', '-c', 'sleep 300;']
          volumeMounts:
            - name: empty-dir-volume
              mountPath: /tmp/emptyDirVolume
      volumes:
        - name: empty-dir-volume
          emptyDir: {}
    To apply the file:
    $ kubectl apply -f hello-busybox-empty-dir-pod.yaml
    pod/hello-busybox-empty-dir created
    To use the volume:
    $ kubectl exec -ti hello-busybox-empty-dir -- sh
    / # echo "hello empty dir volume!" > /tmp/emptyDirVolume/file1.txt
    / # ls -1 /tmp/emptyDirVolume/
    file1.txt
© 2025 mtitek
About