Le même dépôt Symfony sert une API, consomme deux files, génère des exports nocturnes et lance un rappel toutes les cinq minutes. Le problème apparaît lorsque l’équipe crée un conteneur pour chaque nom de commande, puis ne sait plus lequel déployer, superviser ou arrêter. Lorsqu’un export sature la base, le web ralentit et le scheduler republie le travail parce que son premier lancement n’a laissé aucun état lisible.
Le vrai enjeu est de séparer les contrats d’exécution sans fragmenter la compréhension du produit. Plusieurs processus peuvent partager image, code et configuration, tout en possédant commande, concurrence, ressources, santé et arrêt distincts. Le conteneur matérialise une responsabilité opérationnelle ; il ne doit pas devenir une copie technique créée pour chaque script.
Contre-intuitivement, moins de types de conteneurs rendent souvent le système plus isolable. Un web, une famille de workers par profil et un déclencheur de tâches suffisent lorsque les limites sont explicites. Multiplier les variantes avant d’avoir nommé files, idempotence et reprise transforme la topologie en documentation trompeuse.
Dans une application de développement web sur mesure, chaque processus doit raconter la même causalité métier. La démarche construit cette carte, qualifie le démarrage et les healthchecks, puis éprouve déploiement, panne et rollback avec le support avant d’augmenter le nombre de services.
Cartographier les processus avant les conteneurs
Partir des commandes et des effets
La carte recense serveur HTTP, consommateurs, tâches planifiées et opérations ponctuelles. Pour chacun, elle note entrée, sortie, dépendances, durée, effet externe, concurrence, état durable et propriétaire. Deux commandes qui utilisent le même code mais n’ont ni la même latence ni le même risque peuvent justifier des processus distincts.
Le web répond à une requête bornée. Un worker transforme une intention déjà enregistrée. Le planificateur décide quand soumettre un travail, pas comment exécuter toute sa logique. Une commande d’administration réalise une action contrôlée puis sort. Ces différences guident la topologie davantage que les noms de dossiers.
Établir la source de vérité
Le conteneur ne possède pas le statut métier. Base, file ou stockage externe conservent la vérité. Un redémarrage doit reprendre ou refuser proprement. Si l’état nécessaire ne survit que dans la mémoire du processus ou un fichier local, l’isolation en conteneur rend la perte plus fréquente sans la rendre plus visible.
Partager une image et varier les commandes
Web, workers et cron utilisent généralement le même code PHP, vendor et migrations. Une image commune réduit les divergences. Le déploiement change la commande, les variables non sensibles, les ressources et le nombre d’instances. Chaque rôle reste associé au même digest afin qu’un incident puisse être relié à une version unique.
Des images séparées deviennent pertinentes si la surface de sécurité, les extensions ou le cycle de publication divergent réellement. Un convertisseur média peut demander des binaires lourds absents du web. Cette exception possède un Dockerfile ou target, un scan et une maintenance. Elle ne justifie pas sept images presque identiques.
Garder l’entrée simple
L’entrypoint prépare les permissions strictement nécessaires puis exécute la commande reçue. Il ne choisit pas le rôle à partir d’une variable obscure, ne lance pas plusieurs daemons et n’applique pas automatiquement les migrations. Un processus principal clair permet aux signaux, logs et codes de sortie de garder leur sens.
Borner le contrat du service web
Le web accepte des requêtes courtes, applique authentification et règles, puis répond. Une opération longue est soit bornée par un timeout assumé, soit transformée en commande durable dont l’état devient visible. Envoyer un message ne permet pas d’annoncer que le travail est terminé ; l’API distingue reçu, en cours, réussi, refusé et résultat inconnu.
Le service web reste remplaçable. Sessions, uploads durables et verrous vivent hors de sa couche locale. La readiness vérifie sa capacité propre à servir, sans transformer chaque requête de santé en appel à toutes les dépendances. Si un partenaire est indisponible, un mode dégradé peut rester prêt tout en signalant la fonction concernée.
Protéger la base des rafales
Le nombre de processus web, le pool SQL et les timeouts sont cohérents. Ajouter des replicas ne multiplie pas arbitrairement les connexions. Une limite de concurrence et un contrôle de surcharge produisent une erreur maîtrisée plutôt qu’un effondrement de la base qui bloque aussi les workers.
Concevoir les workers pour la redélivrance
Un worker consomme un message, contrôle les préconditions, produit un effet puis acquitte. Il peut tomber après l’effet et avant l’acquittement ; le message revient. L’idempotence utilise une clé métier et une contrainte atomique lorsque l’effet ne peut pas être répété, plutôt qu’une simple vérification sujette à la concurrence.
La documentation officielle de Symfony Messenger décrit transports, retries et failure transports. Ces mécanismes ne classent pas l’erreur métier. Le handler distingue indisponibilité temporaire, donnée refusée, contrat incompatible et résultat externe inconnu, puis choisit retry, correction, rapprochement ou escalade.
Arrêter sans doubler l’effet
Le worker reçoit le signal, cesse de prendre du travail et termine dans une grace period supérieure à la durée normale. Les messages exceptionnellement longs doivent être découpés ou utiliser keepalive selon le transport. Tuer systématiquement après trente secondes peut déclencher deux traitements concurrents alors que le premier continue côté partenaire.
Séparer planification et exécution des tâches
Le scheduler détermine qu’un travail est dû et publie une commande avec une clé de période. Il ne conserve pas tout le traitement dans un cron monolithique. Si deux instances se chevauchent, la clé ou un verrou empêche la double soumission, tandis que la file garde visibilité, retry et reprise.
Une tâche ponctuelle de maintenance peut rester une commande qui s’exécute puis sort. Une tâche récurrente importante doit laisser heure prévue, heure lancée, périmètre, statut et résultat. Le support sait distinguer non-déclenchée, en attente, en cours et échouée sans lire seulement la crontab.
Choisir la sémantique du retard
Après une heure d’arrêt, faut-il rattraper douze occurrences ou une seule synthèse ? Le métier décide. Un rapport horaire et une purge quotidienne n’ont pas la même règle. Le planificateur encode cette politique, évitant une tempête de rattrapage au redémarrage.
Traiter la file comme un composant du produit
Chaque file correspond à une promesse et une priorité, pas nécessairement à une classe PHP. Séparer commandes urgentes, traitements lourds et notifications empêche un export de bloquer une révocation. Le routage reste versionné et les consommateurs déclarent les types compris.
Les métriques utiles sont âge du plus ancien, délai jusqu’au résultat, taux de retry, erreurs définitives et débit par catégorie. Une profondeur stable peut cacher un dossier vieux de deux jours. Le seuil est relié à une action : ajouter temporairement de la capacité, suspendre un producteur ou traiter une erreur de données.
Limiter l’orage de reprise
Quand une dépendance revient, tous les messages ne doivent pas repartir simultanément. Temporisation, jitter, concurrence et rate limit protègent base et partenaire. Les retries possèdent un budget compatible avec la promesse ; passé ce délai, le statut devient intervention requise au lieu de rester « en cours » indéfiniment.
Refuser les démarrages fondés sur l’ordre
Une dépendance démarrée n’est pas forcément prête. La documentation Docker sur l’ordre de démarrage Compose distingue notamment conditions et healthchecks. L’application doit malgré tout supporter reconnexion, car une base ou un broker peut tomber après le démarrage nominal.
Un script wait-for sans timeout masque parfois une panne et bloque le déploiement. Le processus valide sa configuration, tente ses dépendances selon une politique bornée puis expose un état clair. L’orchestrateur redémarre seulement les erreurs qui peuvent guérir ; une configuration invalide doit alerter, pas boucler silencieusement.
Ne pas chaîner les migrations
Le web ne doit pas attendre qu’un autre replica lance une migration destructive. Un job unique applique des étapes compatibles, puis les workloads s’ouvrent. Si le schéma requis manque, readiness échoue avec un diagnostic. Cette séquence conserve un rollback applicatif réel.
Définir une santé propre à chaque rôle
Le web vérifie qu’il peut traiter une requête locale. Le worker expose qu’il consomme et renouvelle un heartbeat sans promettre que toutes les dépendances répondent. Le scheduler prouve sa dernière boucle et le prochain déclenchement. Utiliser la même URL health pour tous produit un vert trompeur.
Liveness répond à un blocage du processus ; readiness décide si le trafic ou le travail doit arriver. Un redémarrage ne doit pas remplacer une alerte métier. Si un worker redémarre cinq fois en dix minutes, ce seuil local gèle l’extension et ouvre une analyse de message, mémoire et dépendance.
Conserver une marge d’arrêt
Le monitoring compare durée haute des jobs et grace period. Il signale les traitements qui dépassent régulièrement la fenêtre de déploiement. Le correctif peut découper le job, prolonger le délai ou ajouter un protocole de checkpoint, mais pas simplement tuer plus vite.
Dimensionner sans laisser un service affamer les autres
Web et workers ont des profils différents. Les limites CPU et mémoire évitent qu’une conversion sature le nœud, tandis que les requests ou réservations rendent la capacité planifiable. Les pools SQL, clients HTTP et descripteurs sont intégrés au calcul. Un autoscaling qui ignore ces ressources déplace le goulot.
La concurrence se règle par expérience représentative. Le pilote augmente les workers jusqu’au point où latence SQL, quota partenaire ou mémoire dégrade la promesse. Le seuil retenu garde une marge et reste local à l’infrastructure. Il est révisé après changement de code ou de volume.
Isoler le travail lourd
Une famille de workers dédiée suffit souvent : même image, commande et file distinctes, ressources adaptées. Extraire un microservice uniquement pour obtenir une limite CPU ajoute contrat réseau et déploiement sans valeur métier. L’isolation opérationnelle précède la séparation du code.
Déployer des versions compatibles avec le travail en attente
Une file traverse les déploiements. Le nouveau consommateur doit comprendre les messages anciens ou l’ancienne version reste active jusqu’à drainage. Ajouter un champ optionnel est généralement plus sûr que changer le sens d’une valeur. Un type incompatible reçoit une nouvelle version et une migration explicite.
Le rollout du web peut être rapide tandis qu’un worker finit un message long. Le pipeline associe digest, schéma de message et migrations. Le rollback vérifie que l’ancienne version comprend les données et messages déjà produits. Revenir au tag précédent sans ce contrôle peut aggraver l’incident.
Canariser par catégorie
Un nouveau handler peut consommer une file ou un type borné. Le monitoring compare erreurs, durée et effets doubles. L’équipe élargit après un nombre de cas représentatif, sans inventer une garantie statistique. Le repli remet le routage précédent sans perdre les commandes acceptées.
Cas concret : commandes web, exports et factures
Cas concret hypothétique. Un portail reçoit des commandes, transmet à un ERP, génère les factures et exporte un reporting chaque nuit. Le web et tous les jobs partagent un conteneur supervisé par un script. Un export de quarante minutes bloque le redémarrage, tandis que deux crons lancent parfois la même période.
La cible utilise une image. Le web sert l’API ; des workers « intégration » et « document » consomment deux files ; un scheduler publie ExporterJour avec la date comme clé. Les statuts commande, facture et export vivent en base. Chaque service a ressources, santé, arrêt et tableau propres, avec corrélation commune.
Le pilote traite mille commandes, cent factures et cinq exports. Les seuils locaux exigent zéro transmission ERP doublée, aucun export concurrent pour une date, un diagnostic sous dix minutes et un déploiement sans message perdu. Scénario supplémentaire : si le worker tombe après l’envoi ERP, alors la redélivrance doit rapprocher la référence avant toute nouvelle émission.
Donner au support une chronologie commune
Le support recherche une corrélation et voit demande web, message, tentative, effet externe et statut métier. Il ne choisit pas un conteneur au hasard. Les logs ajoutent rôle, digest, file et catégorie d’erreur sans exposer le payload sensible. Les métriques ouvrent le runbook correspondant.
La mise en œuvre attribue responsabilités du web, des workers, du scheduler et du broker. Elle documente entrées, sorties, dépendances, monitoring, seuils, retries et rollback. Deux paragraphes de runbook valent mieux que vingt services sans owner si une personne différente peut reprendre le cas.
Répéter depuis les droits réels
Le support traite donnée refusée, dépendance silencieuse et message redélivré avec son interface. Les actions sont rapprocher, rejouer une commande idempotente ou escalader. Modifier la file ou la base librement reste interdit ; chaque geste laisse auteur et motif.
Reconnaître le bon niveau de séparation
La méthode devient utile dès qu’un dépôt exécute web et tâches de durées ou priorités différentes. Elle est essentielle quand plusieurs files, transformations lourdes et déploiements fréquents coexistent. La séparation protège capacité et reprise sans exiger plusieurs applications.
Un petit site avec une commande quotidienne courte peut garder web et cron sur une plateforme simple si le déclenchement et le résultat sont visibles. Créer un broker et trois workers pour envoyer dix emails augmente le run. Le seuil dépend de la douleur et de la promesse, pas d’un diagramme idéal.
Erreurs fréquentes d’une architecture multi-services
Lancer plusieurs daemons dans le même conteneur
Un superviseur interne masque codes de sortie et signaux. Chaque rôle principal mérite un service distinct, même s’il partage l’image.
Faire exécuter le métier par le cron
Le planificateur devient impossible à reprendre et double le travail lors d’un chevauchement. Il publie une intention idempotente dont le résultat est suivi.
Utiliser le redémarrage comme correction
Le conteneur repart mais le message, la donnée ou le partenaire reste défaillant. L’alerte doit nommer la cause et l’action, pas seulement le nombre de restarts.
Décider quels processus isoler
La matrice compare commande, durée, priorité, dépendances, profil de ressources, concurrence, arrêt et propriétaire. Deux workloads restent ensemble si leurs limites et leur cycle coïncident. Ils se séparent lorsqu’un incident ou une montée de charge doit être contenu indépendamment.
- D’abord, conserver une image commune tant que runtime et version restent identiques.
- Ensuite, isoler commandes et ressources selon la promesse de chaque workload.
- Puis, différer une nouvelle file tant qu’elle ne protège ni priorité ni reprise.
- Enfin, refuser tout service sans santé, owner, arrêt et statut métier observables.
Un conteneur distinct n’est pas un microservice. Cette isolation peut rester dans le même dépôt, la même transaction et le même déploiement. La séparation réseau ne devient pertinente que si données, contrat et équipe ont une autonomie réelle.
Plan d’action sur six semaines
Semaine une : inventorier les exécutions
L’équipe relève commandes, horaires, files, durées, effets, ressources et incidents. Elle choisit un flux critique et nomme les états visibles. Les scripts sans propriétaire et les écritures locales sont bloqués pour les nouveaux cas.
Semaines deux et trois : fermer les contrats
Une image sert des commandes explicites. Le scheduler publie, les workers deviennent idempotents et les files reçoivent erreurs qualifiées. Les healthchecks, signaux et grace periods sont testés. La journalisation relie web, messages et sorties.
Semaine quatre : casser la chaîne
Le pilote coupe broker et partenaire, tue un worker après effet, chevauche un cron et déploie avec un ancien message. Le support exécute rapprochement, retry ou repli depuis le runbook. Les doubles effets bloquent l’ouverture.
Semaines cinq et six : mesurer et étendre
Un trafic borné suit âge, latence, restarts, saturation et temps de diagnostic. Le comité compare les seuils puis augmente une catégorie à la fois. Le dossier attribue ressources, monitoring, contrats et rollback.
La sortie contient carte des workloads, commandes, images, files, matrice de ressources, états, alertes et compatibilité de déploiement. Chaque exception reçoit une échéance. Le plan ne s’achève pas au démarrage des conteneurs, mais quand une personne extérieure au chantier reprend un incident complet.
- D’abord, tester le remplacement du web sans perdre une session ni un upload.
- Ensuite, provoquer la redélivrance après un effet externe.
- Puis, rejouer le retard du scheduler et le budget de rattrapage.
- Enfin, décider l’extension depuis le délai métier et la reprise observée.
Guides complémentaires pour tenir le run
Stabiliser l’artefact
Partager l’image entre dev, CI et production évite que chaque rôle dérive vers un runtime différent.
Approfondir la messagerie
Maîtriser Messenger dans le run détaille idempotence, failure transport et reprise support.
Observer les statuts
Observer un workflow métier relie les métriques techniques à la sortie attendue par l’utilisateur.
- Nommer le rôle avant de créer un nouveau service.
- Tester signaux, redélivrance et compatibilité de message.
- Élargir seulement si le support retrouve la causalité sans accès libre à la base.
Conclusion : séparer l’exécution, conserver la causalité
Web, workers et tâches planifiées ont des rythmes et des pannes différents. Les séparer protège ressources, arrêt et déploiement, tout en conservant une image et un vocabulaire communs.
La file ne remplace ni l’état métier ni la reprise. Chaque message doit tolérer la redélivrance, chaque cron expliciter le rattrapage et chaque service exposer une santé adaptée à son rôle.
Le bon nombre de conteneurs est celui dont l’équipe peut expliquer les responsabilités et les incidents. Une topologie plus grande n’apporte aucune maturité si elle disperse les traces et les owners.
Dawap peut cartographier les workloads, fermer un premier flux et répéter son incident dans un accompagnement en développement web sur mesure qui isole les pannes sans perdre la décision métier ni la capacité de reprise.