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
| Mode | Abbreviation | Description |
|---|---|---|
| ReadWriteOnce | RWO | Read/write by a single Node |
| ReadOnlyMany | ROX | Read-only by multiple Nodes |
| ReadWriteMany | RWX | Read/write by multiple Nodes |
| ReadWriteOncePod | RWOP | Read/write by a single Pod |
3.4 Reclaim Policies
| Policy | Description |
|---|---|
| Retain | Keeps the data after the PVC is deleted |
| Delete | Deletes the volume and the data |
| Recycle | Wipes 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.
8.2 Popular CSI drivers
| Driver | Storage |
|---|---|
| ebs.csi.aws.com | AWS EBS |
| disk.csi.azure.com | Azure Disk |
| pd.csi.storage.gke.io | GCP Persistent Disk |
| cephfs.csi.ceph.com | CephFS |
| nfs.csi.k8s.io | NFS |
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