Aller au contenu
Conteneurs & Orchestration medium

Volumes applicatifs Kubernetes : partager et injecter vos données

70 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

La colonne à lire en premier est Cycle de vie pratique : elle dit ce qui reste quand le Pod disparaît. Aucun de ces six types ne survit à une replanification sur un autre nœud, y compris hostPath, qui pointe pourtant vers un disque réel. Les quatre types marqués « projeté » ne stockent d'ailleurs rien : le kubelet reconstruit leur contenu à partir d'un objet de l'API à chaque démarrage de conteneur, ce qui explique qu'ils soient toujours considérés comme des sources en lecture.

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 »

Le manifeste ci-dessous déclare un seul volume et le monte dans deux conteneurs au même chemin /cache. C'est la clé du mécanisme : le nom cache-volume apparaît trois fois, une fois dans volumes et une fois dans chaque volumeMounts. Le sleep 5 du conteneur reader évite une course au démarrage, puisque rien ne garantit l'ordre de lancement des conteneurs d'un Pod. En production, on remplace cette temporisation par une boucle d'attente ou un init container.

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

La ligne qui compte est la dernière. Les trois premiers cas ont un point commun : la perte des données est sans conséquence, l'application sait les régénérer. Dès que ce n'est plus vrai, emptyDir devient un piège, parce qu'il fonctionne parfaitement en test et perd tout au premier redémarrage de Pod, événement banal lors d'une mise à jour de Deployment ou d'un drain de nœud.

Cas d'usageAdapté ?
Cache applicatif temporaireOui
Scratch space pour calculs intermédiairesOui
Partage de fichiers entre conteneurs sidecarOui
Stockage de données qui doivent survivre au PodNon, 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.

Chaque clé de la section data deviendra un fichier portant ce nom, et sa valeur en constituera le contenu. Le pipe | du YAML préserve les retours à la ligne, ce qui permet de coller un fichier de configuration complet sans le transformer. Choisissez donc des clés qui ressemblent à des noms de fichiers valides (config.json, settings.ini) : une clé contenant un / est refusée par l'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

Le montage remplace entièrement le contenu du répertoire mountPath : si /etc/config existait déjà dans l'image, ses fichiers deviennent invisibles tant que le volume est monté. C'est la cause la plus fréquente d'applications qui ne démarrent plus après l'ajout d'un ConfigMap sur un répertoire système. Le readOnly: true n'est pas décoratif ici : sans lui, un processus peut tenter d'écrire dans le volume et échouer de façon obscure, puisque la source projetée refuse l'écriture.

pod-configmap-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: configmap-volume-demo
spec:
containers:
- name: app
image: busybox:1.36@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
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).

La section items restreint la projection aux clés listées et permet au passage de renommer le fichier dans le conteneur. Cette sélection est utile quand un même ConfigMap sert plusieurs applications : chacune ne voit que ce qui la concerne. Attention au comportement par défaut, contre-intuitif : dès que items est présent, toutes les clés non listées disparaissent du volume.

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.

Une exception annule complètement ce mécanisme : le montage par subPath. Dans ce cas, le fichier est copié une fois et n'est plus jamais actualisé, même si le ConfigMap change. La section Sous-chemins détaille cette limitation et les contournements possibles.

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

Le montage est identique à celui d'un ConfigMap, à deux réglages près qui doivent devenir des réflexes : readOnly: true sur le volumeMounts et defaultMode: 0400 sur la source. Le second fixe les permissions POSIX des fichiers projetés, ici lecture seule pour le propriétaire uniquement. Sans ce réglage, Kubernetes applique 0644 et n'importe quel processus du conteneur peut lire le mot de passe, y compris un shell de debug lancé par un tiers.

pod-secret-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-demo
spec:
containers:
- name: app
image: busybox:1.36@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
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.

Un Secret monté en volume est nettement plus sûr qu'une variable d'environnement : le fichier peut être relu après rotation, il n'apparaît pas dans kubectl describe pod, et il n'est pas hérité par les processus enfants ni exposé dans un dump de crash. Cela ne règle pas pour autant la question du stockage : la protection au repos se joue au niveau du cluster, pas du manifeste.

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

Un point critique avant de lire ce tableau : seuls les champs metadata.* sont utilisables dans un volume downwardAPI. Les entrées spec.nodeName, spec.serviceAccountName et status.podIP existent bien dans la Downward API, mais uniquement sous forme de variables d'environnement (env avec valueFrom.fieldRef) ; les déclarer dans un volume fait rejeter le Pod par l'API. Notez enfin que les limites de ressources n'utilisent pas fieldRef mais resourceFieldRef, avec le nom du conteneur cible en paramètre.

