Welcome to from-docker-to-kubernetes

Storage

Understanding Kubernetes storage concepts, volumes, and persistence

Kubernetes Storage

Kubernetes provides various storage options to manage data persistence for containers and pods. Storage management in Kubernetes is designed to abstract the underlying storage infrastructure, allowing applications to consume storage resources without being tightly coupled to the specific storage technology.

The Kubernetes storage architecture is built around the concept of volumes, which provide a way for containers to access and persist data beyond their lifecycle. This architecture addresses key challenges in containerized environments, including data persistence, sharing data between containers, and managing the lifecycle of storage resources.

Storage Concepts

Volumes

  • Basic storage abstraction in Kubernetes
  • Pod-level storage that exists for the pod's lifetime
  • Multiple types available (emptyDir, hostPath, configMap, secret, etc.)
  • Lifecycle tied to pod (created when pod is created, deleted when pod is deleted)
  • Can be shared between containers in the same pod
  • Supports various backend storage systems
  • Used for both ephemeral and persistent storage needs
  • Defined in the pod specification

Persistent Volumes (PV)

  • Cluster-level storage resource
  • Independent of pods and their lifecycle
  • Admin provisioned or dynamically created via StorageClass
  • Reusable resources that can be claimed and reclaimed
  • Defined by capacity, access modes, storage class, reclaim policy
  • Represents actual physical storage in the infrastructure
  • Supports different volume plugins (AWS EBS, Azure Disk, NFS, etc.)
  • Can exist before and after pods that use them

Persistent Volume Claims (PVC)

  • Storage requests made by users
  • User abstraction that hides storage implementation details
  • Binds to PV that meets the requirements
  • Pod storage interface that pods use to request storage
  • Defined by storage capacity, access modes, and storage class
  • Acts as an intermediary between pods and PVs
  • Can request specific storage class or amount of storage
  • Enables separation of concerns between users and administrators

Volume Types

EmptyDir

An empty directory that's created when a Pod is assigned to a node and exists as long as the Pod runs on that node. When a Pod is removed, the data in the emptyDir is deleted permanently.

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: test-container
    image: nginx
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: 
      medium: "" # Memory or disk-backed (use "Memory" for tmpfs)
      sizeLimit: 1Gi # Optional size limit (Kubernetes 1.18+)

Use cases for emptyDir:

  • Scratch space for temporary files
  • Checkpoint data for long computations
  • Sharing files between containers in a pod
  • Cache space for application data
  • Holding data processed by one container and used by another

HostPath

Mounts a file or directory from the host node's filesystem into your Pod. This type of volume presents significant security risks and should be used with caution.

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: test-container
    image: nginx
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
      readOnly: true # Good security practice for host mounts
  volumes:
  - name: test-volume
    hostPath:
      path: /data # Path on the host
      type: Directory # Type checks to perform before mounting
      # Other types: DirectoryOrCreate, File, FileOrCreate, Socket, CharDevice, BlockDevice

Important considerations for hostPath:

  • Data is not portable between nodes
  • Pods using the same path on different nodes will see different data
  • Security risk due to potential access to host filesystem
  • Often used for system-level pods that need access to host resources
  • Not suitable for most applications; prefer PVs for persistent data

Persistent Volumes

PV Definition

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-storage
  labels:
    type: local
    environment: production
spec:
  capacity:
    storage: 10Gi # The size of the volume
  volumeMode: Filesystem # Filesystem or Block
  accessModes:
    - ReadWriteOnce # RWO: Only one node can mount as read-write
    # Other options: ReadOnlyMany (ROX), ReadWriteMany (RWX)
  persistentVolumeReclaimPolicy: Retain # What happens when PVC is deleted
    # Options: Retain, Delete, Recycle (deprecated)
  storageClassName: standard # The storage class this PV belongs to
  mountOptions:
    - hard
    - nfsvers=4.1
  # hostPath example (local storage, generally for testing)
  hostPath:
    path: /data
  
  # Other backend examples:
  # AWS EBS
  # awsElasticBlockStore:
  #   volumeID: <volume-id>
  #   fsType: ext4
  
  # Azure Disk
  # azureDisk:
  #   diskName: myAKSDisk
  #   diskURI: /subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Compute/disks/<disk-name>
  #   kind: Managed
  #   fsType: ext4

PVC Definition

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-claim
  namespace: default
  annotations:
    volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/aws-ebs
spec:
  accessModes:
    - ReadWriteOnce # Must be compatible with the PV's access modes
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi # Requested storage capacity
  storageClassName: standard # Must match the PV's storage class
  # Optional selector to bind to specific PVs
  selector:
    matchLabels:
      environment: production

