Aller au contenu
Conteneurs & Orchestration medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Volumes applicatifs Kubernetes : partager et injecter vos données

23 min de lecture

logo kubernetes

Vos conteneurs ont besoin de partager des fichiers entre eux ? D’accéder à une configuration externe ? De recevoir des secrets ? Les volumes applicatifs permettent de monter des données dans vos Pods — qu’il s’agisse de fichiers temporaires, de configurations projetées ou de données sensibles.

Ce guide couvre les volumes utilisés côté développeur. Pour le stockage persistant géré par les administrateurs (PV, PVC, StorageClass), voir Storage Kubernetes.

Prérequis : concepts des Pods et un cluster fonctionnel.

  • Utiliser emptyDir pour partager des données temporaires entre conteneurs
  • Monter un ConfigMap ou un Secret comme fichiers injectés
  • Combiner plusieurs sources avec projected
  • Comprendre les cycles de vie et limites de chaque type de volume
  • Appliquer les bonnes pratiques de sécurité et de permissions

Un volume Kubernetes est un espace de stockage monté dans un ou plusieurs conteneurs d’un Pod. Selon son type, il peut être :

  • Éphémère : créé avec le Pod, supprimé avec lui (emptyDir)
  • Projeté : généré depuis un objet API Kubernetes (ConfigMap, Secret, downwardAPI)
  • Lié au nœud : adossé au système de fichiers de la machine hôte (hostPath)

Les données écrites dans le système de fichiers propre au conteneur (writable layer) ne constituent pas une solution fiable pour partager ou conserver des données applicatives. Les volumes Kubernetes fournissent une abstraction dédiée à cet usage :

  • Partage : plusieurs conteneurs d’un même Pod peuvent monter le même volume
  • Injection : Kubernetes peut pré-remplir le volume avec des configurations ou secrets
  • Isolation : le volume existe indépendamment du système de fichiers du conteneur
TypeNatureCycle de vie pratique
emptyDiréphémèreDure tant que le Pod reste sur le nœud — supprimé si le Pod disparaît
configMapprojetéReflète un objet ConfigMap — pas du stockage persistant
secretprojetéReflète un objet Secret — pas du stockage persistant
downwardAPIprojetéGénéré à partir des métadonnées du Pod
projectedprojetéCombine ConfigMap, Secret, downwardAPI et serviceAccountToken
hostPathnœud localDépend du nœud — non portable, non résilient

Un volume emptyDir est créé vide quand le Pod démarre et supprimé quand le Pod disparaît du nœud. Idéal pour partager des données temporaires entre conteneurs du même Pod.

Exemple : deux conteneurs qui partagent des données

Section intitulée « Exemple : deux conteneurs qui partagent des données »
emptydir-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: emptydir-demo
spec:
containers:
- name: writer
image: busybox:1.36
command: ['sh', '-c', 'echo "Données partagées" > /cache/data.txt && sleep 3600']
volumeMounts:
- name: cache-volume
mountPath: /cache
- name: reader
image: busybox:1.36
command: ['sh', '-c', 'sleep 5 && cat /cache/data.txt && sleep 3600']
volumeMounts:
- name: cache-volume
mountPath: /cache
volumes:
- name: cache-volume
emptyDir: {}
Fenêtre de terminal
kubectl apply -f emptydir-demo.yaml
kubectl logs emptydir-demo -c reader

Sortie :

Données partagées

Le conteneur reader lit bien le fichier écrit par writer — ils partagent le même volume.

Pour des performances maximales (cache, scratch space), utilisez la RAM via tmpfs :

volumes:
- name: cache-volume
emptyDir:
medium: Memory
sizeLimit: 100Mi
Cas d’usageAdapté ?
Cache applicatif temporaire✅ Oui
Scratch space pour calculs intermédiaires✅ Oui
Partage de fichiers entre conteneurs sidecar✅ Oui
Stockage de données qui doivent survivre au Pod❌ Non — utilisez un PVC