fieldPathContenu
metadata.nameNom du Pod
metadata.namespaceNamespace
metadata.labelsLabels (format key="value")
metadata.annotationsAnnotations
metadata.uidUID du Pod
spec.nodeNameNom du nœud. Variable d'environnement uniquement, rejeté dans un volume
spec.serviceAccountNameServiceAccount. Variable d'environnement uniquement
status.podIPIP du Pod. Variable d'environnement uniquement

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@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
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)

image : monter une image OCI comme volume (GA en 1.36)

Section intitulée « image : monter une image OCI comme volume (GA en 1.36) »

Depuis Kubernetes 1.36, le type de volume image est passé GA (KEP-4639, « OCI Volume Source », alpha en 1.31 et bêta en 1.33). Il permet de monter le contenu d'une image OCI directement dans un Pod, en lecture seule. Les cas d'usage typiques : injecter une configuration versionnée, livrer un dataset ou un modèle ML packagé en image, fournir des outils CLI à un sidecar (debug, audit) sans gonfler l'image principale.

Le champ reference accepte la même syntaxe qu'un image: de conteneur, donc aussi bien un tag qu'un digest @sha256, forme recommandée puisqu'elle rend le contenu du volume reproductible. Deux prérequis conditionnent le fonctionnement : une version de Kubernetes 1.31 ou supérieure, et un runtime de conteneur qui implémente la fonctionnalité côté CRI. Sur un cluster dont le runtime ne la supporte pas, le Pod reste bloqué en création au lieu de renvoyer une erreur de validation à kubectl apply.

Depuis la version 1.33, l'option subPath du volumeMounts fonctionne aussi sur un volume image et permet de ne monter qu'un sous-répertoire de l'image. Si ce sous-répertoire n'existe pas, la création du conteneur échoue et le message remonte dans les événements du kubelet, visibles avec kubectl describe pod.

imagevolume-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: imagevol-demo
spec:
containers:
- name: app
image: alpine:3.20
command: ['sleep', '300']
volumeMounts:
- name: tools
mountPath: /opt/tools
readOnly: true
volumes:
- name: tools
image:
reference: registry.k8s.io/e2e-test-images/agnhost:2.40
pullPolicy: IfNotPresent

Le contenu de l'image cible (binaire agnhost, bin/, certificats…) apparaît dans /opt/tools/. Toute écriture est refusée :

Fenêtre de terminal
kubectl exec imagevol-demo -- ls /opt/tools
# agnhost agnhost-2 bin coredns ...
kubectl exec imagevol-demo -- touch /opt/tools/foo
# touch: /opt/tools/foo: Read-only file system

La source image n'expose que deux champs, ce qui la rend simple mais rigide : ni sélection de fichiers façon items, ni réglage de permissions façon defaultMode. Le contenu monté est exactement le système de fichiers de l'image, tel quel. Privilégiez la forme repo/image@sha256:... dans la colonne des valeurs : avec un tag mutable, un pullPolicy: Always peut changer le contenu du volume d'un redémarrage à l'autre sans qu'aucun manifeste n'ait bougé.

ChampRôleValeurs
referenceRéférence OCI complète de l'imagerepo/image:tag ou repo/image@sha256:...
pullPolicyPolitique de tirageAlways, IfNotPresent, Never

Le critère de choix n'est pas la taille des données mais qui les modifie et à quelle fréquence. Un ConfigMap se change depuis le cluster en une commande, une image OCI impose de reconstruire et republier : cette lourdeur est précisément l'intérêt du volume image pour un dataset ou un modèle, puisqu'elle rend toute modification traçable et signable. À l'inverse, elle disqualifie ce type de volume pour une configuration qu'une équipe ajuste plusieurs fois par semaine.

BesoinType recommandé
Configuration ou dataset versionnés et signés (Cosign)image, l'image OCI est immuable et auditable
Configuration éditable depuis le clusterConfigMap
Données sensiblesSecret
Données partagées entre conteneurs d'un PodemptyDir
Données persistantes au-delà de la vie du PodPVC + StorageClass (voir Storage)

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 un fichier du nœud hôte directement dans le Pod, sans aucune couche d'abstraction. Le Pod voit alors le système de fichiers de la machine avec les mêmes droits que le processus conteneurisé. Ce type de volume existe pour les agents d'infrastructure qui doivent lire les journaux ou les métriques du nœud ; hors de ce cas, il crée plus de problèmes qu'il n'en résout.

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

