Skip to main content

Volumes and Storage


1 - Introduction

Containers are ephemeral by nature. To persist data, Kubernetes provides a flexible volume system.


2 - Volume types

2.1 emptyDir

An ephemeral volume created when a Pod is assigned to a Node.

apiVersion: v1
kind: Pod
metadata:
name: shared-storage
spec:
containers:
# Conteneur qui écrit
- name: writer
image: busybox
command: ['sh', '-c', 'while true; do date >> /data/log.txt; sleep 5; done']
volumeMounts:
- name: shared-data
mountPath: /data

# Conteneur qui lit
- name: reader
image: busybox
command: ['sh', '-c', 'tail -f /data/log.txt']
volumeMounts:
- name: shared-data
mountPath: /data

volumes:
- name: shared-data
emptyDir: {}
# Optionnel: stocker en RAM
# emptyDir:
# medium: Memory
# sizeLimit: 100Mi

2.2 hostPath

Mounts a directory from the host Node.

apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: host-data
mountPath: /data
volumes:
- name: host-data
hostPath:
path: /var/data
type: DirectoryOrCreate # Directory, File, Socket, etc.
caution

hostPath poses security risks and is not recommended in production. Use it only for specific cases (DaemonSets, monitoring).

2.3 configMap and secret

Already covered in the previous chapter.

volumes:
- name: config-vol
configMap:
name: app-config
- name: secret-vol
secret:
secretName: app-secrets

2.4 projected

Combines multiple sources into a single volume.

apiVersion: v1
kind: Pod
spec:
containers:
- name: app
volumeMounts:
- name: all-in-one
mountPath: /etc/config
volumes:
- name: all-in-one
projected:
sources:
- configMap:
name: app-config
- secret:
name: app-secrets
- downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labels

3 - Persistent Volumes (PV)

3.1 What is a PV?

A PersistentVolume is a storage resource provisioned in the cluster.

3.2 Create a static PV

# pv-nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
labels:
type: nfs
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
nfs:
server: 192.168.1.100
path: /exports/data

---
# pv-local.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt/disks/ssd1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- worker-1

3.3 Access Modes

ModeAbbreviationDescription
ReadWriteOnceRWORead/write by a single Node
ReadOnlyManyROXRead-only by multiple Nodes
ReadWriteManyRWXRead/write by multiple Nodes
ReadWriteOncePodRWOPRead/write by a single Pod

3.4 Reclaim Policies

PolicyDescription
RetainKeeps the data after the PVC is deleted
DeleteDeletes the volume and the data
RecycleWipes the data (deprecated)

4 - Persistent Volume Claims (PVC)

4.1 Create a PVC

# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-storage
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: manual
# Optionnel: sélectionner un PV spécifique
selector:
matchLabels:
type: nfs

4.2 Use a PVC in a Pod

apiVersion: v1
kind: Pod
metadata:
name: app-with-storage
spec:
containers:
- name: app
image: postgres:14
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim:
claimName: app-storage

4.3 PV/PVC lifecycle


5 - Storage Classes

5.1 Dynamic provisioning

StorageClasses enable automatic provisioning of PVs.

5.2 Create a StorageClass

# AWS EBS
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-storage
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

---
# GCP Persistent Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ssd-storage
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

---
# Azure Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-premium
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_LRS
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer

5.3 Use a StorageClass

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: fast-storage # Référence la StorageClass

5.4 Default StorageClass

# Voir les StorageClasses
kubectl get storageclass

# Définir comme défaut
kubectl patch storageclass fast-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

6 - Volume Expansion

6.1 Grow a PVC

# La StorageClass doit avoir allowVolumeExpansion: true
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: expandable-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi # Initialement 5Gi
storageClassName: fast-storage
# Modifier la taille
kubectl patch pvc expandable-pvc -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'

# Vérifier
kubectl get pvc expandable-pvc

7 - StatefulSets and storage

7.1 VolumeClaimTemplates

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password

# Crée automatiquement un PVC par replica
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-storage
resources:
requests:
storage: 10Gi

8 - CSI (Container Storage Interface)

8.1 What is CSI?

CSI is the standard for connecting storage systems to Kubernetes.

DriverStorage
ebs.csi.aws.comAWS EBS
disk.csi.azure.comAzure Disk
pd.csi.storage.gke.ioGCP Persistent Disk
cephfs.csi.ceph.comCephFS
nfs.csi.k8s.ioNFS

8.3 Install a CSI driver (AWS EBS example)

# Ajouter le repo Helm
helm repo add aws-ebs-csi-driver https://kubernetes-sigs.github.io/aws-ebs-csi-driver

# Installer
helm install aws-ebs-csi-driver aws-ebs-csi-driver/aws-ebs-csi-driver \
--namespace kube-system \
--set controller.serviceAccount.create=true \
--set controller.serviceAccount.name=ebs-csi-controller-sa

9 - Volume Snapshots

9.1 Create a Snapshot

# VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-snapclass
driver: ebs.csi.aws.com
deletionPolicy: Delete

---
# VolumeSnapshot
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: pvc-snapshot
spec:
volumeSnapshotClassName: csi-snapclass
source:
persistentVolumeClaimName: my-pvc

9.2 Restore from a Snapshot

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

10 - Complete example: PostgreSQL database

# Secret pour le mot de passe
apiVersion: v1
kind: Secret
metadata:
name: postgres-secret
stringData:
password: supersecret123

---
# StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: postgres-storage
provisioner: ebs.csi.aws.com
parameters:
type: gp3
reclaimPolicy: Retain
allowVolumeExpansion: true

---
# PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: postgres-storage

---
# Deployment PostgreSQL
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-data

---
# Service
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
type: ClusterIP
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432

Summary

In this chapter, we learned:

  • The different volume types (emptyDir, hostPath, etc.)
  • Persistent Volumes and Persistent Volume Claims
  • Dynamic provisioning with StorageClasses
  • Volume expansion
  • StatefulSets and volumeClaimTemplates
  • CSI drivers and snapshots

Next step

In the next chapter, we will explore Namespaces and RBAC for isolation and security.

→ Next chapter: Namespaces and RBAC


← Back to the table of contents