Un ConfigMap monté en volume crée des fichiers — un par clé — dans le répertoire cible. C’est un volume projeté : Kubernetes génère les fichiers à partir d’un objet API.

configmap-volume.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
config.json: |
{
"database": "postgresql",
"port": 5432
}
settings.ini: |
[app]
debug = false
log_level = info
pod-configmap-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: configmap-volume-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'ls -la /etc/config && cat /etc/config/config.json && sleep 3600']
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
Fenêtre de terminal
kubectl apply -f configmap-volume.yaml
kubectl apply -f pod-configmap-volume.yaml
kubectl logs configmap-volume-demo

Sortie :

total 0
lrwxrwxrwx 1 root root 18 Mar 22 10:00 config.json -> ..data/config.json
lrwxrwxrwx 1 root root 19 Mar 22 10:00 settings.ini -> ..data/settings.ini
{
"database": "postgresql",
"port": 5432
}

Chaque clé du ConfigMap devient un fichier (via des liens symboliques pour permettre les mises à jour atomiques).

Pour ne monter qu’un fichier spécifique :

volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: config.json
path: app-config.json # Nom du fichier dans le conteneur

Les fichiers sont mis à jour automatiquement quand le ConfigMap change. Cependant, ce n’est pas instantané : le délai dépend du kubelet et de sa stratégie de détection des changements (Watch, TTL, ou accès direct API). Il peut aller jusqu’au délai de synchronisation du kubelet (--sync-frequency, 1 minute par défaut) plus le délai de propagation du cache.

Un Secret monté en volume fonctionne comme un ConfigMap, mais pour des données sensibles (credentials, certificats, clés API).

Utilisez stringData pour écrire les valeurs en clair dans le manifest — Kubernetes les encodera automatiquement en base64 :

secret-volume.yaml
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
username: admin
password: password123
pod-secret-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-demo
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'cat /etc/secrets/username && echo && cat /etc/secrets/password && sleep 3600']
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentials
defaultMode: 0400 # Lecture seule pour le propriétaire
Fenêtre de terminal
kubectl apply -f secret-volume.yaml
kubectl apply -f pod-secret-volume.yaml
kubectl logs secret-volume-demo

Sortie :

admin
password123

Le fichier contient la valeur décodée — pas le base64.

Bonnes pratiques :

  • Montez toujours les Secrets en readOnly: true
  • Définissez des permissions restrictives avec defaultMode: 0400
  • Ne loggez jamais le contenu des fichiers secrets
  • Envisagez des solutions comme Sealed Secrets ou External Secrets Operator pour la gestion

Le volume downwardAPI expose les métadonnées du Pod comme fichiers. Utile pour que l’application connaisse son contexte sans code spécifique Kubernetes.

downward-api-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: downward-demo
labels:
app: myapp
version: v1
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'cat /etc/podinfo/labels && echo "---" && cat /etc/podinfo/name && sleep 3600']
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
readOnly: true
volumes:
- name: podinfo
downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labels
- path: name
fieldRef:
fieldPath: metadata.name
- path: namespace
fieldRef:
fieldPath: metadata.namespace
- path: cpu-limit
resourceFieldRef:
containerName: app
resource: limits.cpu
fieldPathContenu
metadata.nameNom du Pod
metadata.namespaceNamespace
metadata.labelsLabels (format key="value")
metadata.annotationsAnnotations
metadata.uidUID du Pod
spec.nodeNameNom du nœud
spec.serviceAccountNameServiceAccount
status.podIPIP du Pod

Un volume projected permet de monter plusieurs sources (ConfigMap, Secret, downwardAPI, serviceAccountToken) dans un seul répertoire. C’est la méthode standard pour agréger des données de sources différentes.

projected-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: projected-demo
labels:
app: myapp
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'ls -la /etc/all-config && sleep 3600']
volumeMounts:
- name: all-config
mountPath: /etc/all-config
readOnly: true
volumes:
- name: all-config
projected:
sources:
- configMap:
name: app-config
items:
- key: config.json
path: config.json
- secret:
name: db-credentials
items:
- key: password
path: db-password
- downwardAPI:
items:
- path: pod-name
fieldRef:
fieldPath: metadata.name

