Cloud Native 15 min read

Understanding Kubernetes CSI: Persistent Volume Lifecycle and Component Interaction

This article explains how Kubernetes CSI enables out‑of‑tree persistent storage by detailing the PV lifecycle—Create, Attach, Detach, Mount, Unmount, Delete—and describing the roles of core components such as apiserver, PVController, external‑provisioner, external‑attacher, and kubelet, with code examples and diagrams.

Cloud Native Technology Community
Cloud Native Technology Community
Cloud Native Technology Community
Understanding Kubernetes CSI: Persistent Volume Lifecycle and Component Interaction

The article introduces the Container Storage Interface (CSI) as a standard that allows cloud storage vendors to expose storage to Kubernetes workloads. It explains why Kubernetes moved from in‑tree volume handling to out‑of‑tree CSI plugins to decouple core code and enable flexible, vendor‑specific storage solutions.

Core Terminology

Term

Definition

CSI

Container Storage Interface.

CNI

Container Network Interface.

CRI

Container Runtime Interface.

PV

Persistent Volume.

PVC

Persistent Volume Claim.

StorageClass

Defined by provisioner to assemble volume parameters.

Volume

A unit of storage made available inside a container via CSI.

Block Volume

A volume that appears as a block device inside the container.

Mounted Volume

A volume mounted as a directory inside the container.

CO

Container Orchestration system communicating with plugins via CSI RPCs.

SP

Storage Provider, the vendor of a CSI plugin implementation.

RPC

Remote Procedure Call.

Node

A host where workloads run, identified by a node ID.

Plugin

A gRPC endpoint implementing CSI services.

Plugin Supervisor

Process governing the lifecycle of a Plugin, may be the CO.

Workload

The atomic unit of work scheduled by a CO, usually a container.

The article then outlines the PV creation workflow, showing how apiserver creates a Pod, PVController adds annotations, and the external‑provisioner calls the CSI CreateVolume RPC. It describes the handling of StorageClass.volumeBindingMode (WaitForFirstConsumer vs Immediate) and the subsequent steps involving external‑attacher , AttachDetachController , and kubelet for attaching, mounting, and reconciling volumes.

Component Interaction

Key components include:

kube-controller-manager : runs PVController and AttachDetachController to bind PVC/PV and manage attachment.

CSI‑plugin : implements CSI interfaces and is the core of storage logic.

node‑driver‑registrar : sidecar that registers the CSI plugin with kubelet.

external‑provisioner : creates and deletes volumes via CSI CreateVolume / DeleteVolume RPCs.

external‑attacher : handles volume attach/detach via CSI ControllerPublishVolume / ControllerUnpublishVolume .

external‑snapshotter : provides snapshot and backup capabilities.

external‑resizer : supports volume expansion.

kubelet : runs on each node, mounts/unmounts volumes using CSI NodePublishVolume / NodeUnpublishVolume .

cloud‑storage‑provider : vendor‑specific CSI implementation.

Communication between components uses Unix sockets for plugin‑controller interactions and gRPC (HTTP/2) for communication with the storage service.

Code Examples

External provisioner entry point:

main() {
    // Initialize controller that implements Volume create/delete
    csiProvisioner := ctrl.NewCSIProvisioner(...)
    provisionController := controller.NewProvisionController(...)
    run := func(ctx context.Context) {
        provisionController.Run(ctx)
    }
}

PVController initialization (simplified):

func NewController(p ControllerParameters) (*PersistentVolumeController, error) {
    controller := &PersistentVolumeController{
        volumes: newPersistentVolumeOrderedIndex(),
        claims: cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc),
        // ... other fields omitted for brevity
    }
    // Add event handlers for PV and PVC informers
    p.VolumeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{...})
    p.ClaimInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{...})
    return controller, nil
}

AttachDetach reconciler logic (simplified):

func (rc *reconciler) reconcile() {
    // Detach volumes that are no longer desired
    for _, attachedVolume := range rc.actualStateOfWorld.GetAttachedVolumes() {
        if !rc.desiredStateOfWorld.VolumeExists(attachedVolume.VolumeName, attachedVolume.NodeName) {
            rc.attacherDetacher.DetachVolume(attachedVolume.AttachedVolume, verifySafeToDetach, rc.actualStateOfWorld)
        }
    }
    // Attach desired volumes
    rc.attachDesiredVolumes()
}

Summary

By analyzing the creation, attachment, detachment, mounting, unmounting, and deletion processes of Persistent Volumes in Kubernetes, the article demonstrates how CSI implements these steps through a set of coordinated components and standard RPC interfaces, enabling cloud‑native storage extensibility and vendor‑specific features while keeping the core Kubernetes codebase clean.

Cloud NativeKubernetesStorageCSIK8sPersistent Volume
Cloud Native Technology Community
Written by

Cloud Native Technology Community

The Cloud Native Technology Community, part of the CNBPA Cloud Native Technology Practice Alliance, focuses on evangelizing cutting‑edge cloud‑native technologies and practical implementations. It shares in‑depth content, case studies, and event/meetup information on containers, Kubernetes, DevOps, Service Mesh, and other cloud‑native tech, along with updates from the CNBPA alliance.

0 followers
Reader feedback

How this landed with the community

login 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.