Cloud Native 14 min read

Why Kubernetes Introduced Volumes, PVs, and StorageClasses: A Deep Dive

This article explains the origins and purposes of Kubernetes Volume, PersistentVolume (PV), PersistentVolumeClaim (PVC), and StorageClass, compares them with Docker volumes, details static and dynamic provisioning, and walks through configuration examples, illustrating the storage architecture and workflow within a Kubernetes cluster.

Open Source Linux
Open Source Linux
Open Source Linux
Why Kubernetes Introduced Volumes, PVs, and StorageClasses: A Deep Dive

Origin of Volume, PV, PVC, and StorageClass

Why does Kubernetes introduce the concept of a Volume? To achieve data persistence that outlives the container lifecycle.

The answer is simple: data must survive container termination.

Before Kubernetes Volume, Docker provides two common volume types: volumes (managed under /var/lib/docker/volumes) and bind mounts (mount host directories into containers).

Although Docker has volumes, Kubernetes defines its own Volume not because it is fundamentally different, but because its lifecycle aligns with a Pod rather than a container. When a pod is deleted, the associated volume is cleaned up, unless the volume type (e.g., emptyDir) dictates otherwise. Kubernetes supports about 20 volume types.

In a pod’s YAML you can declare a volume, for example an NFS volume:

....
volumes:
  - name: static-nfs
    nfs:
      server: 12.18.17.240
      path: /nfs/data/static

If you use Ceph as the storage plugin, the YAML looks like:

volumes:
  - name: ceph-vol
    cephfs:
      monitors:
        - 12.18.17.241:6789
        - 12.18.17.242:6789
      user: admin
      secretRef:
        name: ceph-secret
      readOnly: true

After defining a volume, the next step is to understand PersistentVolume (PV) and PersistentVolumeClaim (PVC).

A PV describes a persistent storage resource.

A PV is usually pre‑created by an administrator:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 10.244.1.4
    path: "/nfs"
A PVC describes the desired size, access mode, etc.

Developers create a PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

The PVC must bind to a matching PV; the PV’s spec must satisfy the PVC’s requirements. Once bound, the pod can reference the PVC in its YAML.

apiVersion: v1
kind: Pod
metadata:
  labels:
    role: web
spec:
  containers:
  - name: web
    image: nginx
    ports:
    - name: web
      containerPort: 80
    volumeMounts:
    - name: nfs
      mountPath: "/usr/share/nginx/html"
  volumes:
  - name: nfs
    persistentVolumeClaim:
      claimName: nfs

Static provisioning requires administrators to pre‑allocate PVs, which can be inefficient. Dynamic provisioning automates PV creation based on a StorageClass template.

Static provisioning flow: administrators pre‑create PVs, users submit PVCs, the controller binds them, and pods use the storage.

Dynamic provisioning flow: users only create a PVC; the controller reads the StorageClass and creates a PV on‑the‑fly.

# Install NFS
yum -y install nfs-utils rpcbind
systemctl enable rpcbind nfs-server
echo "/nfs/data *(rw,no_root_squash,sync)" > /etc/exports

Deploy the NFS provisioner:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get","list","watch","create","delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get","list","watch","update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get","list","watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["watch","create","update","patch"]
  - apiGroups: [""]
    resources: ["services","endpoints"]
    verbs: ["get","create","list","watch","update"]
  - apiGroups: ["extensions"]
    resources: ["podsecuritypolicies"]
    resourceNames: ["nfs-provisioner"]
    verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: logging
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-client-provisioner
spec:
  selector:
    matchLabels:
      app: nfs-client-provisioner
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccount: nfs-provisioner
      containers:
      - name: nfs-client-provisioner
        image: quay.io/external_storage/nfs-client-provisioner:latest
        imagePullPolicy: IfNotPresent
        volumeMounts:
        - name: nfs-client
          mountPath: /persistentvolumes
        env:
        - name: PROVISIONER_NAME
          value: fuseim.pri/ifs
        - name: NFS_SERVER
          value: 12.18.7.20
        - name: NFS_PATH
          value: /nfs/data
      volumes:
      - name: nfs-client
        nfs:
          server: 12.18.7.20
          path: /nfs/data

Create a StorageClass for NFS:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-storage
provisioner: fuseim.pri/ifs
reclaimPolicy: Retain

When a pod defines a PVC that references this StorageClass, Kubernetes automatically creates the PV and binds it.

Key PV attributes:

Capacity : size of the storage.

AccessModes : ReadWriteOnce, ReadOnlyMany, or ReadWriteMany.

StorageClassName : the class used for dynamic provisioning.

Kubernetes Storage Architecture

PV Controller

: manages PV‑PVC binding and lifecycle. AD Controller: handles attach/detach of storage devices. Volume Manager: mounts/unmounts volumes and formats devices. Volume Plugins: implement the actual mount logic; can be In‑Tree (built‑in) or Out‑of‑Tree (CSI). Scheduler: schedules pods considering storage requirements.

Dynamic PV Interaction Flow

User creates a pod with a PVC. PV Controller watches the API server, creates a matching PV, and binds it to the PVC.

The Provision step creates the actual storage volume. Scheduler places the pod on a suitable node. AD Controller attaches the volume to the node. Volume Manager mounts the volume into the pod’s filesystem.

Container starts and uses the mounted volume.

Conclusion

The article covered the origins of Kubernetes Volume, PV, PVC, and StorageClass, compared them with Docker volumes, explained static and dynamic provisioning, and outlined the storage architecture and interaction workflow within a Kubernetes cluster.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

KubernetesvolumeNFSPersistentVolumeStorageClassDynamicProvisioningPVCStaticProvisioning
Open Source Linux
Written by

Open Source Linux

Focused on sharing Linux/Unix content, covering fundamentals, system development, network programming, automation/operations, cloud computing, and related professional knowledge.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.