Résultat dans /etc/all-config :

config.json (depuis ConfigMap)
db-password (depuis Secret)
pod-name (depuis downwardAPI)

hostPath : accès au système de fichiers du nœud

Section intitulée « hostPath : accès au système de fichiers du nœud »

Un volume hostPath monte un répertoire ou fichier du nœud hôte dans le Pod.

hostpath-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: hostpath-demo
spec:
containers:
- name: logger
image: busybox:1.36
command: ['sh', '-c', 'ls /var/log && sleep 3600']
volumeMounts:
- name: host-logs
mountPath: /var/log
readOnly: true
volumes:
- name: host-logs
hostPath:
path: /var/log
type: Directory
TypeComportement
"" (vide)Pas de vérification
DirectoryOrCreateCrée le répertoire s’il n’existe pas
DirectoryLe répertoire doit exister (échec sinon)
FileOrCreateCrée le fichier s’il n’existe pas
FileLe fichier doit exister (échec sinon)

Les problèmes de permissions sont fréquents avec les volumes. Le securityContext permet de les résoudre.

fsgroup-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: fsgroup-demo
spec:
securityContext:
fsGroup: 1000 # Tous les fichiers montés appartiendront à ce groupe
runAsUser: 1000
runAsGroup: 1000
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c', 'ls -la /data && sleep 3600']
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}

Pour les volumes ConfigMap et Secret :

volumes:
- name: secret-volume
secret:
secretName: db-credentials
defaultMode: 0400 # -r-------- (lecture seule pour le propriétaire)
ModePermissionsUsage
0644-rw-r--r--Fichiers de configuration lisibles par tous
0600-rw-------Fichiers sensibles, écriture par le propriétaire
0400-r--------Secrets, lecture seule
0755-rwxr-xr-xScripts exécutables

Pour monter un fichier spécifique sans écraser le répertoire existant :

volumeMounts:
- name: config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf # Ne monte que ce fichier

Alternative sans subPath : Montez le ConfigMap dans un sous-répertoire et créez un lien symbolique dans un init container.

Un Pod peut monter plusieurs volumes de types différents :

