Tous les services backend ne se déploient pas de la même manière
Une équipe s'apprête à déployer une nouvelle fonctionnalité. La mise à jour du service API se déroule sans accroc - quelques secondes de remplacement progressif, zéro temps d'arrêt. Puis vient la mise à jour du worker. Quelqu'un lance le déploiement, et soudainement tous les travaux de traitement d'images en file d'attente disparaissent. L'équipe scrute les journaux, perplexe. "Ça a fonctionné pour l'API. Pourquoi le worker a-t-il planté ?"
Ce scénario se reproduit chaque semaine dans les équipes d'ingénierie. La cause profonde n'est presque jamais un bug dans l'outil de déploiement. C'est une méconnaissance du type de service backend auquel on a affaire.
Les services backend se ressemblent en surface. Ils tournent tous sur des serveurs, ont tous du code, nécessitent tous des mises à jour. Mais la façon dont ils reçoivent le travail, dont ils conservent l'état, et dont ils gèrent les interruptions est fondamentalement différente. Un pipeline CI/CD qui fonctionne parfaitement pour un type peut détruire silencieusement les données d'un autre.
Services API : toujours à l'écoute, toujours disponibles
Le type de backend le plus familier est le service API. Il écoute sur un port, attend les requêtes entrantes des applications mobiles, des sites web ou d'autres services, et renvoie une réponse. Les utilisateurs ressentent immédiatement l'indisponibilité d'un service API : l'application cesse de charger, la page affiche une erreur, la transaction échoue.
Le diagramme suivant classe les services backend selon la manière dont ils reçoivent le travail et associe chaque type à sa stratégie de déploiement recommandée.
Les services API doivent être toujours prêts. Ils doivent gérer les pics de trafic en augmentant ou réduisant le nombre d'instances. Ils doivent accepter de nouvelles connexions pendant que les anciennes sont encore en cours de traitement. Et surtout, ils doivent être mis à jour sans interrompre les requêtes en vol.
Pour le CI/CD, cela signifie que la stratégie de déploiement est cruciale. Un simple arrêt-démarrage couperait les utilisateurs actifs. Les mises à jour progressives, les déploiements bleu-vert ou les versions canary deviennent nécessaires. Le pipeline doit vérifier que les nouvelles instances passent les tests de santé avant que les anciennes ne soient supprimées.
Workers : traiter des travaux, pas des requêtes
Les workers ne communiquent pas directement avec les utilisateurs. Ils récupèrent des tâches depuis une file d'attente, les traitent une par une, et stockent les résultats. Redimensionnement d'images, envoi d'e-mails, génération de rapports - ce sont des travaux de worker. L'utilisateur télécharge une photo et reçoit une réponse immédiate, mais le redimensionnement se fait en arrière-plan.
Comme les workers ne servent pas de requêtes en direct, ils peuvent être mis à jour plus en douceur. La préoccupation principale est de ne pas perdre les travaux en cours. Un bon pipeline de worker attend la fin du travail en cours avant d'arrêter l'ancien processus. Si le système de file d'attente le permet, le worker peut signaler qu'il s'arrête, terminer sa tâche en cours, puis quitter. Les nouvelles instances démarrent, récupèrent les travaux suivants dans la file, et le système continue sans perte de travail.
L'erreur que commettent les équipes est de traiter les workers comme des services API. Elles tuent le processus immédiatement pendant le déploiement, et tous les travaux en cours sont perdus. La file d'attente ne sait pas que le travail a échoué à mi-parcours - elle ne voit jamais de signal d'achèvement. Le travail disparaît dans un trou noir.
Un Deployment Kubernetes est conçu pour fonctionner en continu et utilise des sondes de préparation pour gérer le trafic. Un Job, en revanche, s'exécute jusqu'à son terme et utilise restartPolicy: Never :
# Service API - fonctionne en continu, nécessite une sonde de préparation
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myapp/api:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
---
# Worker - exécute un seul travail, puis se termine
apiVersion: batch/v1
kind: Job
metadata:
name: image-resize-job
spec:
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: myapp/worker:latest
command: ["process-queue"]
Planificateurs : le timing est primordial
Les planificateurs exécutent des tâches selon un calendrier. Nettoyer les anciennes données à minuit. Synchroniser avec un système externe toutes les heures. Générer des rapports quotidiens à 6 heures du matin.
Le défi avec les planificateurs est d'éviter les exécutions en double. Si un planificateur lance un travail de nettoyage et que le déploiement redémarre le planificateur au moment où le travail commence, il y a un risque que la nouvelle instance déclenche également le même travail. Le nettoyage s'exécute alors deux fois, ce qui peut provoquer des conflits ou des problèmes de données.
Un bon pipeline de planificateur garantit que le planificateur ne démarre pas une nouvelle instance pendant que l'ancienne exécute encore une tâche planifiée. Il nécessite également un mécanisme pour détecter et ignorer les exécutions qui se chevauchent. Certaines équipes utilisent des verrous distribués ou des marqueurs en base de données pour empêcher la double exécution.
Consommateurs : suivre le flux
Les consommateurs sont similaires aux workers, mais ils traitent un flux continu de données. Ils lisent depuis des courtiers de messages comme Kafka ou RabbitMQ, traitent les messages en temps réel, et doivent souvent maintenir leur position dans le flux.
La différence cruciale est la vitesse et le suivi des offsets. Si un consommateur prend du retard, il doit rattraper son retard sans submerger le système. Si un consommateur plante, il doit reprendre là où il s'est arrêté, pas depuis le début.
Pour le CI/CD, cela signifie que le déploiement doit gérer soigneusement les validations d'offset. Arrêter un consommateur sans valider l'offset courant peut entraîner le retraitement de messages déjà traités. Redémarrer depuis le mauvais offset peut sauter des messages ou les traiter deux fois. Le pipeline doit se coordonner avec le courtier de messages pour garantir un arrêt gracieux et une reprise correcte.
Services internes : les dépendances cachées
Les services internes ne sont pas accessibles par les applications frontend. Ils servent d'autres services au sein du système - authentification, configuration, journalisation, feature flags. Plusieurs services en dépendent, souvent de manière synchrone.
Le danger avec les services internes, ce sont les défaillances en cascade. Un petit changement dans un service d'authentification peut casser tous les services qui l'appellent. Une réponse lente d'un service de configuration peut provoquer des timeouts dans tout le système.
Ces services nécessitent des tests plus stricts et un déploiement plus prudent. Le pipeline doit exécuter des tests d'intégration qui simulent le comportement réel des appelants. Le déploiement doit être progressif, avec une surveillance des services dépendants. Le rollback doit être rapide et fiable car le rayon d'explosion est large.
Une checklist pratique pour les pipelines de services backend
Avant de concevoir un pipeline pour un service backend, posez ces questions :
- Comment ce service reçoit-il le travail ? (requête, file d'attente, planification, flux)
- Peut-il être arrêté en toute sécurité en cours de tâche ? Si oui, qu'advient-il de la tâche ?
- Maintient-il un état en mémoire qui ne peut pas être récupéré ?
- Que se passe-t-il si deux instances exécutent la même tâche simultanément ?
- Quels autres services sont impactés si ce service tombe ?
- Combien de temps ce service peut-il être indisponible avant que les utilisateurs ou les systèmes ne le remarquent ?
Les réponses vous indiqueront si vous avez besoin de mises à jour progressives, de hooks d'arrêt gracieux, de coordination de file d'attente, ou d'un ordre de déploiement tenant compte des dépendances.
L'essentiel à retenir
Un pipeline CI/CD n'est pas un script universel. C'est le reflet du fonctionnement réel de votre service. Les services API ont besoin d'une gestion des connexions. Les workers ont besoin de garanties d'achèvement des travaux. Les planificateurs ont besoin de prévention des chevauchements. Les consommateurs ont besoin de gestion des offsets. Les services internes ont besoin de coordination des dépendances.
Lorsque vous comprenez à quel type de service backend vous avez affaire, vous cessez de copier des modèles de pipeline issus de blogs et commencez à construire des pipelines qui protègent vos données, vos utilisateurs et le sommeil de votre équipe.