Se rendre au contenu

A Quarter of a Year with the Wrong Cause

An explanation fit every detail of the error and was still wrong. It lasted three months because it was never measured, only copied.
16 août 2026 par
A Quarter of a Year with the Wrong Cause
IT-Guy
Docker Dépannage Exploitation du serveur Assistant IA

Quart d'année durant, la mauvaise cause

Une explication correspondait à chaque détail de l'erreur et était pourtant fausse. Elle a persisté pendant trois mois, car elle n'a jamais été mesurée, mais seulement copiée.

M
Martin Schmid
2026-08-16

Un conteneur ne démarrait pas. Le log affichait une ligne, toujours la même :

exec /app/bin/boot: no such file or directory

Cela ressemble à un Dockerfile corrompu. Le fichier manque dans l'image, donc quelque chose ne va pas avec l'instruction COPY. On regarde le Dockerfile, on ne trouve rien, on examine le contexte de construction, on ne trouve toujours rien.

Puis on lance l'image avec /bin/ls au lieu de l'Entrypoint, et la réponse est : stat /bin/ls: no such file or directory. Ce n'est pas l'Entrypoint qui manque. Tout le système de fichiers manque. La construction Docker a rapporté un succès, un image de taille plausible a été généré, et cet image est vide à l'intérieur.

Pour cela, j'avais une explication depuis juillet. Elle correspondait à chaque détail et était pourtant fausse, et il m'a fallu un trimestre pour m'en rendre compte.

En juillet, l'explication était trop parfaite

À l'époque, le même symptôme a touché un serveur fraîchement installé. Docker avait récemment modifié la valeur par défaut : les nouvelles installations à partir de la version 29 stockent les images dans le store containerd au lieu du store classique. Et dans le bugtracker de Docker se trouvait un rapport d'erreur ouvert concernant exactement ce store, où l'export d'images s'interrompait lors de builds volumineux — moby/moby#52431, ouvert depuis avril.

La boucle se referma immédiatement. Nouveau serveur, nouveau store, erreur connue. Nous avons identifié le pilote classique dans la daemon.json, le avons reconstruit, et le problème a disparu.

Ce que je n'ai pas fait à l'époque : vérifier si l'explication était correcte. Elle correspondait, le problème a disparu, et l'affaire était close.

Comment une hypothèse devient un fait

Elle est écrite. D'abord en commentaire dans le script de configuration, juste au-dessus de la ligne qui installe l'ancien pilote. La même justification figurait ensuite dans les notes de projet, dans la documentation d'exploitation et dans plusieurs messages de commit.

Aucun de ces endroits n'était une mesure ; chacun était une copie de l'autre.

Cela apparaît le plus clairement dans une phrase de mes notes : La broche ne doit pas être retirée tant que le bug en amont n'est pas résolu. Cela sonne comme de la rigueur. Cela signifie en réalité que j'avais rendu l'état de mes propres serveurs dépendant d'un bugtracker étranger.

À cela s'ajoute mon outil. J'utilise Claude comme assistant IA, et il lit précisément ces fichiers. Lorsque le symptôme est revenu en août, il a suggéré un redémarrage du serveur, en s'appuyant de manière parfaitement justifiée sur les notes de juillet. Il n'a pas demandé si ces notes étaient exactes.

Ce n'est pas une erreur du modèle. Il restaure la cohérence avec ce qu'il trouve. Si ce qui est trouvé est faux, l'erreur n'est que plus soigneusement répartie.

En août, l'explication ne tenait plus la route.

Le serveur touché cette fois tournait depuis un mois sur l'ancien pilote. Précisément celui qui aurait dû empêcher le problème.

Ainsi, l'hypothèse était déjà pratiquement morte. Une protection qui n'empêche pas le problème n'en est pas une. Pourtant, j'aurais presque résolu l'affaire avec une nouvelle tentative d'explication plutôt qu'avec une mesure, car le redémarrage semblait plausible et le journal du kernel contenait des messages correspondants.

La question qui a résolu le nœud est venue d'un humain. Elle était formulée à peu près ainsi :« Ne pourrait-il pas s'agir d'un simple lapsus unique, et le nouveau magasin n'est-il pas depuis longtemps en ordre ? »

Une après-midi de mesure contre un trimestre de conjecture

La configuration était simple. Une instance jetable avec Docker 29.7.2, deux exécutions, chacune de cinq tours avec une image de 2,2 gigaoctets et 22 couches, construite à froid, à chaud et après la commande de nettoyage que notre outil de mise à jour exécute de toute façon.

Deux aspects étaient plus importants que la configuration elle-même.

Premièrement, j'avais défini les règles de décisionauparavant noté, y compris les résultats qui allaient contre moi. Si le nouveau store fonctionne sans erreur, le pin est retiré. Si les deux passes échouent, il n'a jamais été la solution. Celui qui définit la règle d'évaluation seulement après les chiffres trouvera toujours un résultat qui correspond à sa thèse.

Deuxièmement, il y avait un bras de contrôle. Le nouveau store aurait pu fonctionner proprement par accident.

Résultat : cinq sur cinq sans erreur sur le nouveau store, aucune notification noyau. Le bras de contrôle avec l'ancien pilote, également cinq sur cinq. Les deux stores génèrent des images utilisables, et les images vides ne viennent ni l'un ni l'autre.

Le pin est revenu, pour une autre raison.

Le lendemain, j'ai mesuré le cas d'usage réel au lieu d'une image de test : la compilation complète de notre application, avec des archives déjà présentes localement, comme en production. Trois cycles froids et deux cycles chauds par passage, même machine.

Froid : 14 secondes contre 37. L'export seul : 3,5 contre 19,7. Et la ligne qui a tranché : le build chaud, qui devrait durer quelques millièmes de seconde, a nécessité sur le nouveau store les mêmes 36 secondes que le build froid. Son cache de build ne survit pas à la commande de nettoyage que notre outil exécute après chaque mise à jour. C'est ma mesure sur une machine, pas une propriété documentée du produit.

Donc l'ancien pilote est de nouveau dans le script de configuration. Pour une raison mesurée, avec les chiffres à côté, et avec un interrupteur qui le désactive.

La cause initiale des images vides reste inconnue à ce jour. J'ai une hypothèse qui correspond aux horodatages, et je l'ai notée comme telle dans les notes, avec la commande à côté qui la confirmera ou l'infirmera lors de la prochaine occurrence.

C'est la seule différence par rapport à juillet.

Valeurs mesurées

Deux cycles sur deux jours, tous deux sur la même instance jetable : Debian 13, Docker 29.7.2. La partie supérieure répond à la question des images vides, la partie inférieure à celle de la vitesse. Seule la partie inférieure a rétabli le pin.

Mesure overlay2 (ancien pilote) containerd-Store
Images défectueuses, 5 cycles de 2,2 Go et 22 couches 0 sur 5 0 sur 5
Messages overlayfs dans le journal du noyau aucun aucun
Construction synthétique, temps total 20 s 36 s
Construction Odoo, à froid 14 s 37 s
dont l'étape d'exportation 3,5 s 19,7 s
Construction Odoo, à chaud après docker system prune -f 0–1 s 35–36 s

Le cycle synthétique a été exécuté le 14.08.2026 avec des données aléatoires non compressibles, ce qui signifie qu'il est presque entièrement constitué de l'étape d'exportation. La construction Odoo du 15.08.2026 correspond au cas d'usage réel : 394 archives de modules étaient déjà présentes localement, trois cycles à froid et deux cycles à chaud par exécution.

La dernière ligne est celle qui compte. Notre outil de mise à jour appelle docker system prune -f après chaque cycle, et le cache de construction du containerd-Store ne survit pas à cela — chaque mise à jour y serait une reconstruction complète.

Les chiffres des deux dernières et de la troisième dernière ligne doivent être lus ensemble : si les temps totaux avaient différé sans que l'étape d'exportation ne se déplace en même temps, le pilote de stockage n'aurait pas été la cause. Il se déplace avec, pour un facteur de 5,6.


Créé par Martin Schmid, avec le soutien de Claude Opus 5 et approuvé après vérification de contenu interne. S'appliquent nos indications et exclusion de responsabilité.

A Quarter of a Year with the Wrong Cause
IT-Guy 16 août 2026
Archives
5,000 Tokens Less, No Lines Deleted
A report recommended that I delete 80 percent of my Claude Code configuration. I tested it, read the transcript, and built something else.