Le déploiement a réussi, mais le site est hors service
Votre tableau de bord indique que le service est en cours d’exécution, mais vos utilisateurs voient une erreur. Découvrez pourquoi la réussite d’un déploiement et la santé de l’application sont deux signaux différents, et comment faire en sorte qu’un déploiement au vert signifie réellement que l’application répond.
Il existe une forme particulière de mauvaise matinée qui commence par une coche verte. Le déploiement a réussi, mais le site est hors service, le tableau de bord indique running, et quelqu’un vous envoie une capture d’écran d’une erreur 502.
Ce n’est pas un cas marginal. C’est le résultat prévisible d’une plateforme qui signale une chose tout en mesurant autre chose. Il est donc utile de comprendre précisément ce qui se passe, car la solution n’est pas de « vérifier plus attentivement » : il faut changer ce que le mot running est autorisé à signifier.
Trois questions différentes, un seul voyant d’état
Lorsqu’une plateforme indique qu’un service est en cours d’exécution, elle peut répondre à l’une des questions suivantes :
- Le conteneur a-t-il démarré ? Le processus existe et ne s’est pas arrêté.
- Le port est-il ouvert ? Quelque chose écoute à l’emplacement attendu par la plateforme.
- L’application répond-elle correctement ? Une requête reçoit une réponse indiquant que l’application est prête à fonctionner.
Ces garanties sont très différentes, et la plupart des incidents de ce type surviennent parce que le tableau de bord répond à la question 1 alors que vous pensiez qu’il répondait à la question 3.
Un processus Node qui démarre, échoue à se connecter à sa base de données, puis reste bloqué dans une boucle de nouvelle tentative satisfait indéfiniment à la question 1. Il n’a pas rencontré de crash. Il ne traitera jamais aucune requête. Le conteneur est « en cours d’exécution » dans tous les sens qui importent pour l’orchestrateur.
L’espace où se loge l’interruption
La fenêtre dangereuse se situe entre « la nouvelle version a démarré » et « la nouvelle version peut fonctionner ». Pendant cette période, une plateforme naïve a déjà basculé le trafic, car le démarrage était la seule chose qu’elle mesurait.
Ce qui rend la situation pire qu’un simple crash, c’est le scénario de rollback. Une boucle de crash est bruyante : le conteneur s’arrête, redémarre, s’arrête à nouveau, et la plateforme finit par s’en rendre compte. Un démarrage suivi d’un blocage est silencieux. Rien ne redémarre, aucune alerte ne se déclenche et l’ancienne version fonctionnelle a généralement déjà été supprimée.
C’est cette dernière étape qui cause les vrais dégâts. L’ancienne version fonctionnait. Elle a été supprimée parce qu’une nouvelle version avait démarré, et démarrer a été confondu avec fonctionner.
Ce que fait un véritable contrôle de santé
La solution est structurelle, pas procédurale. Le trafic ne doit pas basculer tant que la nouvelle version n’a pas répondu à une requête.
Sur Dockup, un release se déroule comme suit : la nouvelle version est construite de manière isolée, démarrée à côté de la version qui traite actuellement le trafic, puis soumise à une question. Ce n’est que lorsqu’elle répond que le domaine pointe vers elle. Si elle ne répond jamais, le release s’arrête à ce stade et l’ancienne version continue de servir les requêtes : personne en dehors de votre tableau de bord ne saura même qu’un déploiement a été tenté.
C’est pourquoi un déploiement échoué sur Dockup ne provoque pas d’interruption. L’ancien conteneur n’a jamais été supprimé en partant du principe que le nouveau fonctionnerait correctement.
# The health gate is per-service configuration, not a platform default you inherit
dockup info my-project/my-api --json
Le bloc healthCheck de cette sortie constitue l’intégralité du contrat : le chemin appelé, le délai d’attente d’une réponse, le nombre de tentatives et le délai entre celles-ci.
Configurez le contrôle pour répondre à la question 3
Un endpoint de santé qui renvoie systématiquement 200 est pire que l’absence totale de contrôle, car il transforme une véritable barrière en simple formalité. Le but du contrôle est d’échouer lorsque l’application ne peut pas remplir sa fonction.
Un endpoint de readiness utile vérifie les éléments dont l’application ne peut pas se passer :
// Not this — it proves only that the process is alive
app.get('/healthz', (req, res) => res.send('ok'))
// This — it proves the app can actually serve a request
app.get('/healthz', async (req, res) => {
try {
await db.query('select 1') // the dependency that is usually the problem
if (!cacheReady) throw new Error('cache warming')
res.status(200).json({ ok: true })
} catch (err) {
res.status(503).json({ ok: false, reason: err.message })
}
})
Deux règles permettent de faire fonctionner ce mécanisme en pratique :
Vérifiez les dépendances dont vous ne pouvez pas vous passer, et rien d’autre. Si votre application peut continuer à fonctionner en mode dégradé lorsque l’index de recherche est indisponible, ne faites pas dépendre la readiness de cet index : vous bloqueriez les déploiements pour un problème qui ne constitue pas une interruption de service.
Gardez le contrôle peu coûteux. L’endpoint est appelé à plusieurs reprises lors de chaque release. Un contrôle de readiness qui exécute une requête coûteuse crée un problème de charge de votre propre fait.
Donnez-lui suffisamment de temps, mais pas un délai illimité
Deux paramètres déterminent si la barrière vous aide ou vous pénalise :
- Le délai d’attente par tentative doit être supérieur à la durée de votre démarrage à froid légitime le plus lent. Une application qui se connecte à une base de données et préchauffe un cache en huit secondes échouera à chaque fois face à un contrôle de trois secondes, et vous « corrigerez » le problème en désactivant la barrière — ce qui vous ramènera au point de départ.
- Les nouvelles tentatives doivent couvrir la durée totale du démarrage, et non une seule tentative. Intervalle × tentatives constitue le budget réel.
Sur Dockup, ces paramètres sont healthCheckInterval, healthCheckTimeout et healthCheckRetries. Ils sont définis par service, car un monolithe Rails et un sidecar Go ne démarrent pas selon le même calendrier.
Lorsqu’il est déjà hors service
Si vous lisez cet article en pleine interruption, voici l’ordre des opérations qui permet de résoudre la situation le plus rapidement :
- Vérifiez si l’application répond directement, en contournant le domaine. Si elle répond sur son port mais pas via le domaine, il s’agit d’un problème de routage, pas d’un problème applicatif : arrêtez donc de déboguer votre code.
- Consultez les logs d’exécution, pas les logs de build. Le build a réussi : c’est le point de départ. Ce que vous voulez savoir, c’est ce que le processus a fait après son démarrage.
- Effectuez le rollback avant d’établir le diagnostic. Le diagnostic coûte moins cher quand personne n’attend.
dockup logs my-project/my-api --follow # what the running process is saying
dockup deployments my-project/my-api # what was live before this
dockup rollback <deployment-id> my-project/my-api # put that back
Sur Dockup, un rollback consiste à basculer vers une autre version plutôt qu’à reconstruire, car la version précédente est toujours présente sur le disque. À 3 heures du matin, c’est essentiel : la récupération la plus rapide est celle qui ne nécessite aucune compilation.
La question à poser à une plateforme
Lorsque vous choisissez où exécuter votre environnement de production, voici un test utile à effectuer volontairement : déployez une application qui démarre correctement, puis échoue à joindre sa base de données. Observez ce qu’indique le tableau de bord.
S’il indique running, vous saurez désormais exactement ce que ce mot vaudra lors de votre prochaine interruption.
Foire aux questions
Pourquoi mon tableau de bord indique-t-il que le service est en cours d’exécution alors que le site est hors service ? Parce que « en cours d’exécution » signifie généralement que le processus du conteneur existe, et non que l’application peut traiter une requête. Un processus bloqué dans une nouvelle tentative de connexion à la base de données satisfait indéfiniment à cette définition.
Un contrôle de santé doit-il interroger la base de données ? Oui, si votre application ne peut pas traiter de requêtes sans elle. Vérifiez les dépendances dont vous avez réellement besoin et ignorez celles qui permettent un fonctionnement en mode dégradé.
Quelle est la différence entre liveness et readiness ? La liveness détermine si le processus doit être redémarré. La readiness détermine s’il doit recevoir du trafic. La barrière qui empêche ce type de problème est la readiness, et elle doit être exécutée avant le basculement du trafic.
Comment empêcher un mauvais déploiement de mettre complètement le site hors service ? Ne basculez le trafic qu’après que la nouvelle version a répondu à une véritable requête, et conservez l’ancienne version jusqu’à confirmation du basculement. Ainsi, un release échoué sera un release qui n’a jamais eu lieu, plutôt qu’une interruption de service.