Le champ type est votre seul garde-fou, et il est vide par défaut. Dans ce cas, Kubernetes n'effectue aucune vérification avant le montage : un chemin mal orthographié ne provoque pas d'erreur au démarrage du Pod, et l'application se retrouve à travailler sur un emplacement vide. Renseignez systématiquement Directory ou File pour obtenir un échec immédiat et lisible plutôt qu'un comportement dégradé qu'il faudra diagnostiquer en production. Les variantes OrCreate ne conviennent qu'aux DaemonSets qui doivent initialiser leur propre arborescence sur le nœud.

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 se déclare au niveau du Pod, pas du conteneur, et agit sur les volumes montés : le kubelet applique récursivement ce GID aux fichiers du volume et ajoute le groupe au processus. C'est le réglage qui résout la plupart des permission denied quand un conteneur tourne avec un utilisateur non root. Le trio fsGroup, runAsUser et runAsGroup va généralement de pair : le premier donne l'accès au volume, les deux autres déterminent l'identité qui exécute le processus.

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@sha256:73aaf090f3d85aa34ee199857f03fa3a95c8ede2ffd4cc2cdb5b94e566b11662
command: ['sh', '-c', 'ls -la /data && sleep 3600']
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}

defaultMode s'applique aux volumes projetés (configMap, secret, downwardAPI, projected) et vaut 0644 par défaut, une valeur trop permissive pour un secret. La notation reste octale : en YAML, 0400 est bien interprété comme les droits r--------, mais si vous écrivez le manifeste en JSON, la valeur doit être donnée en décimal (256), sinon les permissions obtenues n'auront rien à voir avec celles attendues.

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

subPath répond à un besoin précis : injecter un seul fichier dans un répertoire qui contient déjà d'autres fichiers de l'image. Sans lui, monter un ConfigMap sur /etc/nginx/ masquerait tout le contenu d'origine du répertoire. Avec lui, seul nginx.conf est remplacé et le reste survit. Ce confort se paie par une limitation qu'il faut connaître avant de l'adopter.

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.

Le manifeste ci-dessous représente le cas courant en production : une configuration en lecture seule, des certificats TLS en lecture seule avec permissions restreintes, et un cache inscriptible. Le fsGroup: 101 correspond au groupe nginx de l'image officielle Alpine et donne au processus non root le droit d'écrire dans /var/cache/nginx. Repérez la logique : seul le volume sur lequel l'application doit écrire n'a pas de readOnly: true.

multi-volume-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: multi-volume
spec:
securityContext:
fsGroup: 101 # Groupe nginx
containers:
- name: app
image: nginx:1.30-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: {}

Ce tableau condense les choix vus jusqu'ici. Parcourez-le par la colonne Besoin et non par le type de volume : dans la pratique, l'erreur vient presque toujours de la démarche inverse, quand on choisit hostPath ou emptyDir parce qu'ils sont faciles à écrire avant de vérifier qu'ils correspondent au besoin. La dernière ligne est la frontière du guide : dès qu'une donnée doit survivre au Pod, on quitte les volumes applicatifs pour le domaine des PersistentVolumeClaims.

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

Ces sept erreurs ont un point commun : elles fonctionnent en environnement de test. Un hostPath marche parfaitement sur un cluster à un nœud, un emptyDir conserve ses données tant qu'on ne redéploie pas, et un ConfigMap monté par subPath semble à jour tant que personne ne le modifie. Elles se manifestent au moment le moins choisi : la montée en charge, la mise à jour ou la panne de nœud.

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

Presque tous les incidents de volume se lisent dans les événements du Pod, pas dans les logs applicatifs : commencez donc toujours par kubectl describe pod. Un Pod bloqué en ContainerCreating signale un problème de montage, alors qu'un Pod en Running qui se plaint de permissions relève du securityContext. Cette distinction oriente le diagnostic bien plus vite que la lecture du manifeste.

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

Ces cinq commandes se lancent dans l'ordre, du manifeste vers le système de fichiers réel. Le point de bascule est la troisième : si kubectl exec ... ls montre les fichiers attendus, le problème est applicatif ; s'il ne montre rien, le montage a échoué et la dernière commande, sur les événements, en donnera la raison. Notez que la première nécessite jq sur votre poste, pas dans le cluster.

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

L'examen CKAD est chronométré et n'accorde aucun point pour un YAML élégant. Les cinq réflexes ci-dessous visent le gain de temps : générer une base plutôt que l'écrire, vérifier en une commande plutôt qu'en trois, et connaître par cœur le piège qui coûte le plus de points, la correspondance entre volumes.name et volumeMounts.name.

  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

Dix questions pour vérifier que les distinctions du guide sont acquises : volume éphémère contre volume projeté, effet de subPath sur la propagation, rôle respectif de fsGroup et de defaultMode. Une réponse hésitante sur l'une d'elles pointe la section à relire.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
8 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Les StatefulSets : Le volume propre à chaque replica, pour les applications à état.
  • Introduction au stockage : Les PersistentVolume et PersistentVolumeClaim, au-delà des volumes éphémères.
  • StorageClass : Le provisionnement dynamique qui évite de créer les volumes à la main.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn