Skip to content

Module 5 : Stockage

Objectifs du Module

  • Comprendre les types de volumes Kubernetes
  • Configurer PersistentVolumes et PersistentVolumeClaims
  • Implémenter le Dynamic Provisioning
  • Utiliser les StorageClasses

Durée : 3 heures


1. Types de Volumes

1.1 Vue d'Ensemble

Types de Volumes Kubernetes

1.2 emptyDir

# emptyDir - Volume éphémère partagé entre containers
apiVersion: v1
kind: Pod
metadata:
  name: shared-volume
spec:
  containers:
    - name: writer
      image: busybox
      command: ['sh', '-c', 'echo "Hello" > /data/message && sleep 3600']
      volumeMounts:
        - name: shared-data
          mountPath: /data

    - name: reader
      image: busybox
      command: ['sh', '-c', 'cat /data/message && sleep 3600']
      volumeMounts:
        - name: shared-data
          mountPath: /data

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

1.3 hostPath

# hostPath - Attention: non recommandé en production
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - name: host-logs
          mountPath: /var/log/nginx
  volumes:
    - name: host-logs
      hostPath:
        path: /var/log/nginx
        type: DirectoryOrCreate
        # Types: Directory, DirectoryOrCreate, File, FileOrCreate, Socket, CharDevice, BlockDevice

2. PersistentVolumes et PersistentVolumeClaims

2.1 Architecture

Architecture PV/PVC Kubernetes

2.2 PersistentVolume

# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-data
  labels:
    type: nfs
spec:
  capacity:
    storage: 100Gi
  volumeMode: Filesystem  # ou Block
  accessModes:
    - ReadWriteMany  # RWX - plusieurs pods en lecture/écriture
    # - ReadWriteOnce  # RWO - un seul node
    # - ReadOnlyMany   # ROX - plusieurs pods en lecture seule
    # - ReadWriteOncePod  # RWOP - un seul pod (K8s 1.22+)
  persistentVolumeReclaimPolicy: Retain
    # Retain  - Conservation après suppression du PVC
    # Delete  - Suppression automatique
    # Recycle - Deprecated
  storageClassName: nfs-storage
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    server: nfs-server.example.com
    path: /exports/data

---
# PV avec hostPath (dev/test)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-local
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  hostPath:
    path: /mnt/data

2.3 PersistentVolumeClaim

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

2.4 Utilisation dans un Pod

# pod-with-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-storage
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - name: data-volume
          mountPath: /usr/share/nginx/html
  volumes:
    - name: data-volume
      persistentVolumeClaim:
        claimName: data-pvc

3. StorageClasses et Dynamic Provisioning

3.1 Concept

Provisionnement Dynamique

3.2 StorageClass

# storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/aws-ebs  # ou csi driver
parameters:
  type: gp3
  iopsPerGB: "50"
  encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
  # Immediate - bind dès la création du PVC
  # WaitForFirstConsumer - bind quand un pod utilise le PVC

---
# StorageClass pour NFS (avec provisioner externe)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client
provisioner: nfs-subdir-external-provisioner
parameters:
  archiveOnDelete: "false"
  pathPattern: "${.PVC.namespace}-${.PVC.name}"

3.3 PVC avec StorageClass

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

4. CSI Drivers

4.1 Architecture CSI

Architecture CSI Kubernetes

4.2 Installation AWS EBS CSI Driver

# Installation avec Helm
helm repo add aws-ebs-csi-driver https://kubernetes-sigs.github.io/aws-ebs-csi-driver
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

# StorageClass pour EBS
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
parameters:
  type: gp3
  encrypted: "true"

Exercice : À Vous de Jouer

Mise en Pratique

Objectif : Configurer un stockage persistant pour une base de données MySQL

Contexte : Vous devez déployer une base de données MySQL dans Kubernetes avec un stockage persistant. Les données doivent survivre aux redémarrages des pods et être stockées de manière fiable.

Tâches à réaliser :

  1. Créer une StorageClass pour le provisionnement de volumes
  2. Créer un PersistentVolume (PV) de 10Gi
  3. Déployer un StatefulSet MySQL qui utilise un PVC automatique
  4. Insérer des données dans MySQL et vérifier leur persistance
  5. Supprimer le pod et vérifier que les données sont toujours présentes

Critères de validation :

  • [ ] La StorageClass est créée et disponible
  • [ ] Le PV est créé avec la capacité demandée
  • [ ] Le StatefulSet crée automatiquement un PVC
  • [ ] Les données survivent à la suppression du pod
  • [ ] Le PVC reste en status "Bound"
