Si vous utilisez Docker Desktop sur Windows avec WSL2 et que vos conteneurs donnent l’impression de travailler dans du ciment mouillé, vous ne vous trompez pas. « npm install » prend un temps géologique. Les watcheurs de fichiers ratent des changements ou consument le CPU. Un simple git status peut vous faire remettre en question vos choix de carrière.
Cela se corrige. Mais pas avec des approximations, des bidouilles du registre trouvées sur un forum, ou en cochant des cases au hasard jusqu’à ce que le ventilateur cesse de hurler. Vous le corrigez en identifiant quel sous‑système est réellement lent : la frontière du système de fichiers, le disque VHDX, l’ordonnancement CPU, la pression mémoire, le DNS, ou le comportement du cache de build.
Plan de diagnostic rapide
La plupart des équipes gaspillent des jours à « tuner Docker » alors que le goulot d’étranglement est une frontière unique : fichiers Windows sous /mnt/c, un montage bind dans un conteneur, ou un VHDX qui a gonflé et effectue maintenant des E/S tristes. Voici le chemin rapide vers la vérité.
Première étape : déterminer où se trouvent les fichiers lents
- Si votre repo est sous
/mnt/c(ou n’importe quel/mnt/<drive>) : considérez les E/S fichiers comme le goulot d’étranglement jusqu’à preuve du contraire. - Si votre repo est à l’intérieur du système de fichiers Linux (
~dans WSL2) : les E/S fichiers sont généralement correctes ; vérifiez ensuite la pression CPU/mémoire ou les schémas de montage de conteneur.
Deuxième étape : identifier la « classe d’opération lente »
- Beaucoup de petits fichiers (node_modules, répertoires vendor, monorepos) : les bind mounts et l’interop Windows vont nuire.
- Workloads de bases de données (Postgres, MySQL, Elasticsearch) : I/O aléatoire + fsync + problèmes de thin provisioning.
- Workloads de build (builds Docker) : emplacement du cache et transfert du contexte dominent.
- Workloads réseau (pull d’images, registres privés) : DNS et config de proxy, pas la « performance Docker » en soi.
Troisième étape : exécutez trois tests pour localiser le goulot
- Test du système de fichiers WSL : créez et faites stat d’une pile de fichiers sous
~dans WSL2. - Test
/mnt: répétez sous/mnt/c. - Test de montage dans un conteneur : exécutez le même test dans un conteneur sur un bind mount vs un volume nommé.
Si /mnt/c est dramatiquement plus lent que ~, stoppez. Déplacez le repo. Si les bind mounts de conteneur sont plus lents que les volumes nommés, stoppez. Changez la stratégie de montage. Si les deux sont corrects, vous êtes probablement limité par le CPU/mémoire ou par le DNS.
Quatrième étape : vérifiez la pénurie de ressources
Si WSL2 est sous‑dimensionné (ou sur‑dimensionné au point d’affamer Windows), tout devient instable : watchers de fichiers, compilateurs, bases de données. Corrigez les limites mémoire et le swap, puis retestez.
Pourquoi c’est lent : l’architecture réelle et ses pièges
Docker Desktop sur Windows avec WSL2 n’est pas « Docker qui tourne sur Windows ». C’est Docker qui tourne dans une VM Linux avec beaucoup de plomberie pour donner l’impression d’être natif. C’est dans cette plomberie que la performance se met à faire des hobbies.
À haut niveau :
- WSL2 est une VM légère utilisant Hyper‑V en coulisse.
- Votre distribution Linux (Ubuntu, Debian, etc.) vit sur un système de fichiers ext4 à l’intérieur d’un VHDX.
- Docker Desktop exécute typiquement le démon dans une distribution WSL2 spéciale (souvent
docker-desktop). - Quand vous accédez à des fichiers Windows depuis WSL2 (comme
/mnt/c/Users/...), vous traversez une frontière implémentée par une couche de système de fichiers spéciale. - Quand vous bind‑montez un chemin Windows dans un conteneur Linux, vous traversez souvent cette frontière à nouveau, plus les sémantiques d’overlay et de montage de conteneur.
Les problèmes de performance proviennent généralement de l’un de ces modes d’échec :
- Surcharge du système de fichiers d’interop : les outils Linux attendent des opérations de métadonnées bon marché. Les fichiers Windows accessibles via la couche de montage de WSL peuvent rendre les
stat()et les parcours de répertoire coûteux. - Trop de fichiers : des millions de petits fichiers stressent toute couche de partage inter‑OS.
- Comportement de l’image disque : le VHDX grossit facilement ; le réduire n’est pas trivial ; la fragmentation et la disposition de l’espace libre comptent plus que vous ne voulez.
- Pression mémoire : WSL2 cache agressivement ; sous pression, vous obtenez des tempêtes d’éviction et des moments « pourquoi tout swappe ? ».
- Transfert du contexte de build : quand vous build depuis un chemin Windows, Docker peut passer un vrai temps à envoyer votre contexte dans la VM.
- Inotify et watchers : certains outils reposent sur des événements filesystem ; traverser des frontières peut casser la sémantique ou déclencher du polling.
Une citation pour rester lucide : « L’espoir n’est pas une stratégie. »
— idée paraphrasée attribuée au général Gordon R. Sullivan, souvent répétée en fiabilité. Remplacez « espoir » par « bascules aléatoires », et vous êtes prêts pour la production.
Faits et contexte historique (court, utile)
- WSL1 vs WSL2 était un compromis lié au système de fichiers : WSL1 traduisait les appels système Linux vers Windows ; WSL2 est passé à un vrai noyau Linux dans une VM. Beaucoup de choses sont devenues plus compatibles, mais l’accès inter‑systèmes est devenu plus critique.
- Docker Desktop utilisait Hyper‑V avant que l’intégration WSL2 mûrisse : les premiers setups Windows exécutaient des VMs LinuxKit ; WSL2 a réduit la surcharge et amélioré l’intégration, sans éliminer les frontières de VM.
- ext4 dans un VHDX est rapide… jusqu’à ce que vous le rendiez compliqué : il se comporte comme un système Linux normal, mais le fichier VHDX sous‑jacent a son propre cycle de vie (croissance, fragmentation, compactage).
- Les workloads riches en métadonnées sont la douleur commune : les gestionnaires de paquets et toolchains linguistiques effectuent énormément de
stat(),readdir()et d’écritures de courte durée. - Les sémantiques de partage de fichiers diffèrent : sensibilité à la casse, bits exécutables, symlinks et mapping des permissions ne sont pas gratuits. « Ça marche » ne veut pas dire « ça marche vite ».
- BuildKit a changé la donne pour les builds : les builds Docker modernes peuvent mettre en cache plus intelligemment, mais seulement si vous cessez de saboter le cache avec des contextes énormes et des timestamps instables.
- Les volumes nommés ne sont pas réservés à la production : ils évitent certaines couches de partage hôte‑chemin lentes et peuvent être bien plus rapides pour les répertoires chauds.
- Le réseau WSL2 est virtualisé : DNS et routage peuvent différer de Windows, et les VPN d’entreprise adorent s’immiscer.
Faites ces tâches d’abord : 12+ vérifications par commandes avec prises de décision
Ce sont de vraies tâches que vous pouvez exécuter. Chacune inclut : commande, sortie d’exemple, ce que ça signifie, et quelle décision prendre.
Tâche 1 : Confirmez où se trouve réellement votre repo
cr0x@server:~$ pwd
/home/cr0x/work/myapp
Signification : Ceci est à l’intérieur du système de fichiers Linux (bon). Si à la place vous voyez /mnt/c/..., c’est un suspect principal.
Décision : Si votre projet est sur /mnt/c, planifiez de le déplacer dans ~ et utilisez un éditeur qui supporte l’accès distant WSL.
Tâche 2 : Comparez les performances sur petits fichiers : home WSL vs /mnt/c
cr0x@server:~$ mkdir -p ~/io-test && cd ~/io-test && time bash -lc 'rm -rf a && mkdir a && for i in $(seq 1 20000); do echo x > a/f$i; done; find a -type f | wc -l'
20000
real 0m3.412s
user 0m1.102s
sys 0m2.101s
cr0x@server:~$ mkdir -p /mnt/c/Users/Public/io-test && cd /mnt/c/Users/Public/io-test && time bash -lc 'rm -rf a && mkdir a && for i in $(seq 1 20000); do echo x > a/f$i; done; find a -type f | wc -l'
20000
real 1m12.883s
user 0m2.004s
sys 0m8.991s
Signification : Si votre exécution sur /mnt/c est un ordre de grandeur plus lente, vous avez trouvé le problème principal.
Décision : Gardez le code source et les répertoires de dépendances dans le système de fichiers WSL. Considérez /mnt/c comme un montage de commodité, pas une usine de build.
Tâche 3 : Vérifiez le contexte Docker et les détails du moteur
cr0x@server:~$ docker context ls
NAME DESCRIPTION DOCKER ENDPOINT
default * Current DOCKER_HOST based configuration npipe:////./pipe/docker_engine
Signification : Sur Windows, le CLI peut parler à Docker Desktop via un named pipe Windows, même si le moteur tourne dans WSL2.
Décision : Si vous mélangez les CLI Windows et WSL, choisissez un workflow. Préférez exécuter le CLI Docker dans WSL pour moins de traversées de frontières.
Tâche 4 : Vérifiez que vous construisez depuis des chemins Linux (le contexte compte)
cr0x@server:~$ docker build -t myapp:ctxcheck .
[+] Building 9.6s (10/10) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> [internal] load .dockerignore 0.0s
=> [internal] load metadata for docker.io/library/node:20-alpine 0.9s
=> [internal] load build context 6.8s
=> => transferring context: 412.34MB 6.6s
...
Signification : « transferring context » qui prend des secondes est normal pour de gros contextes ; si c’est des dizaines de secondes, vous payez une taxe de frontière ou vous envoyez trop.
Décision : Ajoutez un vrai .dockerignore et build depuis WSL. Si votre contexte est énorme, arrêtez d’envoyer node_modules au démon.
Tâche 5 : Vérifiez si BuildKit est activé (il le devrait)
cr0x@server:~$ docker buildx version
github.com/docker/buildx v0.12.1
Signification : Buildx existe ; BuildKit est disponible.
Décision : Utilisez les fonctionnalités BuildKit comme les cache mounts pour les gestionnaires de paquets. Si vous êtes encore sur le builder legacy, attendez‑vous à des douleurs.
Tâche 6 : Mesurez la performance bind mount vs volume nommé dans un conteneur
cr0x@server:~$ docker volume create voltest
voltest
cr0x@server:~$ mkdir -p ~/bindtest
cr0x@server:~$ time docker run --rm -v ~/bindtest:/work alpine sh -lc 'cd /work; rm -rf a; mkdir a; for i in $(seq 1 20000); do echo x > a/f$i; done; find a -type f | wc -l'
20000
real 0m18.911s
user 0m0.211s
sys 0m0.404s
cr0x@server:~$ time docker run --rm -v voltest:/work alpine sh -lc 'cd /work; rm -rf a; mkdir a; for i in $(seq 1 20000); do echo x > a/f$i; done; find a -type f | wc -l'
20000
real 0m4.022s
user 0m0.203s
sys 0m0.380s
Signification : Si les volumes nommés sont beaucoup plus rapides, votre chemin de bind mount est le goulot.
Décision : Placez les répertoires chauds (deps, caches, données DB) sur des volumes nommés. Bind mount uniquement le code que vous modifiez activement.
Tâche 7 : Vérifiez la mémoire, le swap et la pression WSL
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 7.6G 6.8G 220M 120M 650M 390M
Swap: 2.0G 1.4G 600M
Signification : Vous êtes proche du bord. Peu d’« available » et swap actif signifie que vous aurez des pics de latence partout.
Décision : Augmentez la mémoire WSL2, réduisez le parallélisme, ou arrêtez d’exécuter cinq bases de données plus un navigateur plus Teams. Choisissez deux.
Tâche 8 : Identifiez les E/S chaudes et les processus dans WSL
cr0x@server:~$ sudo apt-get update -qq && sudo apt-get install -y -qq iotop
...
cr0x@server:~$ sudo iotop -o -b -n 3
Total DISK READ: 12.34 M/s | Total DISK WRITE: 45.67 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
2421 be/4 postgres 0.00 B/s 9.12 M/s 0.00 % 85.12 % postgres: checkpointer
9911 be/4 cr0x 0.00 B/s 5.44 M/s 0.00 % 62.10 % node dev-server
Signification : Vous pouvez voir qui martèle le disque. Un checkpointer Postgres effectuant des écritures soutenues signifie souvent fsync + paramètres de checkpoint, ou un décalage workload/infrastructure.
Décision : Si les bases de données dominent, déplacez leurs répertoires de données sur des volumes nommés et révisez les réglages DB pour un usage dev (compromis sécurité/latence).
Tâche 9 : Vérifiez les options de montage du système de fichiers pour /mnt/c (comportement des métadonnées)
cr0x@server:~$ mount | grep -E ' /mnt/c '
C: on /mnt/c type 9p (rw,noatime,dirsync,aname=drvfs;path=C:\;uid=1000;gid=1000;metadata;symlinkroot=/mnt/)
Signification : Les options drvfs/9p impactent le comportement. metadata peut améliorer la sémantique des permissions mais ajouter du surcoût ; dirsync est souvent cher.
Décision : Ne faites pas de tuning aveugle des flags de montage. Si votre repo est sur /mnt/c, la solution est de le déplacer. Si vous devez rester, envisagez d’ajuster les options d’automontage, mais traitez‑le comme un dernier recours.
Tâche 10 : Vérifiez si votre VHDX est énorme et si vous le payez
cr0x@server:~$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sdd 251G 201G 38G 85% /
Signification : Une forte utilisation et un grand système de fichiers ext4 peuvent corréler avec des opérations plus lentes, surtout si vous brassez constamment des dépendances.
Décision : Nettoyez les caches, prunez les artéfacts Docker, et envisagez de compacter/réduire le VHDX périodiquement (avec précaution et backups).
Tâche 11 : Mesurez le comportement DNS depuis un conteneur
cr0x@server:~$ docker run --rm alpine sh -lc 'apk add -q bind-tools; time nslookup registry-1.docker.io'
real 0m1.882s
user 0m0.012s
sys 0m0.008s
Server: 192.168.65.1
Address: 192.168.65.1:53
Non-authoritative answer:
Name: registry-1.docker.io
Address: 54.161.123.11
Signification : Des résolutions DNS de plusieurs secondes feront « sentir » les pulls/builds lents même si le disque va bien.
Décision : Si le DNS est lent, corrigez le DNS (génération de resolv.conf WSL, DNS d’entreprise, split tunneling VPN). Ne blâmez pas overlay2 pour votre résolveur.
Tâche 12 : Confirmez que vous n’avez pas une inflation pathologique d’images/volumes
cr0x@server:~$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 42 8 18.4GB 12.7GB (68%)
Containers 19 2 1.2GB 900MB (75%)
Local Volumes 31 6 54.0GB 40.5GB (75%)
Build Cache 0 0 0B 0B
Signification : Des volumes occupant 54GB n’est normal que si vous utilisez de vrais jeux de données. En dev, c’est souvent des bases abandonnées et des caches.
Décision : Prunez ce que vous pouvez, mais avec intention. Si des volumes contiennent un état important, migrez ou sauvegardez avant de tout supprimer.
Tâche 13 : Vérifiez si un conteneur effectue trop de fsync (bases de données)
cr0x@server:~$ docker exec -it my-postgres sh -lc 'psql -U postgres -c "SHOW synchronous_commit; SHOW fsync;"'
synchronous_commit
-------------------
on
(1 row)
fsync
-------
on
(1 row)
Signification : En dev, synchronous_commit=on et fsync=on est sûr mais peut être lent sur des stockages virtualisés.
Décision : Pour la vitesse en dev uniquement, envisagez d’assouplir ces réglages (en acceptant une perte de données en cas de crash). Pour tout ce qui ressemble à un test production, gardez les valeurs par défaut et optimisez plutôt le chemin de stockage.
Tâche 14 : Repérez les watchers qui retombent sur du polling (consommation CPU)
cr0x@server:~$ ps aux | grep -E 'watch|chokidar|webpack|nodemon' | head
cr0x 18821 65.2 3.1 1245320 251212 ? Sl 10:11 5:44 node node_modules/.bin/webpack serve
cr0x 18844 22.7 1.2 981244 96120 ? Sl 10:11 2:03 node node_modules/chokidar/index.js
Signification : Un CPU élevé provenant des watchers signifie souvent qu’ils ne reçoivent pas les événements natifs et scannent en boucle.
Décision : Gardez les répertoires surveillés sur ext4 dans WSL, pas sur /mnt/c. Réduisez la portée des watchers. Préférez des configurations d’outils qui utilisent inotify efficacement.
Corrections qui améliorent vraiment
1) Placez votre code dans le système de fichiers WSL. Vraiment.
C’est le correctif à plus fort ROI pour les workloads développeur riches en petits fichiers. Gardez les repos sous ~/src dans WSL. Accédez‑y depuis Windows via des outils compatibles WSL plutôt que l’inverse.
- À faire : exécutez
git clonedans WSL. - À éviter : garder le repo sur NTFS et « simplement le monter ». C’est ainsi que vous obtenez des parcours de répertoire à 70 secondes.
Blague #1 : Si vous insistez pour exécuter node_modules depuis /mnt/c, vous ne faites pas de tuning — vous rejouez un film catastrophe au ralenti.
2) Utilisez des volumes nommés pour les données chaudes, bind mounts pour la boucle d’édition
Le modèle mental qui vous sauve : les bind mounts servent pour le code source ; les volumes nommés servent pour le churn. Bases de données, caches de gestionnaires de paquets, répertoires de dépendances, tout ce qui crée des milliers de fichiers rapidement — donnez‑leur un volume.
Stratégie d’exemple :
- Bind mount :
./app:/app - Volume nommé :
node_modules:/app/node_modules - Volume nommé :
pgdata:/var/lib/postgresql/data
Pourquoi ça aide : les volumes nommés vivent directement dans la couche système de fichiers de la VM Linux, évitant les couches de partage hôte lentes.
3) Cessez d’envoyer des déchets dans votre contexte de build Docker
Si votre contexte de build fait des centaines de Mo, vous payez pour un tarball + transfert + extraction avant que la première ligne du Dockerfile n’importe. Votre .dockerignore doit être agressif : ignorez les répertoires de dépendances, outputs de build, artefacts de tests, caches locaux.
La plupart des équipes font cela à moitié : elles ignorent node_modules mais oublient .git, dist, .next, coverage, et caches spécifiques aux langages. Puis se demandent pourquoi « load build context » domine.
4) Utilisez les cache mounts BuildKit pour les gestionnaires de paquets
BuildKit peut mettre en cache des répertoires entre builds sans les intégrer dans des couches. Cela signifie des rebuilds plus rapides et des images plus petites. Si vous faites des builds répétés sur WSL2, ça compte.
Patrons d’exemple (conceptuels) : mettez en cache le cache de téléchargement du gestionnaire de paquets, pas la sortie de l’application. Gardez le build déterministe.
5) Gardez le CLI Docker dans WSL quand c’est possible
Utiliser le CLI Windows pour contrôler un démon dans WSL2 n’est pas toujours terrible, mais ajoute des frontières, des différences d’environnement et de la confusion de chemins. Exécuter le CLI dans WSL réduit les frictions et rend les chemins cohérents.
6) Dimensionnez correctement les ressources WSL2 (et n’affamez pas Windows)
WSL2 va volontiers manger de la RAM pour du cache, puis la relâcher… parfois… éventuellement. Si vous ne faites rien, vous pourriez avoir de bonnes performances jusqu’au jour où non. Ajoutez une politique de ressources pour que votre laptop reste un laptop.
Stratégie typique : plafonner la mémoire, définir un swap raisonnable, et éviter de donner à WSL tous les cœurs si Windows doit rester réactif.
7) Soyez délibéré sur l’antivirus et l’indexation
Quand Windows Defender (ou la protection d’entreprise) scanne les mêmes fichiers que vous frappez via l’interop WSL2, vous obtenez des lenteurs « mystérieuses ». Les exclusions peuvent aider, mais faites‑les avec votre équipe sécurité, pas dans la panique à 2 h du matin.
8) Traitez les réglages de durabilité des bases comme un contrat dev/prod
Si vous assouplissez la durabilité (fsync off, commit asynchrone), votre DB de dev sera rapide et aussi susceptible de perdre des données lorsque la VM éternue. C’est acceptable pour du dev local ; inacceptable pour des tests d’intégration qui prétendent reproduire la prod.
Décidez ce que vous faites, puis configurez en conséquence.
9) Corrigez le DNS si les pulls et connexions sont lents
Dans les réseaux d’entreprise, le DNS peut être le méchant caché. WSL2 a son propre setup de résolveur, Docker a son DNS interne, et les clients VPN aiment patcher les choses à chaud.
Règle : si nslookup est lent dans un conteneur, corrigez d’abord le DNS. Ne touchez pas aux réglages de stockage tant que la résolution de noms n’est pas sous 100ms pour les domaines courants.
10) Gardez les watchers là où inotify fonctionne
Les watchers de fichiers sont des multiplicateurs de performance : inotify signifie changements pilotés par événements ; le polling signifie « scanner la planète en boucle ». Placez les arbres surveillés sur ext4 dans WSL2, réduisez la portée et configurez vos outils pour éviter de surveiller les répertoires vendor.
11) Prunez et compactez intentionnellement, pas en rituel
Les artéfacts Docker s’accumulent. Les images disque WSL2 grossissent. Le pruning peut aider, mais le pruning irréfléchi détruit l’état et fait perdre du temps à reconstruire. Établissez un rythme : prune du cache de build hebdomadaire, nettoyage des volumes abandonnés mensuel, compactage des images disque quand elles ont massivement gonflé à cause du churn.
Blague #2 : « Docker prune » n’est pas une stratégie de performance ; c’est une confession que vous ne savez pas ce qui utilise le disque.
Trois mini‑histoires d’entreprise issues du terrain
1) Incident causé par une mauvaise hypothèse : « C’est sur mon SSD, donc c’est rapide »
Une équipe que j’ai accompagnée est passée de Mac à Windows pour des raisons d’achat. Docker Desktop + WSL2 semblait la manière la plus simple de garder leur workflow dev « identique ». Ils ont cloné des repos dans C:\Users\..., ouvert dans leur IDE Windows, et laissé WSL2 et les conteneurs faire le reste.
En une semaine, les symptômes étaient partout : tests qui timeout, mode watch qui manque les rebuilds, et une hausse de tickets « Docker est cassé ». Leurs logs étaient propres. Leurs conteneurs sains. Mais chaque dev décrivait la même chose : les petites opérations étaient lentes, et la lenteur devenait non linéaire, comme si ça empirait dans la journée.
L’hypothèse erronée était simple : « le code est sur un SSD, donc l’I/O fichier ne peut pas être le goulot. » Ils ont oublié que le code n’était pas accédé en NTFS brut par les processus Linux. Il était accédé via une frontière, avec traduction et sémantique de métadonnées collées au-dessus.
Nous avons exécuté un test simple : créer 20 000 petits fichiers sur /mnt/c et dans ~. Le delta était si grand que personne n’a discuté. Ils ont déplacé les repos dans WSL, utilisé l’intégration éditeur compatible WSL, et bind‑monté depuis le chemin Linux dans les conteneurs.
La partie dramatique n’était pas la correction. C’était la rapidité avec laquelle le récit « Docker est lent » a disparu une fois le workflow arrêté de faire faire des allers‑retours au système de fichiers Windows pour chaque stat().
2) Optimisation qui s’est retournée : « Mettons tout sur des volumes »
Une autre organisation a vu les bind mounts comme « la partie lente », ce qui est souvent vrai. Ils ont donc tout mis sur des volumes nommés, y compris le code source. Leur fichier Compose créait un volume par service, puis ils utilisaient docker cp pour synchroniser le code dans le volume, et lançaient l’app depuis là.
La perf s’est améliorée immédiatement. Les builds étaient plus rapides. Les installations Node ont cessé de timeout. On a fait des tours de victoire.
Puis le retour de bâton : l’expérience dev s’est dégradée. Les modifications incrémentales ne se propageaient pas toujours de façon prévisible. Le debug est devenu délicat parce que la source de vérité était désormais « code dans un volume », pas « code dans le repo ». Quelques devs avaient du code obsolète dans des volumes et ont passé des heures à chasser des bugs fantômes que « personne d’autre ne reproduit ».
Pire encore, l’analyse de sécurité et les outils de licence s’attendaient à inspecter le repo sur disque. Ils ont dû créer une nouvelle pipeline pour scanner le contenu des volumes, que personne ne voulait prendre en charge. La performance a gagné, mais le workflow est devenu fragile.
Le compromis stable était ennuyeux mais correct : bind mount le code (depuis ext4 WSL), garder les répertoires de churn (dépendances, caches, données DB) sur volumes nommés, et conserver une seule source de vérité — votre checkout git.
3) Pratique ennuyeuse mais correcte qui a sauvé la mise : « Mesurer la frontière, puis standardiser »
Une équipe plateforme supportant plusieurs équipes produit voyait des plaintes récurrentes : « Docker est lent sur Windows. » Ils auraient pu écrire une page wiki et prier. À la place, ils ont créé un script diagnostic minimal et l’ont intégré à l’onboarding.
Il faisait trois choses : un benchmark de création de petits fichiers sous ~ et sous /mnt/c, un benchmark de bind‑mount en conteneur, et un timing de lookup DNS. Il affichait les résultats avec des seuils et une action recommandée (« move repo », « use named volume », « check VPN DNS »).
Cette pratique n’était pas glamour. Mais elle a évité des mois de perte de productivité diffuse. Les nouveaux arrivants ont appris le workflow attendu dès le jour 1 : garder les repos dans WSL, utiliser des outils distants, ne pas bind‑monter des chemins Windows, ne pas blâmer Docker pour le DNS.
Quand des régressions de performance survenaient après des mises à jour Windows, ils avaient des résultats de référence pour comparer. C’est ainsi qu’on évite de traiter la performance comme du folklore.
Erreurs courantes : symptôme → cause racine → correction
1) « npm install prend une éternité »
Symptôme : les installations prennent des minutes ; le CPU est bas ; l’activité disque semble occupée mais pas saturée.
Cause racine : l’arbre de dépendances vit sur /mnt/c ou est bind‑monté depuis un chemin Windows ; les opérations de métadonnées sont chères.
Correction : déplacez le repo sur ext4 WSL ; mettez node_modules sur un volume nommé ; assurez‑vous que le contexte de build ignore les dépendances.
2) « Le watching de fichiers est peu fiable ou brûle le CPU »
Symptôme : hot reload manque des changements ; les ventilateurs tournent ; les watchers montrent un CPU élevé.
Cause racine : les événements inotify ne traversent pas la frontière ; l’outil retombe sur du polling de grands arbres.
Correction : gardez les répertoires surveillés dans le filesystem WSL ; réduisez la portée des watchers ; évitez de surveiller les répertoires de dépendances ; configurez les outils pour WSL.
3) « Docker build est lent même avec le cache »
Symptôme : « load build context » domine ; cache miss fréquent.
Cause racine : contexte énorme, mauvais .dockerignore, build depuis des chemins Windows ; timestamps ou fichiers générés polluent les couches.
Correction : .dockerignore agressif ; build depuis les chemins WSL ; utilisez BuildKit cache mounts ; stabilisez les entrées.
4) « La base de données dans un conteneur est douloureusement lente »
Symptôme : latence élevée sur des requêtes simples ; beaucoup d’écritures disque ; pauses occasionnelles.
Cause racine : le répertoire de données DB est sur un bind mount traversant la frontière Windows ; les réglages de durabilité amplifient le coût I/O ; la pression mémoire déclenche un churn de checkpoint.
Correction : stockez les données DB sur un volume nommé ; allouez plus de mémoire ; pour du dev uniquement, envisagez une durabilité assouplie (en connaissance de cause).
5) « Pull d’images lent ; builds bloqués sur apt/apk/npm »
Symptôme : étapes réseau qui stallent ; retries ; fonctionne sur Wi‑Fi maison, échoue sur VPN.
Cause racine : latence DNS ou résolveur cassé sous WSL2/Docker ; interactions proxy/VPN d’entreprise.
Correction : mesurez le DNS depuis un conteneur ; ajustez la configuration DNS ; coordonnez‑vous avec l’IT sur la politique split DNS/proxy.
6) « Tout allait bien, puis c’est devenu lent au fil des mois »
Symptôme : dégradation graduelle ; l’usage disque augmente ; le pruning « aide » temporairement.
Cause racine : croissance du VHDX, images/volumes accumulés, churn de dépendances ; possible fragmentation et faible espace libre.
Correction : nettoyez intentionnellement (volumes/images/cache de build) ; conservez de l’espace libre ; compactez le VHDX en maintenance programmée.
Listes de contrôle / plan étape par étape
Checklist A : Corriger le workflow dev (gains rapides)
- Déplacez les repos dans WSL :
~/srcest votre base. - Exécutez le CLI Docker dans WSL : réduisez la confusion de chemins et les traversées de frontières.
- Bind mount depuis des chemins WSL uniquement : ne bind‑montez jamais des chemins
C:\dans des conteneurs Linux si vous tenez à la vitesse. - Placez le churn sur des volumes : données DB, répertoires de dépendances, caches de paquets.
- Renforcez le
.dockerignore: réduisez le transfert de contexte et l’invalidation du cache. - Mesurez à nouveau : relancez les benchmarks de petits fichiers et montages pour prouver l’amélioration.
Checklist B : Stabiliser le comportement des ressources WSL2
- Choisissez un plafond mémoire qui garde Windows réactif et WSL productif.
- Définissez le swap intentionnellement (ni zéro, ni infini). Le swap cache des problèmes jusqu’à ce que non.
- Surveillez la mémoire « available » pendant les builds et tests ; évitez un swap soutenu.
- Arrêtez d’exécuter des stacks en double : ne faites pas tourner la même DB sous Windows et dans des conteneurs « au cas où ».
Checklist C : Rendre les builds rapides et prévisibles
- Activez BuildKit et utilisez les cache mounts quand approprié.
- Minimisez le contexte ; si le contexte est énorme, vous payez à chaque build.
- Séparez les dépendances du code applicatif dans votre Dockerfile pour maximiser les hits du cache.
- Ne bind‑montez pas les outputs de build vers Windows sauf si nécessaire ; gardez les artéfacts chauds dans des chemins Linux.
Checklist D : Debuggez la lenteur réseau sans deviner
- Mesurez le DNS dans un conteneur (
nslookuptiming). - Confirmez les réglages de proxy dans les étapes de build (proxy au build != proxy au runtime).
- Retestez VPN on/off pour isoler les effets réseau d’entreprise.
- Corrigez la configuration du résolveur avant de toucher aux réglages de stockage Docker.
FAQ
1) Dois‑je utiliser WSL2 ou le backend Hyper‑V pour Docker Desktop ?
Utilisez WSL2 sauf raison de compatibilité spécifique. Les grands problèmes de perf ne sont généralement pas « WSL2 vs Hyper‑V », mais « chemins Windows vs chemins Linux ».
2) Est‑ce sûr de garder mon repo dans WSL ? Vais‑je le perdre ?
C’est aussi sûr que tout environnement de dev local : sauvegardez et poussez vers le remote. L’ext4‑in‑VHDX de WSL est stable, mais ne traitez pas le « local only » comme un plan de durabilité.
3) Pourquoi /mnt/c est‑il tellement plus lent pour les outils dev ?
Parce que vous traversez une couche de traduction avec des sémantiques de métadonnées différentes. Les outils Linux font beaucoup de petits syscalls ; la frontière rend chacun plus coûteux.
4) Dois‑je désactiver Windows Defender ?
Non. Vous pourriez avoir besoin d’exclusions ciblées pour les répertoires de développement si votre politique de sécurité le permet. Coordonnez‑vous avec l’IT/sécurité ; la désactivation aléatoire crée des tickets d’incident intéressants.
5) Les volumes nommés sont‑ils toujours plus rapides que les bind mounts ?
Pas toujours. Les bind mounts depuis ext4 WSL peuvent très bien fonctionner. Les bind mounts depuis des chemins Windows sont souvent ceux qui posent problème. Les volumes nommés tendent à être régulièrement rapides pour les répertoires churn‑heavy.
6) Pourquoi le transfert de contexte de mon build Docker est‑il énorme ?
Parce que vous envoyez trop : répertoires de dépendances, outputs de build, parfois même tout l’historique .git. Corrigez le .dockerignore et build depuis les chemins WSL.
7) Ma base de données est lente dans un conteneur ; dois‑je juste l’installer sur Windows ?
Parfois c’est une solution pragmatique, mais ça augmente l’écart avec la production et complique les outils. Première tentative : volume nommé pour les données + mémoire adéquate + éviter les montages depuis des chemins Windows.
8) Pourquoi la performance se dégrade‑t‑elle avec le temps ?
Les images disque grossissent, les caches s’accumulent, les volumes s’entassent et l’espace libre diminue. De plus, les toolchains évoluent et génèrent plus de fichiers. Traitez le nettoyage et le compactage comme de la maintenance, pas comme un bouton de panique.
9) Dois‑je exécuter l’IDE sous Windows ou dans WSL ?
Les deux peuvent fonctionner, mais l’élément crucial est l’emplacement des fichiers. IDE Windows avec support WSL remote est un compromis courant : UI Windows, filesystem Linux.
10) « Désactiver le partage de fichiers » est‑ce une solution ?
C’est un moyen d’arrêter d’utiliser par accident des chemins lents. La vraie solution est d’organiser votre workflow pour ne pas dépendre du partage inter‑OS pour les chemins chauds.
Étapes suivantes
Si vous voulez que Docker sur Windows/WSL2 devienne raisonnable, faites ceci dans l’ordre :
- Exécutez les benchmarks (Tâche 2 et Tâche 6). Ne débattez pas ; mesurez.
- Déplacez le repo dans WSL si
/mnt/cest lent. Cela résout la plus grosse classe de problèmes. - Basculez les répertoires chauds vers des volumes nommés (données DB, répertoires de dépendances, caches).
- Corrigez le bloat du contexte de build et utilisez le cache BuildKit intentionnellement.
- Vérifiez le DNS si les étapes de pull/build stagnent ; corrigez le résolveur avant de manipuler le stockage.
- Dimensionnez correctement les ressources WSL et gardez de l’espace libre sain ; la performance aime avoir de la marge.
Faites cela, et le meme « Docker est lent sur Windows » redeviendra ce qu’il devrait être : une plainte occasionnelle, pas une caractéristique qui définit votre journée.