multi-volume-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: multi-volume
spec:
securityContext:
fsGroup: 101 # Groupe nginx
containers:
- name: app
image: nginx:alpine
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: secrets
mountPath: /etc/nginx/ssl
readOnly: true
- name: cache
mountPath: /var/cache/nginx
volumes:
- name: config
configMap:
name: nginx-config
- name: secrets
secret:
secretName: nginx-certs
defaultMode: 0400
- name: cache
emptyDir: {}
BesoinVolume conseilléRemarque
Partager temporairement entre conteneursemptyDirDisparaît avec le Pod
Cache haute performanceemptyDir avec medium: MemoryCompte dans la mémoire du Pod
Injecter une config sous forme de fichiersconfigMapVolume projeté, mises à jour auto
Injecter un secret sous forme de fichierssecretVolume projeté, toujours en readOnly
Exposer le nom/namespace/labels du PoddownwardAPIVolume projeté
Combiner config + secret + metadataprojectedMéthode standard d’agrégation
Accéder à un chemin du nœudhostPathÉviter sauf DaemonSet/debug
Stocker des données persistantesPVCPas un volume applicatif
Anti-patternPourquoi c’est problématiqueSolution
Utiliser hostPath comme stockage persistantNon portable, risques de sécuritéUtiliser un PVC
Modifier des fichiers depuis un ConfigMap/SecretCe sont des sources projetées, pas un espace d’écritureCopier dans un emptyDir si besoin de modifier
Oublier que subPath casse la propagationLes mises à jour ConfigMap ne seront pas reflétéesMonter le répertoire complet ou redémarrer le Pod
Supposer qu’un emptyDir survit au PodLe contenu est perdu si le Pod est supprimé ou rescheduléUtiliser un PVC pour les données importantes
Oublier readOnly: true sur les SecretsRisque de modification accidentelleToujours spécifier readOnly: true
Ignorer les permissions (fsGroup, defaultMode)Erreurs “permission denied” au runtimeDéfinir explicitement le contexte de sécurité
Monter des chemins sensibles via hostPathContournement des contrôles de sécurité du clusterUtiliser les Pod Security Standards pour bloquer
SymptômeCause probableSolution
MountVolume.SetUp failed for volumeConfigMap/Secret introuvableVérifier que la ressource existe dans le même namespace
Fichiers videsConfigMap/Secret videkubectl get configmap xxx -o yaml pour vérifier
Permission deniedMode trop restrictif ou mauvais user/groupAjuster defaultMode, fsGroup ou runAsUser
Volume non montéNom mal référencéVérifier que volumeMounts.name correspond exactement à volumes.name
Fichier non mis à jour après modification ConfigMapUtilisation de subPathRedémarrer le Pod ou ne pas utiliser subPath
Pod bloqué en ContainerCreatingVolume non disponiblekubectl describe pod pour voir les événements
Fenêtre de terminal
# Voir les montages déclarés
kubectl get pod mon-pod -o jsonpath='{.spec.volumes}' | jq
# Voir les montages effectifs dans le conteneur
kubectl describe pod mon-pod | grep -A 20 "Mounts:"
# Lister les fichiers dans un volume monté
kubectl exec mon-pod -- ls -la /chemin/du/montage
# Vérifier les permissions
kubectl exec mon-pod -- stat /chemin/du/fichier
# Voir les événements liés aux volumes
kubectl get events --field-selector involvedObject.name=mon-pod
  1. Identifier où déclarer

    • spec.volumes[] : déclare le volume et sa source
    • spec.containers[].volumeMounts[] : monte le volume dans un conteneur
    • Le name doit correspondre exactement entre les deux
  2. Créer rapidement un Pod avec volume

    Fenêtre de terminal
    # Générer le YAML de base
    kubectl run mypod --image=nginx --dry-run=client -o yaml > pod.yaml
    # Puis ajouter volumes et volumeMounts manuellement
  3. Vérifier vite le montage

    Fenêtre de terminal
    kubectl describe pod mypod | grep -A 5 "Mounts:"
    kubectl exec mypod -- ls -la /chemin/montage
    kubectl exec mypod -- cat /chemin/montage/fichier
  4. Différencier volume partagé et volume projeté

    • emptyDir : les conteneurs peuvent lire et écrire — c’est un espace partagé
    • configMap, secret, downwardAPI : injectés par Kubernetes — à traiter comme read-only
  5. Piège courant à l’examen

    Le volume est déclaré mais pas monté, ou le nom ne correspond pas. Toujours vérifier la correspondance volumes.namevolumeMounts.name.

Pour des besoins avancés, Kubernetes propose aussi des ephemeral volumes génériques qui permettent d’utiliser des drivers CSI pour créer des volumes éphémères (par exemple, un disque SSD temporaire provisionné dynamiquement). Ces volumes sont supprimés avec le Pod.

volumes:
- name: scratch
ephemeral:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi

Consultez la documentation Kubernetes sur les ephemeral volumes pour approfondir.

  1. emptyDir : partage temporaire entre conteneurs — disparaît avec le Pod
  2. configMap et secret : volumes projetés depuis des objets API — pas du stockage persistant
  3. projected : combine plusieurs sources dans un seul répertoire — méthode standard d’agrégation
  4. downwardAPI : expose les métadonnées du Pod comme fichiers
  5. hostPath : accès au système de fichiers du nœud — risques majeurs, à éviter en production
  6. Toujours monter ConfigMap et Secret en readOnly: true
  7. Avec subPath, les mises à jour ne sont pas propagées
  8. Utilisez securityContext et fsGroup pour gérer les permissions
  9. Un volume se déclare dans spec.volumes, se monte avec volumeMounts — les noms doivent correspondre

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn