Docker — Nettoyer en toute sécurité : récupérer de l’espace sans supprimer ce dont vous avez besoin

Cet article vous a aidé ?

Ça arrive toujours au pire moment : votre nœud est sain, le déploiement est vert, puis le disque atteint 100%.
Soudain Docker ne peut plus tirer d’images, votre runtime ne peut plus écrire de logs, et tout semble « mystérieusement instable ».
La tentation est d’exécuter un héroïque docker system prune -a --volumes et d’espérer le meilleur.

L’espoir n’est pas une stratégie de nettoyage. Voici la méthode pratique et sûre pour la production afin de récupérer de l’espace Docker tout en conservant ce dont vous avez réellement besoin : les bonnes images, les bons volumes, et votre capacité à expliquer ce qui s’est passé ensuite.

Un modèle mental d’où Docker utilise l’espace (et pourquoi cela vous surprend)

L’utilisation disque par Docker n’est pas « une chose ». Ce sont plusieurs compartiments qui grandissent pour différentes raisons, et « nettoyer Docker »
signifie choisir quels compartiments vous êtes prêt à vider.

Les compartiments qui comptent

  • Images : couches tirées depuis les registres, plus vos images construites localement.
  • Containers : la couche inscriptible (modifications par-dessus l’image), plus les métadonnées des conteneurs.
  • Volumes : données durables. Les bases de données les adorent. Les accidents aussi.
  • Cache de build : couches intermédiaires et métadonnées de cache. Dans les CI, cela devient un tas de compost.
  • Logs : logs des conteneurs (souvent fichiers JSON) et logs applicatifs que vous écrivez à l’intérieur des conteneurs.
  • « Autre » : réseaux, plugins, objets swarm, et reliquats d’anciens drivers de stockage.

La plupart des incidents viennent de la confusion entre ces compartiments. On traite les volumes comme du cache. Ce n’en est pas.
On traite le cache de build comme « il s’auto-gèrera ». Il ne le fera pas. On ignore les logs jusqu’à ce qu’ils deviennent un déni de service de stockage.

Pourquoi overlay2 rend l’utilisation du disque étrange

Sur Linux, le driver de stockage Docker le plus courant est overlay2. Il stocke les couches d’images et les couches inscriptibles
des conteneurs sous le répertoire racine des données Docker (souvent /var/lib/docker).
Ce répertoire peut devenir énorme, et il n’est pas immédiatement évident quel conteneur « possède » quelles parties.

OverlayFS est efficace, mais aussi très littéral : chaque changement qu’un conteneur fait sur son système de fichiers devient un nouveau
fichier dans sa couche inscriptible. Si votre application écrit 50GB de données « temporaires » dans /tmp à l’intérieur du conteneur,
ce n’est pas temporaire. C’est 50GB sur l’hôte, et vous le retrouverez lorsque le disque se remplira.

Règle générale : si vous voulez que des données survivent à la suppression d’un conteneur, mettez-les dans un volume. Si vous voulez que des données soient faciles à
supprimer, mettez-les quelque part où vous pouvez supprimer de manière déterministe — idéalement un volume que vous pouvez détruire volontairement, ou un
chemin sur l’hôte avec une politique de rétention.

Une citation à garder : la phrase de Gene Kranz est célèbre dans les cercles fiabilité : « Failure is not an option. »
C’est aussi un rappel que le nettoyage fait partie des opérations, pas un passe-temps qu’on fait après un incident.

Faits intéressants et petite histoire (parce que ça explique le bazar actuel)

  1. Les drivers de stockage par défaut de Docker ont changé au fil du temps. Les systèmes plus anciens utilisaient AUFS ou Device Mapper ; beaucoup de distributions modernes ont choisi overlay2 pour de meilleures performances et simplicité.
  2. Les « images pendantes » sont devenues un phénomène parce que les couches sont partagées. Docker conserve des couches qui pourraient encore être référencées. Il ne les supprimera pas tant qu’il n’est pas sûr qu’elles sont inutilisées.
  3. BuildKit a changé la signification du « cache de build ». Avec BuildKit activé, le cache peut exister sous différentes formes et peut être exporté/importé, ce qui est génial — jusqu’à ce que vous ne le purgiez jamais.
  4. Les logs de conteneur utilisent par défaut un logger JSON sur de nombreuses installations. C’est pratique, mais il écrit sans fin à moins de configurer la rotation.
  5. Les pannes liées au disque plein sur les hôtes conteneurisés sont souvent des pannes secondaires. La première panne est « un processus a écrit trop », Docker a juste facilité la dissimulation.
  6. Les commandes « prune » ont été ajoutées parce que le nettoyage manuel était trop risqué. Docker a introduit des opérations prune de haut niveau pour éviter la manipulation directe de /var/lib/docker (ce qui reste une mauvaise idée).
  7. La réutilisation des couches est à la fois l’optimisation et le piège. Reconstruire des images avec beaucoup de couches uniques rend parfois le pull/build plus rapide, et la croissance disque toujours plus rapide.
  8. Les runners CI ont re-popularisé par accident les « hôtes-dont-on-prend-soin ». Un runner « sans état » qui n’est jamais re-imagé devient un musée de couches en cache.

Blague #1 : l’utilisation disque Docker, c’est comme un tiroir à bazar — tout ce qui s’y trouve était « temporaire » à l’époque.

Mode d’urgence pour diagnostic rapide : premières/deuxièmes/troisièmes vérifications

Quand le disque crie, vous n’avez pas le temps de débattre philosophiquement sur les couches. Vous avez besoin d’une séquence rapide
qui vous dit où sont les octets et ce que vous pouvez supprimer en toute sécurité.

Première étape : confirmer que l’hôte est réellement à court d’espace (et où)

  • Vérifiez l’utilisation des systèmes de fichiers (df) et l’utilisation des inodes (df -i).
  • Identifiez quel mount est plein. Si /var est séparé, Docker peut être sain et ce sont vos logs qui posent problème.

Deuxième étape : mesurer les compartiments Docker avant de supprimer quoi que ce soit

  • docker system df pour obtenir la répartition haute-niveau.
  • Cherchez le plus gros contributeur : images, containers, volumes, cache de build.

Troisième étape : identifier les « écrivains inattendus »

  • Logs de conteneurs énormes (fichiers JSON) et logs applicatifs à l’intérieur des couches inscriptibles des conteneurs.
  • Volumes qui gonflent parce qu’une base de données ou une queue a conservé des données.
  • Croissance du cache de build sur les runners CI.

Si vous êtes en véritable urgence (disque à 100%), priorisez le rétablissement de la capacité d’écriture : faites tourner/tronquez les logs incontrôlés,
stoppez les plus gros coupables, ou déplacez des données hors hôte. Puis procédez à un nettoyage sûr avec traçabilité.

Tâches pratiques (commandes + signification des sorties + décision)

Ces tâches supposent que vous avez un accès shell à l’hôte Docker. Exécutez-les dans l’ordre quand vous le pouvez.
Si vous êtes dans un moment « le disque est plein, tout brûle », sautez aux tâches marquées comme adaptées à l’urgence.

Tâche 1 : Vérifier quel système de fichiers est plein

cr0x@server:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2   80G   78G  1.2G  99% /
tmpfs            16G   92M   16G   1% /run
/dev/nvme1n1p1  200G   40G  160G  20% /data

Ce que ça signifie : Le système de fichiers racine est presque plein. C’est typiquement là où vit /var/lib/docker.
Décision : Concentrez-vous sur Docker et les logs système sur /, pas sur le mount spacieux /data.

Tâche 2 : Vérifier l’épuisement des inodes (sournois, fréquent)

cr0x@server:~$ df -i
Filesystem       Inodes  IUsed   IFree IUse% Mounted on
/dev/nvme0n1p2  5242880 5110000 132880   98% /

Ce que ça signifie : Vous êtes presque à court d’inodes. Cela peut arriver avec des millions de petits fichiers (cache de build, node_modules, etc.).
Décision : « Libérer du disque » peut ne pas suffire ; il faut supprimer beaucoup de petits fichiers, généralement des caches ou artefacts de build.

Tâche 3 : Trouver la racine de données Docker

cr0x@server:~$ docker info --format '{{ .DockerRootDir }}'
/var/lib/docker

Ce que ça signifie : C’est le répertoire dont vous gérez la croissance.
Décision : Si votre système racine est petit, planifiez une migration de Docker data root plus tard. Pour l’instant, diagnostiquez l’utilisation à l’intérieur.

Tâche 4 : Obtenir la comptabilité disque propre à Docker

cr0x@server:~$ docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          52        12        38.6GB    21.4GB (55%)
Containers      19        7         2.4GB     1.1GB (45%)
Local Volumes   34        15        96.2GB    18.0GB (18%)
Build Cache     184       0         22.8GB    22.8GB (100%)