Using PVC in Pod

apiVersion: v1
kind: Pod
metadata:
  name: web-server
spec:
  containers:
  - name: web
    image: nginx
    volumeMounts:
    - mountPath: "/usr/share/nginx/html"
      name: web-data
      readOnly: false
    ports:
    - containerPort: 80
      name: "http-server"
    resources:
      limits:
        memory: "128Mi"
        cpu: "500m"
  volumes:
  - name: web-data
    persistentVolumeClaim:
      claimName: pvc-claim
  securityContext:
    fsGroup: 1000 # Set group ownership for mounted volumes

PV and PVC Lifecycle

  1. Provisioning: Static (admin creates PVs) or Dynamic (via StorageClass)
  2. Binding: PVC is bound to a suitable PV
  3. Using: Pod uses the PVC as a volume
  4. Reclaiming: When PVC is deleted, PV is reclaimed according to policy
    • Retain: PV and data are kept (admin must clean up manually)
    • Delete: PV and associated storage are deleted
    • Recycle: Basic scrub (rm -rf) before reuse (deprecated)

Storage Classes

StorageClasses enable dynamic provisioning of Persistent Volumes. They abstract the underlying storage provider details and allow administrators to define different "classes" of storage with varying performance characteristics, reclaim policies, and other parameters.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
  annotations:
    storageclass.kubernetes.io/is-default-class: "false"
provisioner: kubernetes.io/aws-ebs # The volume plugin used for provisioning
parameters:
  type: gp2 # Amazon EBS volume type
  fsType: ext4 # Filesystem type to be created
  encrypted: "true" # Enable encryption
  iopsPerGB: "10" # IOPS per GB for io1 volumes
reclaimPolicy: Retain # What happens to PVs when PVC is deleted
allowVolumeExpansion: true # Allow PVCs to be expanded after creation
volumeBindingMode: WaitForFirstConsumer # Delay binding until pod is created
# Immediate is the alternative - bind immediately when PVC is created
mountOptions:
  - debug

Different provisioner examples:

# GCE Persistent Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard-gce
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-standard
  replication-type: none

# Azure Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-premium
provisioner: kubernetes.io/azure-disk
parameters:
  storageaccounttype: Premium_LRS
  kind: Managed

# Local Storage
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

Access Modes

Volume Snapshots

Volume snapshots provide a way to create point-in-time copies of persistent volumes. This feature is particularly useful for backup, disaster recovery, and creating copies of data for testing or development environments.

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: new-snapshot
  namespace: default
  labels:
    environment: production
    app: database
spec:
  volumeSnapshotClassName: csi-hostpath-snapclass
  source:
    persistentVolumeClaimName: pvc-claim
  # Alternative: use volumeSnapshotContentName to pre-provisioned snapshot
  # volumeSnapshotContentName: existing-snapshot-content

Volume Snapshot Class

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-hostpath-snapclass
driver: hostpath.csi.k8s.io
deletionPolicy: Delete  # Or Retain
parameters:
  # Driver-specific parameters
  csi.storage.k8s.io/snapshotter-secret-name: snapshotter-secret
  csi.storage.k8s.io/snapshotter-secret-namespace: default

Restoring from a Snapshot

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-from-snapshot
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard
  dataSource:
    name: new-snapshot
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io

Snapshot operations require:

  1. The VolumeSnapshot CRD (CustomResourceDefinition)
  2. The snapshot controller
  3. A CSI driver that supports snapshots
  4. VolumeSnapshotClass configuration

Dynamic Provisioning

Storage Class

  • Defines storage type and characteristics
  • Enables automatic provisioning of storage resources
  • Supports different storage providers (AWS, GCP, Azure, on-premises)
  • Configures quality of service parameters (IOPS, throughput)
  • Can be set as the default for the cluster
  • Defines volume binding mode (immediate or wait for consumer)
  • Configures reclaim policy for created PVs
  • Enables or disables volume expansion

Automatic PV Creation

  • Triggered based on PVC request
  • Uses StorageClass to determine how to provision storage
  • Provider-specific parameters control the resulting storage
  • Handles resource management automatically
  • Creates appropriately sized volumes based on PVC request
  • Labels and annotations from StorageClass are applied to PV
  • Example flow:
    1. User creates PVC with storageClassName
    2. Dynamic provisioner watches for new PVCs
    3. Provisioner creates actual storage in infrastructure
    4. Provisioner creates PV object in Kubernetes
    5. Kubernetes binds PVC to newly created PV
    6. Pod can now use the PVC

