Le problème se reconnaît à une boucle banale devenue pénible : modifier un contrôleur, rafraîchir, attendre huit secondes, relancer une commande, puis perdre encore du temps sans savoir si PHP, Docker ou la base ralentit. L’équipe finit par contourner la pile officielle, et le confort promis par la reproductibilité se transforme en configurations locales divergentes.
Le vrai enjeu est de réduire le temps entre une intention et un retour fiable, sans optimiser au hasard. Une pile locale rapide vient d’un budget explicite pour les entrées-sorties, le cache, le debugger, la base et les watchers. Chaque correctif doit être mesuré sur le poste concerné puis vérifié sur les autres profils.
Contre-intuitivement, ajouter CPU et mémoire aggrave parfois la situation. Si chaque requête parcourt des milliers de petits fichiers à travers un montage coûteux, davantage de ressources ne retire pas le goulot. À l’inverse, déplacer aveuglément tout dans des volumes casse l’édition et rend les sources opaques.
Pour une équipe de développement web sur mesure, la performance locale est une propriété du produit interne. La démarche construit une baseline, traite chaque catégorie de latence, documente les compromis par système et conserve une voie de repli avant d’imposer un profil commun.
Chronométrer quatre parcours avant toute optimisation
Mesurer le geste du développeur
La baseline couvre démarrage à froid, requête après modification PHP, commande console ciblée et suite de tests courte. Elle note médiane, cas lent, poste, système, architecture, version Docker et présence du debugger. Trois répétitions après échauffement suffisent pour comparer une intervention locale ; elles ne constituent pas un benchmark universel.
Le seuil d’alerte appartient à l’équipe. Par exemple, un rafraîchissement qui dépasse deux secondes sur le parcours le plus fréquent ou une suite courte qui double après migration justifie un diagnostic. Le but n’est pas le chiffre parfait : il est de protéger la concentration et d’empêcher chaque membre de fabriquer son propre contournement.
Séparer froid et chaud
Le premier démarrage télécharge, construit et initialise. Le parcours chaud reflète la journée réelle. Les deux sont utiles mais n’ont pas le même propriétaire. Optimiser le build hebdomadaire ne compense pas un autoload lent répété cent fois ; accélérer une requête chaude ne résout pas l’arrivée d’un nouveau membre bloqué une heure.
Séparer CPU, mémoire, disque et réseau Docker
Observer avant de changer
Le diagnostic compare usage CPU, pression mémoire, attente disque, nombre de fichiers ouverts, requêtes SQL et appels réseau. Une commande exécutée directement dans le conteneur élimine le terminal comme cause ; un fichier synthétique distingue accès au montage et accès au volume. Le profiler applicatif montre ensuite où le temps est réellement dépensé.
La mesure ne doit pas elle-même fausser tout le parcours. Xdebug et profiling détaillé sont utilisés sur une session dédiée. Les outils système et métriques Docker donnent une tendance, puis une trace applicative confirme. Une conclusion sans comparaison avant/après reste une hypothèse.
Réduire le coût des montages de sources
Les bind mounts rendent les sources modifiables depuis l’hôte, mais leur coût dépend fortement du système et de la couche de virtualisation. La documentation Docker sur les montages bind rappelle qu’ils lient le conteneur à l’arborescence hôte et peuvent masquer le contenu préexistant. Cette propriété explique à la fois leur confort et plusieurs anomalies locales.
L’équipe monte les répertoires réellement édités plutôt que toute la racine par réflexe. Elle exclut caches, logs, base et dépendances générées lorsqu’ils provoquent beaucoup d’entrées-sorties. Les fichiers nécessaires à l’éditeur restent accessibles. Un montage en lecture seule convient aux zones que le conteneur ne doit pas modifier.
Éviter les arborescences fantômes
Un volume qui masque un répertoire inclus dans l’image peut faire fonctionner le local avec un fichier absent en CI. Le démarrage vérifie les chemins importants et la reconstruction sans montage fait partie de la recette. Toute optimisation de stockage conserve donc une preuve que l’image seule contient ce qu’elle promet.
Sortir vendor et cache des chemins bavards
Symfony et Composer consultent de nombreux petits fichiers. Placer vendor et var/cache dans des volumes gérés peut réduire le coût sur certains postes, tout en gardant src et templates montés. Le script d’initialisation installe les dépendances lorsque le lock ou le volume change ; il ne suppose pas qu’un ancien vendor est correct.
Cette solution possède une contrepartie : l’éditeur hôte peut perdre l’indexation des dépendances. Une synchronisation dédiée, un interpréteur distant ou un volume exposé à l’IDE peut résoudre le besoin. L’équipe choisit selon ses outils et documente le profil, au lieu de publier une commande qui accélère seulement le poste de son auteur.
Nettoyer sans détruire
Une commande contrôlée invalide cache ou vendor d’un projet identifié. Elle ne supprime pas tous les volumes Docker. Le runbook explique comment recréer l’état local, préserver un dump utile et distinguer cache applicatif, cache de build et données de développement.
Accélérer le build sans dépendance cachée
Le Dockerfile copie d’abord les manifestes stables, installe les dépendances, puis ajoute le code. Un contexte réduit évite d’envoyer Git, caches et données. La documentation Docker sur l’optimisation du cache de build détaille ordre des couches, contextes et cache mounts. Ces mécanismes sont appliqués après mesure de la phase lente.
Le cache accélère une entrée déterministe ; il ne corrige pas une dépendance non verrouillée. Une reconstruction régulière sans cache vérifie que les registres et scripts suffisent. Le pipeline conserve aussi la durée par étape afin de détecter une croissance après ajout d’un paquet ou d’une copie trop large.
Éviter le rebuild à chaque ligne
Le code PHP monté n’exige pas de reconstruire l’image à chaque édition. Un rebuild est réservé aux dépendances système, extensions, lockfiles ou contrat d’image. Les assets peuvent utiliser un service de watch séparé. Cette frontière réduit les gestes inutiles et rend la cause d’un rebuild compréhensible.
Désactiver Xdebug hors des sessions utiles
Xdebug apporte une valeur forte pour un point d’arrêt, mais son mode et son déclenchement influencent le coût. Le profil rapide le désactive réellement ; le profil debug l’active à la demande avec le mapping d’IDE. Une variable seule n’est pas considérée comme preuve : phpinfo ou la configuration chargée confirme le mode.
OPcache local peut rester actif avec validation des timestamps adaptée au développement. Les réglages ne copient pas aveuglément la production, car le besoin de détection des fichiers change. Le test compare requête avec et sans debugger, puis attribue le surcoût à un usage volontaire.
Profiler une question précise
Le profiler est lancé pour expliquer une route ou une commande, arrêté ensuite, et son résultat est joint au diagnostic. Laisser une collecte détaillée partout dégrade le confort et accumule des fichiers. La mesure légère continue se limite aux indicateurs nécessaires.
Diagnostiquer la base plutôt que surdimensionner Docker
Une page lente peut attendre une requête sans index, un dump énorme ou une résolution DNS, pas le moteur de conteneurs. Le journal des requêtes lentes, les plans et le nombre d’allers-retours séparent ces causes. Un dataset local représente les formes et ordres de grandeur utiles sans copier des données personnelles de production.
Le stockage de la base utilise un volume approprié au système. La mémoire allouée tient compte du moteur et des autres services. Si un import monopolise les ressources, il s’exécute ponctuellement avec une limite connue. Redémarrer la base à chaque test masque les connexions et transactions mal gérées.
Borner la fidélité locale
Le local n’a pas besoin de reproduire la haute disponibilité, mais doit conserver version, extensions et comportement contractuel. Les écarts assumés sont écrits et testés en CI ou en recette. Cette distinction évite une pile de quinze services allumés pour modifier une page indépendante.
Borner les watchers et la recompilation frontend
Un watcher qui parcourt node_modules, cache et fichiers générés peut saturer CPU et événements. Les chemins d’entrée sont précis, les sorties exclues et le polling n’est utilisé que si les événements ne traversent pas la plateforme. Son intervalle reste adapté au geste humain et mesuré sur batterie comme sur secteur.
Le service frontend possède son propre cache de dépendances et sa version Node verrouillée. Le navigateur reçoit un signal de compilation fini plutôt qu’un rafraîchissement pendant l’écriture. Si le projet ne touche pas aux assets, un profil les laisse arrêtés et sert la dernière construction locale.
Éviter la concurrence de deux watchers
Un seul propriétaire compile une sortie. Lancer un watcher hôte et un autre en conteneur crée doublons, fichiers partiels et diagnostics incohérents. Le script de statut affiche les services actifs afin que le développeur sache quel processus consomme les événements.
Ajuster CPU et mémoire sur les postes réels
Docker Desktop partage des ressources avec l’hôte. Une limite trop basse provoque swap et arrêts ; une limite trop haute peut priver l’éditeur et le navigateur. Le réglage part d’une pile au repos puis d’un test représentatif. Il garde une marge pour les outils habituels.
Les limites par service rendent un emballement visible. Un worker d’import ne doit pas rendre le web inutilisable. Si la mémoire augmente sans retour après chaque test, le diagnostic cherche fuite ou cache avant d’ajouter de la capacité. La valeur recommandée est une plage locale par profil de poste, jamais un chiffre absolu.
Qualifier Linux, macOS, Windows et processeurs
Linux, la virtualisation de Docker Desktop et WSL ne présentent pas le même chemin de fichiers. L’équipe maintient deux ou trois profils réellement supportés, exécutés dans la CI de configuration ou par des référents. Elle évite de déclarer un système « lent » sans localiser stockage, antivirus, partage ou architecture.
Sur Windows avec WSL, placer le projet dans le système de fichiers Linux peut changer fortement le résultat selon le workflow. Sur macOS, les options de partage disponibles évoluent avec Docker Desktop. Ces conseils sont vérifiés sur les versions installées et réévalués, car ils ne forment pas une règle éternelle.
Respecter ARM et amd64
Une image native évite l’émulation coûteuse. Les extensions et outils sont contrôlés sur les architectures supportées. Si la production reste amd64, le pipeline construit et teste cette plateforme, tandis que le local garde une image native quand le contrat le permet.
Cas concret : diviser une attente Symfony locale
Cas concret hypothétique. Sur un projet Symfony, une requête chaude prend huit secondes sur deux Mac et moins d’une seconde sous Linux. CPU et mémoire restent bas. Une trace révèle des milliers d’accès à vendor et var/cache via le montage, auxquels s’ajoute Xdebug actif sans session.
L’équipe déplace vendor et cache dans des volumes nommés, ajoute une commande de synchronisation pour l’IDE et crée un profil debug explicite. Elle limite aussi le montage aux sources éditées. La médiane locale tombe sous deux secondes sur ces postes, objectif local, tandis que Linux ne régresse pas. Une reconstruction sans volume confirme que l’image reste complète.
Le scénario de reprise supprime seulement les volumes du projet, réinstalle depuis le lock et exécute tests courts. Une personne non auteure réussit la procédure. Le gain est accepté parce que vitesse, fidélité et récupération sont toutes démontrées.
Rendre les mesures partageables par l’équipe
Une commande diagnostic imprime versions, architecture, profils actifs, capacité allouée et chronomètre les parcours sans collecter de secret. Son rapport joint aux tickets évite « Docker est lent » sans contexte. Le tableau conserve médiane par profil et date de changement.
Le runbook contient démarrage, mise à jour, nettoyage borné, debug et retour au profil rapide. Une alerte locale n’est pas nécessaire ; une régression répétée en CI de développement déclenche une revue. Les optimisations ont un propriétaire et une date de réévaluation.
Pour qui et dans quels cas optimiser Docker local
Une équipe Symfony avec nombreux fichiers, workers et postes hétérogènes bénéficie d’un profil mesuré. Un petit service modifié rarement peut accepter une pile plus simple. Une équipe frontend peut exécuter Node sur l’hôte si versions et sortie restent contractuelles, tandis qu’une équipe réglementée préférera tout isoler.
Le compromis dépend du risque de dérive, du nombre de gestes quotidiens et du support disponible. Un profil « web », un profil « workers » et un profil « debug » évitent d’allumer chaque dépendance. La valeur se juge sur les parcours du rôle concerné.
Erreurs fréquentes de performance Docker locale
Modifier cinq variables à la fois
Sans changement isolé, l’équipe ne sait ni conserver le gain ni expliquer la régression. Chaque expérience part de la baseline, modifie une hypothèse et garde une commande de retour arrière.
Monter toute l’arborescence
Cette facilité masque fichiers de l’image et fait traverser au cache un chemin coûteux. Les montages suivent la responsabilité d’édition. Les données et sorties bavardes possèdent un stockage dédié.
Publier une astuce pour un seul OS
Une option excellente sur une version peut être inutile ailleurs. La fiche de décision nomme environnement, mesure, effet et repli. Une autre plateforme conserve son profil vérifié.
Arbitrer vitesse, fidélité et maintenance
Chaque option est évaluée sur boucle chaude, démarrage froid, fidélité à l’image, support IDE, complexité et récupération. Par exemple, exécuter PHP sur l’hôte peut être très rapide mais réintroduire une version divergente ; un volume vendor accélère certains postes mais ajoute une synchronisation.
Si une optimisation fait gagner moins que le temps mensuel nécessaire à la maintenir, alors l’équipe la retire. Si elle réduit un geste quotidien sans casser la reconstruction ni la CI, elle devient le profil par défaut. Les exceptions sont rares, nommées et réversibles.
- D’abord, chronométrer les quatre parcours sur chaque profil de poste réellement supporté.
- Ensuite, isoler montage, debugger, base, build et watcher avec une expérience à la fois.
- Puis, vérifier l’image sans volumes, la suite courte et la procédure de récupération après chaque optimisation.
- Enfin, publier le profil seulement si une personne non auteure retrouve le gain et peut revenir à la baseline.
Définir un contrat local observable
Les entrées sont code, lockfiles, profils et données synthétiques. Les sorties sont page, test et asset avec durée mesurée. Les responsabilités distinguent image, stockage, IDE et base. La journalisation locale identifie version et profil ; le monitoring léger suit build et test sans enregistrer de données métier.
Les dépendances et seuils sont propres à chaque système. Le runbook couvre réinitialisation, restauration du dump local et désactivation du debugger. Le rollback remet le Compose et les volumes connus ; le repli autorise temporairement un profil simple si un outil est indisponible, sans changer le contrat de CI.
Dans un scénario de cache corrompu, la commande cible ce cache, reconstruit puis valide. Dans un scénario de base lente, elle conserve les données et collecte le plan avant toute suppression. Ces chemins évitent que « tout nettoyer » soit la seule réponse.
Plan d’action sur quatre semaines
Semaine 1 : instrumenter
L’équipe choisit quatre parcours, trois profils de poste et une convention de mesure. Elle archive les résultats, versions, architecture et services. Elle recense les contournements existants, car ils révèlent souvent le coût caché. Aucun réglage collectif ne change encore.
Semaines 2 et 3 : expérimenter
Chaque binôme traite un goulot : montages et cache, PHP et debugger, base, puis frontend. Il applique un changement, mesure, reconstruit sans cache et vérifie le repli. Les gains non reproductibles sont abandonnés. Les profils supportés restent peu nombreux et documentés.
Semaine 4 : standardiser sans figer
Une personne nouvelle suit démarrage, debug et nettoyage. La revue compare temps gagné, maintenance et fidélité. Les commandes deviennent l’interface stable ; les détails peuvent évoluer. Une date trimestrielle revalide les mesures après mise à jour Docker, système, PHP ou dépendances.
Le plan se termine par une décision explicite pour chaque optimisation : défaut, option ou retrait. Le responsable du poste le plus lent possède une voie supportée, pas une note annexe. Les écarts restants sont transférés à la CI ou à la recette avec un test nommé.
La documentation conserve une fiche par profil avec système, chemins montés, services actifs et commande de diagnostic. Elle explique pourquoi une optimisation existe et quel signal autorise sa suppression. Ce contexte évite qu’une future mise à jour réintroduise un réglage devenu inutile ou fasse disparaître une protection de reconstruction.
Enfin, l’équipe chiffre le temps économisé sur une semaine représentative et le compare au temps de maintenance du dispositif. Elle ne généralise pas un résultat isolé. Si un profil ne retrouve pas le gain ou perd la capacité de reconstruire l’image, il revient à la baseline et ouvre une expérience séparée.
Relier performance locale et observabilité
Une mesure locale utile reprend les principes du monitoring et de l’observabilité applicative sans transformer le poste en production. La vérification du cache et des migrations rejoint aussi la migration Symfony compatible avec le run.
Le tableau local reste distinct des SLO utilisateurs. Il protège la boucle de travail qui permet de les tenir. Les mêmes identifiants de version et scénarios facilitent toutefois le passage du diagnostic local à la CI.
- Comparer requête chaude, commande console et tests courts.
- Vérifier l’image complète sans montages de développement.
- Rejouer la récupération du cache et des volumes du seul projet.
Conclusion : optimiser une boucle mesurée
Docker local n’est ni lent par nature ni rapide par déclaration. Le temps vient d’un chemin précis entre fichiers, runtime, base, réseau et outils. Une baseline courte transforme l’agacement en problème testable.
La meilleure optimisation conserve sources éditables, image reconstruisible et récupération simple. Elle peut différer selon le système, tant que la CI ferme le contrat commun et que l’écart est explicite.
Le confort de développement mérite un propriétaire, des seuils locaux et une revue. Cette discipline évite que chaque minute perdue soit normalisée ou contournée en secret.
Dawap peut profiler votre pile, simplifier ses profils et sécuriser la reconstruction dans une mission de développement web Symfony, avec un accompagnement expert fondé sur vos parcours réels.