Ce que ça signifie : Vos volumes constituent le plus gros morceau (96GB), mais le cache de build est entièrement récupérable (22GB).
Décision : Commencez par pruner le cache de build (plutôt sûr), puis les images/containers inutilisés. Traitez les volumes avec précaution.

Tâche 5 : Lister les images les plus lourdes (repérer les coupables)

cr0x@server:~$ docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.Size}}' | head
REPOSITORY            TAG        IMAGE ID       SIZE
myapp/api             prod       9c1a2d4e0b2f   1.21GB
myapp/api             staging    62f13c9d1a11   1.19GB
postgres              15         2b6d1f2aa0c1   413MB
node                  20         1c2d0a41d2aa   1.11GB

Ce que ça signifie : Vous avez plusieurs tags volumineux pour le même repo. C’est courant quand les déploiements taggent chaque commit.
Décision : Conservez ceux qui tournent actuellement ; supprimez les anciens tags s’ils sont inutilisés. Plus tard : implémentez une rétention dans votre pipeline.

Tâche 6 : Identifier les conteneurs qui utilisent encore ces images

cr0x@server:~$ docker ps --format 'table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}'
CONTAINER ID   IMAGE                 STATUS          NAMES
c8b2d9f2a4aa   myapp/api:prod        Up 6 days       api-prod-1
e12a9a0110bb   postgres:15           Up 12 days      pg-main

Ce que ça signifie : Seul myapp/api:prod est activement utilisé ; staging pourrait être superflu.
Décision : Ne supprimez pas les images utilisées par des conteneurs en cours d’exécution. Supprimez les inutilisées après avoir confirmé qu’aucun autre hôte n’en dépend localement.

Tâche 7 : Afficher les conteneurs arrêtés et leurs tailles

cr0x@server:~$ docker ps -a --size --format 'table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Size}}\t{{.Names}}' | head
CONTAINER ID   IMAGE              STATUS                      SIZE                NAMES
a1b2c3d4e5f6   myapp/api:staging  Exited (137) 3 days ago     1.2GB (virtual 2GB) api-staging-1
f1e2d3c4b5a6   node:20            Exited (0) 9 days ago       600MB (virtual 1GB) build-job-77

Ce que ça signifie : Les conteneurs arrêtés peuvent conserver de grosses couches inscriptibles.
Décision : Supprimez les anciens conteneurs arrêtés si vous n’avez pas besoin de leur système de fichiers pour des analyses judiciaires.

Tâche 8 : Supprimer les conteneurs arrêtés (faible risque)

cr0x@server:~$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
a1b2c3d4e5f6
f1e2d3c4b5a6

Total reclaimed space: 1.8GB

Ce que ça signifie : Vous avez récupéré les couches inscriptibles de conteneurs qui n’étaient pas en cours d’exécution.
Décision : Bon premier nettoyage. Si l’espace est encore serré, passez au pruning des images/cache de build.

Tâche 9 : Pruner le cache de build (généralement sûr, parfois coûteux)

cr0x@server:~$ docker builder prune
WARNING! This will remove all dangling build cache.
Are you sure you want to continue? [y/N] y
Deleted build cache objects:
l8p4m2n7k0q1
u2v3w4x5y6z7

Total reclaimed space: 22.8GB

Ce que ça signifie : Vous avez supprimé le cache de build inutilisé. Les builds peuvent être plus lents jusqu’à la reconstruction du cache.
Décision : Sur les runners CI, c’est souvent le bon compromis. Sur les serveurs de build, envisagez un prune programmé avec des seuils.

Tâche 10 : Prévisualiser le prune des images avant exécution

cr0x@server:~$ docker image prune --all --force --filter 'until=240h'
Deleted Images:
untagged: myapp/api@sha256:1a2b...
deleted: sha256:9c1a2d4e0b2f...

Total reclaimed space: 12.3GB

Ce que ça signifie : Les images non utilisées par un conteneur et plus anciennes que 10 jours (240h) ont été supprimées.
Décision : Utilisez des filtres temporels en production. « Supprimer tout ce qui n’est pas utilisé » c’est acceptable ; « supprimer tout ce qui n’est pas en cours maintenant » est la façon de ruiner votre prochain déploiement.

Tâche 11 : Inspecter l’utilisation des volumes et repérer les candidats risqués

cr0x@server:~$ docker volume ls
DRIVER    VOLUME NAME
local     pgdata_main
local     redisdata
local     myapp_cache
local     old_test_volume_42

Ce que ça signifie : Les volumes existent indépendamment des conteneurs. Certains sont clairement des données de production.
Décision : Ne prunez jamais les volumes à l’aveugle. Identifiez quels conteneurs les montent et ce qu’ils stockent.