Solution

Étape 1 : Créer la StorageClass et le PV

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

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  hostPath:
    path: /mnt/data/mysql
    type: DirectoryOrCreate
kubectl apply -f storage.yaml
kubectl get storageclass
kubectl get pv

Étape 2 : Créer le Service Headless pour MySQL

# mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
    - name: mysql
      port: 3306
kubectl apply -f mysql-service.yaml

Étape 3 : Déployer le StatefulSet MySQL

# mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          ports:
            - containerPort: 3306
              name: mysql
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "rootpassword"
            - name: MYSQL_DATABASE
              value: "testdb"
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
            limits:
              cpu: 1
              memory: 1Gi
          livenessProbe:
            exec:
              command:
                - mysqladmin
                - ping
                - -h
                - localhost
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            exec:
              command:
                - mysql
                - -h
                - localhost
                - -e
                - "SELECT 1"
            initialDelaySeconds: 10
            periodSeconds: 5
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: local-storage
        resources:
          requests:
            storage: 5Gi
kubectl apply -f mysql-statefulset.yaml

# Attendre que le pod soit prêt
kubectl wait --for=condition=Ready pod/mysql-0 --timeout=120s

# Vérifier le PVC créé automatiquement
kubectl get pvc
kubectl get pv

Étape 4 : Insérer des données

# Accéder au pod MySQL
kubectl exec -it mysql-0 -- mysql -uroot -prootpassword testdb

# Dans MySQL, exécuter :
# CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50));
# INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob');
# SELECT * FROM users;
# EXIT;

# Ou via une seule commande
kubectl exec -it mysql-0 -- mysql -uroot -prootpassword testdb -e "
CREATE TABLE IF NOT EXISTS users (id INT PRIMARY KEY, name VARCHAR(50));
INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob');
SELECT * FROM users;
"

Étape 5 : Tester la persistance

# Supprimer le pod (pas le StatefulSet!)
kubectl delete pod mysql-0

# Le StatefulSet va recréer le pod automatiquement
kubectl wait --for=condition=Ready pod/mysql-0 --timeout=120s

# Vérifier que les données sont toujours présentes
kubectl exec -it mysql-0 -- mysql -uroot -prootpassword testdb -e "SELECT * FROM users;"

# Les données devraient être intactes !

Étape 6 : Vérifications supplémentaires

# Vérifier le StatefulSet
kubectl describe statefulset mysql

# Vérifier le PVC
kubectl get pvc data-mysql-0 -o yaml

# Vérifier le binding PV <-> PVC
kubectl get pv mysql-pv -o yaml | grep -A5 claimRef

# Voir les événements
kubectl get events --sort-by='.lastTimestamp' | grep mysql

Test avancé : Scaling

# Scaler à 2 replicas (chaque pod aura son propre PVC)
kubectl scale statefulset mysql --replicas=2

# Observer la création du deuxième PVC
kubectl get pvc -w

# Note: Vous auriez besoin d'un deuxième PV pour que le deuxième pod démarre

Nettoyage :

# Supprimer le StatefulSet
kubectl delete statefulset mysql

# Les PVC ne sont PAS supprimés automatiquement (protection des données)
kubectl get pvc

# Supprimer manuellement si nécessaire
kubectl delete pvc data-mysql-0

# Supprimer le PV
kubectl delete pv mysql-pv

# Supprimer la StorageClass
kubectl delete storageclass local-storage

Quiz

  1. Quel accessMode permet plusieurs pods en écriture ?
  2. [ ] A. ReadWriteOnce
  3. [ ] B. ReadWriteMany
  4. [ ] C. ReadOnlyMany

  5. Que fait le reclaimPolicy "Retain" ?

  6. [ ] A. Supprime le PV
  7. [ ] B. Conserve le PV après suppression du PVC
  8. [ ] C. Recycle le volume

  9. Qu'est-ce que le Dynamic Provisioning ?

  10. [ ] A. Création manuelle de PV
  11. [ ] B. Création automatique de PV via StorageClass
  12. [ ] C. Augmentation automatique de la taille

Réponses : 1-B, 2-B, 3-B


Précédent : Module 4 - Networking

Suivant : Module 6 - Sécurité


← Module 4 : Networking Module 6 : Sécurité et RBAC →

Retour au Programme