Example Dynamic Provisioning

# First, define a StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-storage
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  iopsPerGB: "3000"
  throughput: "125"
reclaimPolicy: Delete
allowVolumeExpansion: true

# Then, create a PVC that uses the StorageClass
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dynamic-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: fast-storage

# Finally, use the PVC in a Pod
apiVersion: v1
kind: Pod
metadata:
  name: web-app
spec:
  containers:
  - name: web-app
    image: nginx
    volumeMounts:
    - mountPath: "/data"
      name: data-volume
  volumes:
  - name: data-volume
    persistentVolumeClaim:
      claimName: dynamic-pvc

Best Practices

Common Storage Providers

Cloud Providers

  • AWS EBS (Elastic Block Store)
    • Block storage for AWS EC2 instances
    • Provides ReadWriteOnce access mode
    • Multiple volume types (gp3, io2, st1, sc1)
    • Supports snapshots and encryption
    • Region-specific and availability zone bound
    • Example:
      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: ebs-sc
      provisioner: kubernetes.io/aws-ebs
      parameters:
        type: gp3
        encrypted: "true"
      
  • Azure Disk
    • Block storage for Azure
    • Provides ReadWriteOnce access
    • Multiple SKUs (Standard_LRS, Premium_LRS, UltraSSD_LRS)
    • Supports managed and unmanaged disks
    • Example:
      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: azure-disk
      provisioner: kubernetes.io/azure-disk
      parameters:
        storageaccounttype: Premium_LRS
        kind: Managed
      
  • Google Persistent Disk
    • Block storage for GCP
    • pd-standard (HDD) and pd-ssd options
    • Regional or zonal deployment
    • Automatic encryption
    • Example:
      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: gce-pd
      provisioner: kubernetes.io/gce-pd
      parameters:
        type: pd-ssd
        replication-type: none
      
  • OpenStack Cinder
    • Block storage for OpenStack
    • Various volume types based on configuration
    • Integration with OpenStack authentication
    • Example:
      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: cinder-sc
      provisioner: kubernetes.io/cinder
      parameters:
        type: fast
        availability: nova
      

On-Premise

  • NFS (Network File System)
    • Provides ReadWriteMany access
    • Good for shared file access
    • Widely supported in enterprise environments
    • Simple to set up and manage
    • Example:
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: nfs-pv
      spec:
        capacity:
          storage: 10Gi
        accessModes:
          - ReadWriteMany
        nfs:
          server: nfs-server.example.com
          path: "/shared"
      
  • iSCSI (Internet Small Computer Systems Interface)
    • Block storage protocol
    • Widely supported in enterprise storage
    • Provides ReadWriteOnce access
    • Requires iSCSI initiator configuration
    • Example:
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: iscsi-pv
      spec:
        capacity:
          storage: 20Gi
        accessModes:
          - ReadWriteOnce
        iscsi:
          targetPortal: 10.0.0.1:3260
          iqn: iqn.2000-01.com.example:storage.kube.sys1.xyz
          lun: 0
          fsType: ext4
          readOnly: false
      
  • Ceph
    • Distributed storage system
    • Provides block (RBD), file (CephFS), and object storage
    • Highly scalable and resilient
    • Supports ReadWriteMany with CephFS
    • Example RBD (Rados Block Device):
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: ceph-pv
      spec:
        capacity:
          storage: 10Gi
        accessModes:
          - ReadWriteOnce
        rbd:
          monitors:
            - 10.16.154.78:6789
            - 10.16.154.82:6789
          pool: kube
          image: mysql-persistent
          user: admin
          secretRef:
            name: ceph-secret
          fsType: ext4
          readOnly: false
      
  • GlusterFS
    • Distributed file system
    • Provides ReadWriteMany access
    • Scales horizontally
    • Good for large file storage
    • Example:
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: glusterfs-pv
      spec:
        capacity:
          storage: 10Gi
        accessModes:
          - ReadWriteMany
        glusterfs:
          endpoints: "glusterfs-cluster"
          path: "volume1"
          readOnly: false
      

CSI Drivers (Container Storage Interface)

  • Standardized interface for storage providers
  • Enables third-party storage systems to work with Kubernetes
  • Dynamically provisioned volumes
  • Support for advanced features like snapshots
  • Plugin model for extending storage capabilities
  • Examples include:
    • Dell EMC PowerFlex
    • NetApp Trident
    • Pure Storage Pure Service Orchestrator
    • Portworx
    • VMware vSphere CSI Driver

Troubleshooting