Tâche 12 : Mapper les volumes aux conteneurs (éviter de supprimer des données en production)

cr0x@server:~$ docker ps -a --format '{{.ID}} {{.Names}}' | while read id name; do
>   echo "== $name ($id) =="
>   docker inspect --format '{{range .Mounts}}{{.Name}} -> {{.Destination}} ({{.Type}}){{"\n"}}{{end}}' "$id"
> done
== api-prod-1 (c8b2d9f2a4aa) ==
myapp_cache -> /var/cache/myapp (volume)

== pg-main (e12a9a0110bb) ==
pgdata_main -> /var/lib/postgresql/data (volume)

Ce que ça signifie : Vous savez maintenant quels volumes sont montés par quels conteneurs.
Décision : Envisagez de supprimer uniquement les volumes qui sont (a) non montés et (b) confirmés comme jetables.

Tâche 13 : Mesurer la taille réelle des volumes sur le disque

cr0x@server:~$ sudo du -sh /var/lib/docker/volumes/*/_data 2>/dev/null | sort -h | tail
2.1G  /var/lib/docker/volumes/myapp_cache/_data
18G   /var/lib/docker/volumes/redisdata/_data
71G   /var/lib/docker/volumes/pgdata_main/_data

Ce que ça signifie : Postgres est le poids lourd. Cela peut être attendu, ou bien une rétention hors de contrôle.
Décision : Ne « nettoyez pas Docker » pour résoudre la croissance d’une base de données. Corrigez la rétention de la base et planifiez le stockage.

Tâche 14 (adaptée urgence) : Trouver des logs JSON de conteneurs surdimensionnés

cr0x@server:~$ sudo find /var/lib/docker/containers -name '*-json.log' -printf '%s %p\n' 2>/dev/null | sort -n | tail
21474836480 /var/lib/docker/containers/c8b2d9f2a4aa.../c8b2d9f2a4aa...-json.log
5368709120  /var/lib/docker/containers/e12a9a0110bb.../e12a9a0110bb...-json.log

Ce que ça signifie : Un log de conteneur fait 20GB. Ce n’est pas de l’observabilité, c’est une prise d’otage.
Décision : Mettez en place la rotation des logs correctement. En cas d’urgence, vous pouvez tronquer le fichier (voir tâche suivante), mais considérez cela comme une mesure exceptionnelle.

Tâche 15 (break-glass) : Tronquer un log JSON incontrôlé sans redémarrer le conteneur

cr0x@server:~$ sudo truncate -s 0 /var/lib/docker/containers/c8b2d9f2a4aa.../c8b2d9f2a4aa...-json.log

Ce que ça signifie : Vous avez récupéré de l’espace immédiatement. Docker continue d’écrire dans le même handle de fichier.
Décision : Ne faites cela que sous forte pression disque. Ensuite, corrigez la rotation des logs pour ne plus jamais avoir à le faire.

Tâche 16 : Configurer la rotation des logs Docker (la vraie solution)

cr0x@server:~$ sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "5"
  }
}
EOF
cr0x@server:~$ sudo systemctl restart docker

Ce que ça signifie : Les nouveaux conteneurs auront une croissance de fichier de log limitée. Les conteneurs existants peuvent nécessiter un redémarrage pour prendre en compte ces options.
Décision : Adoptez cela comme base partout. Si vous voulez des logs centralisés, poussez-les, mais limitez toujours la croissance locale.

Tâche 17 : Identifier la croissance à l’intérieur des couches inscriptibles des conteneurs

cr0x@server:~$ docker exec c8b2d9f2a4aa sh -lc 'du -sh /tmp /var/tmp /var/log 2>/dev/null | sort -h'
120M  /var/log
5.6G  /tmp
32M   /var/tmp

Ce que ça signifie : L’application utilise le système de fichiers du conteneur pour des données temporaires volumineuses.
Décision : Déplacez ce chemin vers un volume ou tmpfs, ou corrigez le nettoyage de l’application. Sinon, cela recommencera à croître après chaque « nettoyage ».

Tâche 18 : Utiliser un system prune filtré par temps (prudent, mais pratique)

cr0x@server:~$ docker system prune --force --filter 'until=168h'
Deleted Networks:
old_ci_net

Deleted Images:
deleted: sha256:62f13c9d1a11...

Total reclaimed space: 6.4GB

Ce que ça signifie : Vous avez nettoyé des objets inutilisés plus vieux que 7 jours. Les volumes ne sont pas touchés sauf si vous ajoutez --volumes.
Décision : C’est le « prune par défaut sûr » pour beaucoup d’hôtes. Il réduit les risques tout en contrôlant la croissance.

Tâche 19 : Vérifier la récupération d’espace et confirmer la stabilité de l’hôte

cr0x@server:~$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2   80G   52G   25G  68% /

Ce que ça signifie : Vous avez retrouvé une marge de manœuvre saine.
Décision : Ne vous arrêtez pas là. Mettez en place des garde-fous : rotation des logs, prune programmé, et politique de cache de build.

Trois mini-récits d’entreprise venus des tranchées disque-plein

1) L’incident causé par une mauvaise hypothèse : « Les volumes, c’est juste du cache, non ? »

Une entreprise de taille moyenne gérait une petite flotte d’hôtes Docker pour des outils internes. Rien de sophistiqué : un conteneur Postgres,
une application web, un worker en arrière-plan. Ce n’était pas Kubernetes. C’était « simple ». Ce mot a un bilan.

Un hôte a atteint 95% de disque. Un ingénieur est intervenu, a regardé docker system df, et a constaté que les volumes représentaient le plus gros morceau.
Ils avaient récemment nettoyé le cache de build et les images, et le volume restait important. Ils ont donc fait ce que l’internet suggère quand on est fatigué : docker system prune -a --volumes.

La commande a réussi rapidement. Le disque s’est libéré de façon spectaculaire. Les dashboards semblaient soulagés. Puis le téléphone d’astreinte a sonné à nouveau, plus fort, parce que la base de données était revenue vide. Le conteneur Postgres a redémarré avec un nouveau répertoire de données. L’application web a commencé à créer des comptes admin « premier utilisateur ». Ce n’était pas une nouvelle fonctionnalité.

La cause racine n’était pas Docker. La cause racine était l’hypothèse que les volumes étaient jetables. Dans cet environnement,
les volumes étaient le seul stockage durable. La correction a été procédurale et technique : étiqueter les volumes par usage, les mapper aux services, et protéger les volumes de production des opérations prune. Ils ont aussi mis en place des sauvegardes hôtes parce que compter sur « on ne lancera pas la mauvaise commande » n’est pas une stratégie.

La leçon durable : la commande de nettoyage la plus dangereuse est celle que vous exécutez quand vous êtes stressé et que vous pensez savoir ce qu’elle fait.

2) L’optimisation qui s’est retournée contre eux : « On va mettre en cache chaque build pour toujours »

Une autre équipe gérait un monorepo avec des builds Docker lourds. Ils ont activé BuildKit et poussé les performances :
cache mounts, builds multi-étapes et réutilisation agressive des couches. Leur CI est devenue plus rapide. Le résumé exécutif affichait de meilleures barres. Tout le monde félicitait le pipeline pour son optimisation.

Six semaines plus tard, la CI a commencé à échouer par intermittence. Pas des échecs cohérents — ceux-là sont trop gentils. Pulls d’images aléatoires échouaient.
Les builds échouaient en cours de route. Parfois apt ne pouvait pas écrire de fichiers temporaires. Parfois Docker ne pouvait pas créer de couches.
Les échecs se déplaçaient entre les runners. Les ingénieurs ont d’abord pointé du doigt le registre. Puis le réseau. Puis les rayons cosmiques.

Le vrai problème : les runners étaient « cattle », mais personne ne les re-imagéait. Le cache de build a grandi sans plafond.
Docker data root s’est rempli jusqu’à ce que le système de fichiers atteigne ses blocs réservés et que tout se dégrade. L’« optimisation »
n’était pas fausse ; elle était incomplète. Le travail de performance qui ignore la capacité devient du travail de fiabilité, que ça vous plaise ou non.

Ce qui s’est retourné contre eux était culturel : l’équipe croyait que le cache de build était uniquement bénéfique et inoffensif pour la production.
Ils traitaient le disque comme infini parce qu’il était invisible. Quand le disque s’est rempli, leurs « builds rapides » se sont arrêtés.

La correction a été ennuyeuse mais efficace : définir un calendrier de prune avec seuils, ajouter de la surveillance pour /var/lib/docker,
et re-imager périodiquement les runners. Ils ont conservé la plupart des gains de performance, et les échecs ont cessé d’être mystérieux.

3) La pratique ennuyeuse mais correcte qui a sauvé la mise : « Mesurer, puis pruner, puis vérifier »

Un service lié aux paiements tournait sur quelques hôtes Docker derrière un load balancer. L’équipe n’avait pas beaucoup de glamour, mais elle avait de la discipline. Chaque hôte avait un petit runbook : vérifier le disque, vérifier les compartiments Docker, vérifier les logs, et seulement ensuite supprimer. Ils avaient aussi la rotation des logs configurée par défaut.

Un week-end, le trafic a explosé à cause d’un problème chez un partenaire. Le service n’est pas tombé à cause du CPU ; il est tombé à cause des logs.
L’application a commencé à logger une erreur par requête. Ce type de bug n’est pas subtil : il convertit le trafic utilisateur en consommation disque à grande échelle.

L’ingénieur d’astreinte a vu la courbe disque monter. Avant que ça atteigne 100%, il a limité la fuite en déployant un changement de config qui réduisait la verbosité des logs. Parce que les logs Docker étaient rotatés, l’hôte n’est pas mort. Ils ont ensuite utilisé docker system df
et ont pruné le cache de build et les anciennes images de manière contrôlée, avec des filtres temporels, à travers la flotte.

Il n’y a pas eu de commande héroïque à minuit. Pas de fichiers édités à la main dans /var/lib/docker. Juste une réponse mesurée qui a gardé de la marge et rétabli le fonctionnement normal. La pratique ennuyeuse et correcte a encore une fois sauvé la mise.

Stratégie de nettoyage sûre qui fonctionne en production

Le bon plan de nettoyage dépend du type d’hôte. Un runner CI n’est pas la même chose qu’un hôte de base de données.
Un laptop de dev n’est pas la même chose qu’un nœud de production. L’astuce est d’adapter la rétention au rôle.

Définir ce qui ne doit jamais être supprimé automatiquement

Dans la plupart des environnements de production, voici les « classes protégées » :

  • Volumes nommés contenant des données (bases de données, queues, uploads).
  • Images nécessaires au rollback (au moins une version précédente).
  • Preuves pendant un incident (containeurs arrêtés que vous comptez inspecter).

Si vous ne pouvez pas lister cela pour votre environnement, vous n’êtes pas prêt pour un pruning agressif. Commencez par un prune filtré par temps qui ne touche pas les volumes.

Utiliser des filtres temporels pour réduire les surprises

Le meilleur truc pour un nettoyage plus sûr est --filter until=.... Cela crée une zone tampon : « ne supprime rien créé récemment. »
Cette zone protège les déploiements en cours, les fenêtres de rollback, et le fait que les humains oublient ce qu’ils ont lancé mardi dernier.

Choisir une posture de nettoyage selon le rôle de l’hôte

Posture pour runner CI

  • Prunez le cache de build de façon agressive (quotidiennement ou au seuil).
  • Prunez les images plus vieilles qu’une courte fenêtre (quelques jours), car elles sont reproductibles.
  • Privilégiez le reimage périodique des runners ; c’est le ramasse-miettes le plus propre.
  • Gardez les volumes au minimum ; considérez tout volume comme suspect.

Posture pour hôte applicatif en production

  • Activez la rotation des logs dans la config du daemon Docker.
  • Utilisez un system prune filtré par temps sans volumes, sur un planning.
  • Conservez des images de rollback pour une fenêtre définie.
  • Alertez sur l’utilisation disque et sur la croissance du Docker root.

Posture pour hôte stateful (bases de données sur Docker)

  • Ne pruniez jamais les volumes automatiquement. Jamais. Si vous devez le faire, utilisez des allowlists explicites.
  • Planifiez la capacité de croissance des volumes. « Nettoyage Docker » n’est pas une politique de rétention de base de données.
  • Utilisez des sauvegardes et des restaurations testées. Les erreurs de nettoyage ici sont limitantes de carrière.

Blague #2 : Si vous exécutez des bases de données dans Docker et que votre plan de sauvegarde est « on fera attention », vous n’êtes pas attentifs — vous êtes optimistes.

Erreurs courantes : symptôme → cause racine → correctif

1) Symptôme : Disque plein, mais docker system df n’affiche pas assez d’utilisation

Cause racine : L’espace est dans des chemins non-Docker (logs système, core dumps), ou la racine Docker n’est pas là où vous pensez, ou le système de fichiers est à court d’inodes.

Correctif : Vérifiez df -h et df -i. Trouvez les répertoires les plus volumineux avec sudo du -xhd1 /var (et autres mounts). Confirmez la racine Docker avec docker info.

2) Symptôme : Les fichiers /var/lib/docker/containers/*-json.log sont énormes

Cause racine : Pas de rotation configurée pour le logger json-file de Docker, ou l’application logge trop.

Correctif : Configurez les options de log dans daemon.json ; réduisez la verbosité applicative ; poussez les logs hors hôte si nécessaire. Tronquez uniquement en cas de break-glass.

3) Symptôme : Le nettoyage ne libère pas d’espace immédiatement

Cause racine : Des fichiers supprimés sont encore détenus ouverts par un processus (commun pour les logs), ou des blocs réservés du système de fichiers, ou des particularités de thin-provisioning sur certains drivers.

Correctif : Identifiez les fichiers supprimés encore ouverts avec lsof (non montré dans les tâches, mais c’est la méthode). Redémarrez le processus si c’est sûr. Vérifiez l’espace libéré réel avec df, pas avec l’espoir.

4) Symptôme : Vous avez pruné des images et maintenant un déploiement échoue à pull/build

Cause racine : Vous avez supprimé des images locales utilisées pour des rollouts rapides, ou votre registre/chemin réseau est plus lent/peu fiable, ou vous avez pruné trop agressivement pendant une fenêtre de déploiement.

Correctif : Utilisez des filtres temporels ; coordonnez les horaires de prune en dehors des fenêtres de déploiement ; conservez une fenêtre de rollback d’images ; assurez l’accès robuste au registre.

5) Symptôme : Le disque grossit en continu même si vous prunez hebdomadairement

Cause racine : La croissance se trouve dans les volumes (état) ou à l’intérieur des couches inscriptibles des conteneurs (apps écrivant des données temporaires), pas dans les images/cache.

Correctif : Mesurez la taille des volumes ; corrigez les chemins applicatifs pour utiliser des volumes/tmpfs ; implémentez la rétention au niveau applicatif/données.

6) Symptôme : Après docker system prune, un service perd des données

Cause racine : Les volumes ont été prunés (--volumes) ou un volume « nommé » était en réalité jetable mais non sauvegardé ; pas de garde-fous.

Correctif : N’utilisez jamais --volumes en production sans cartographie explicite et sauvegardes. Utilisez des labels, des inventaires, et des backups avec tests de restauration.

7) Symptôme : L’hôte est sain, mais les opérations Docker échouent avec « no space left on device »

Cause racine : Le filesystem Docker (ou métadonnées du driver de stockage) est contraint, ou les inodes sont épuisés, ou le mount /var est plein alors que / ne l’est pas.

Correctif : Confirmez les mounts ; vérifiez les inodes ; assurez-vous que la racine Docker est sur un filesystem suffisamment grand ; envisagez de déplacer Docker data root.

Listes de contrôle / plan pas-à-pas

Checklist d’urgence (disque > 95%, impact en production)

  1. Arrêter l’hémorragie : identifiez les écrivains incontrôlés (logs, fichiers temporaires). Si ce sont des logs, réduisez la verbosité et faites une rotation/tronquage en mode break-glass.
  2. Gagner de la marge : pruner les conteneurs arrêtés, puis le cache de build, puis les images inutilisées avec un filtre temporel.
  3. Vérifier : confirmez avec df -h que vous avez de l’espace libre réel à nouveau.
  4. Stabiliser : ajoutez la rotation des logs Docker si elle manque. Planifiez des travaux de suivi.

Routine de nettoyage sûre pour la production (hebdomadaire ou quotidienne selon le churn)

  1. Mesurer : docker system df et enregistrez-le (même dans un ticket). Les tendances valent mieux que les suppositions.
  2. Pruner : docker system prune --force --filter 'until=168h' (sans volumes).
  3. Pruner le cache de build séparément si build-intensive : docker builder prune --force --filter 'until=168h'.
  4. Revoir : lister les images principales et confirmer que la politique de rétention correspond à la réalité.
  5. Vérifier : contrôler df -h et les seuils d’alerte.

Plan de durcissement « Ne me réveillez plus »

  1. Activer la rotation des logs au niveau du daemon Docker.
  2. Alertes disque (utilisation des systèmes de fichiers) et sur la croissance du Docker root.
  3. Définir des politiques pour les runners CI : reimage périodique + prune agressif du cache de build.
  4. Étiqueter et inventorier les volumes : savoir lesquels sont des données et lesquels sont jetables.
  5. Rendre le rollback explicite : garder les N dernières images de déploiement, pruner le reste par temps.
  6. Planifier la capacité des volumes stateful : si la DB grossit, c’est une décision produit, pas une décision Docker.

FAQ

1) Est-ce que docker system prune est sûr en production ?

Avec des garde-fous : oui, souvent. Utilisez --filter until=... et n’ajoutez pas --volumes à moins d’en être très sûr.
Le docker system prune par défaut supprime les conteneurs arrêtés, les réseaux inutilisés, les images pendantes, et le cache de build.

2) Quelle est la différence entre images pendantes et images inutilisées ?

Les images « pendantes » sont typiquement des couches sans tag laissées par des builds. « Inutilisées » signifie non référencées par aucun conteneur.
docker image prune cible par défaut les pendantes ; docker image prune -a cible aussi les images inutilisées.

3) Pourquoi Docker garde des images que je n’ai pas utilisées depuis des semaines ?

Parce que Docker ne connaît pas votre intention. Les images peuvent être conservées pour des rollbacks rapides ou de futurs démarrages. Si vous voulez de la rétention,
vous devez la définir : filtres temporels, prune planifié, ou politiques CI.

4) Puis-je supprimer des fichiers directement sous /var/lib/docker pour gagner de l’espace ?

Ne le faites pas. Vous allez désynchroniser les métadonnées de Docker avec la réalité et créer des pannes plus difficiles à diagnostiquer. Utilisez les commandes Docker.
L’exception que l’on prend parfois en urgence est la troncature de logs, et même cela doit être suivie d’une rotation correcte.

5) Pourquoi le prune n’a pas libéré d’espace même s’il a indiqué des GB récupérés ?

Fréquemment parce que des fichiers supprimés sont encore détenus ouverts, surtout des logs. Le système de fichiers ne récupérera pas l’espace tant que le dernier handle est ouvert. Vérifiez aussi que vous regardez le bon mount et que les inodes ne sont pas le facteur limitant.

6) Dois-je utiliser docker volume prune ?

Seulement quand vous comprenez exactement ce que « volume inutilisé » signifie dans votre environnement. Les volumes non référencés actuellement par un conteneur sont considérés inutilisés — même s’ils contiennent votre seule copie de données importantes.

7) Quelle est une bonne rotation de logs par défaut pour Docker ?

Pour beaucoup de services : max-size autour de 50–100MB et max-file de 3–10. Ajustez selon vos besoins d’intervention. Si les logs sont importants, expédiez-les ailleurs. Si les logs sont du spam, corrigez d’abord le spam.

8) Pourquoi le cache de build devient-il si gros sur la CI ?

La CI produit beaucoup de couches uniques : commits différents, dépendances différentes, cache mounts, et builds parallèles.
Sans prune ni reimage, le cache ne fait que croître. Il ne vieillit pas automatiquement.

9) Est-il mieux de pruner régulièrement ou de re-imager les hôtes ?

Pour les runners CI : reimager est excellent car garantit une baseline propre. Pour les hôtes de production stables : un prune régulier et conservateur plus de la surveillance est généralement meilleur que des reimages fréquentes. Faites les deux quand c’est pertinent.

10) Combien d’espace libre devrais-je conserver sur un hôte Docker ?

Gardez suffisamment de marge pour votre plus grosse pull/build attendue plus les pics opérationnels (logs, rafales de déploiement).
Pratiquement : visez au moins 15–25% de libre sur le filesystem qui héberge la racine Docker, davantage sur les nœuds tournant beaucoup de builds.

Prochaines étapes réalisables cette semaine

Si vous voulez moins de surprises disque-plein, faites ceci dans l’ordre :

  1. Activez la rotation des logs Docker dans /etc/docker/daemon.json et redémarrez Docker pendant une fenêtre de maintenance.
  2. Ajoutez docker system df à votre routine de monitoring : suivez la croissance des images, volumes et cache de build au fil du temps.
  3. Adoptez le prune filtré par temps sur un planning : commencez par until=168h et ajustez selon le rythme des déploiements.
  4. Rendez les volumes explicites : sachez lesquels sont des données, qui les possède, et comment ils sont sauvegardés.
  5. Corrigez les vrais écrivains : si votre appli écrit d’énormes données temporaires sur le FS du conteneur ou logge sans arrêt, le nettoyage n’est qu’une réunion récurrente sur le même problème.

Le disque est bon marché. Les incidents ne le sont pas. Nettoyez Docker comme si vous gériez un système de production : mesurez d’abord, supprimez ensuite, vérifiez toujours.

← Précédent
Interface de notifications Toast avec CSS : Empilement, Animations, Variantes de placement
Suivant →
Vitesse de restauration Proxmox : réglages PBS, choix de compression et pourquoi les restaurations sont lentes

Laisser un commentaire