Skip to content

Room 53

CSI DRIVER DEVELOPMENT


The Storage Architect

  • The Container Storage Interface (CSI) is the standard for exposing storage to Kubernetes.
  • Every PersistentVolume you use goes through a CSI driver. Now you’ll build one from scratch.

πŸ“œ Mission Briefing

  1. Understand the CSI architecture: Controller, Node, Identity services.
  2. Deploy the CSI hostpath driver components into your Kind cluster.
  3. Create a StorageClass that uses your CSI driver.
  4. Provision a PersistentVolumeClaim and mount it in a Pod.
  5. Write data to the volume and verify persistence across pod restarts.
  6. Inspect CSI driver registration and volume attachments.
  7. The password is revealed in the PV metadata annotation: csi53vol.

Key Commands
Command Purpose
kubectl get csidrivers List registered CSI drivers
kubectl get csinodes CSI node info
kubectl get sc List StorageClasses
kubectl get pv,pvc List volumes and claims
kubectl get volumeattachments Volume attachment status
kubectl describe pvc <name> PVC provisioning details

CSI Architecture Overview
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Identity   β”‚    β”‚  Controller  β”‚    β”‚     Node     β”‚
β”‚   Service    β”‚    β”‚   Service    β”‚    β”‚   Service    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ GetPluginInfoβ”‚    β”‚ CreateVolume β”‚    β”‚ NodeStageVol β”‚
β”‚ GetPluginCap β”‚    β”‚ DeleteVolume β”‚    β”‚ NodePublishV β”‚
β”‚ Probe        β”‚    β”‚ ControllerPubβ”‚    β”‚ NodeGetCap   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚ ValidateVolCaβ”‚    β”‚ NodeGetInfo  β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Deploy the CSI HostPath Driver
# csi-hostpath-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: csi-hostpath-sc
  annotations:
    password: "csi53vol"
provisioner: hostpath.csi.k8s.io
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
# csi-hostpath-plugin.yaml - Simplified CSI HostPath DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: csi-hostpath-node
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: csi-hostpath
  template:
    metadata:
      labels:
        app: csi-hostpath
    spec:
      containers:
        - name: node-driver-registrar
          image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.9.0
          args:
            - "--csi-address=/csi/csi.sock"
            - "--kubelet-registration-path=/var/lib/kubelet/plugins/hostpath.csi.k8s.io/csi.sock"
          volumeMounts:
            - name: plugin-dir
              mountPath: /csi
            - name: registration-dir
              mountPath: /registration
        - name: hostpath
          image: registry.k8s.io/sig-storage/hostpathplugin:v1.12.0
          args:
            - "--v=5"
            - "--endpoint=unix:///csi/csi.sock"
            - "--nodeid=$(KUBE_NODE_NAME)"
          env:
            - name: KUBE_NODE_NAME
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: spec.nodeName
          securityContext:
            privileged: true
          volumeMounts:
            - name: plugin-dir
              mountPath: /csi
            - name: pods-mount-dir
              mountPath: /var/lib/kubelet/pods
              mountPropagation: Bidirectional
      volumes:
        - name: plugin-dir
          hostPath:
            path: /var/lib/kubelet/plugins/hostpath.csi.k8s.io
            type: DirectoryOrCreate
        - name: pods-mount-dir
          hostPath:
            path: /var/lib/kubelet/pods
            type: Directory
        - name: registration-dir
          hostPath:
            path: /var/lib/kubelet/plugins_registry
            type: Directory

Use the CSI Volume
# test-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: csi-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: csi-hostpath-sc
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: csi-app
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "echo 'CSI works!' > /data/test.txt && cat /data/test.txt && sleep 3600"]
      volumeMounts:
        - name: my-volume
          mountPath: /data
  volumes:
    - name: my-volume
      persistentVolumeClaim:
        claimName: csi-pvc

Inspect CSI Components
# Check CSI driver registration
kubectl get csidrivers
# NAME                  ATTACHREQUIRED   PODINFOONMOUNT   STORAGECAPACITY
# hostpath.csi.k8s.io   true             true             false

# Check node plugin status
kubectl get csinodes -o yaml

# Check volume was provisioned
kubectl get pv
# NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM
# pvc-xxx    1Gi        RWO            Delete           Bound    default/csi-pvc

# See the StorageClass annotation for the password
kubectl get sc csi-hostpath-sc -o jsonpath='{.metadata.annotations.password}'
# Output: csi53vol

# Verify data persistence
kubectl delete pod csi-app
kubectl apply -f test-pvc.yaml  # Re-create pod with same PVC
kubectl exec csi-app -- cat /data/test.txt
# Output: CSI works!

πŸ’‘ Hints

  • CSI drivers have 3 gRPC services: Identity, Controller, and Node.
  • The Controller service handles provisioning/deleting volumes at the cluster level.
  • The Node service handles mounting/unmounting volumes on specific nodes.
  • volumeBindingMode: WaitForFirstConsumer delays provisioning until a Pod needs the volume.
  • The password is in the StorageClass annotation - use kubectl get sc -o yaml.

πŸ”‘ Password

Check the StorageClass annotation: kubectl get sc csi-hostpath-sc -o jsonpath='{.metadata.annotations.password}'