jguillaumesio
cloud-opsdevopsaws

Le canal d'alertes que personne ne lisait, et le catalogue qui l'a réparé

Chaque moniteur réveillait quelqu'un, donc aucun ne voulait plus rien dire. La staging sonnait la nuit, un tenant tranquille pouvait afficher 100 pour cent d'erreurs sur une seule requête, et l'équipe avait appris à balayer la notification. Voici le découpage P1 et P2 sur lequel on s'est arrêté pour une stack ECS, et les deux mécaniques qui ont arrêté le flapping.

Le canal d’alertes avait quatre cents notifications non lues et l’équipe avait appris, avec raison, à l’ignorer.

Ce n’est pas de la négligence. C’est la réponse rationnelle à un canal où un déploiement en staging, un seul 502 venu d’un health check, un pic de CPU résorbé en quatre-vingt-dix secondes et une vraie base de données à bout de connexions arrivent tous avec exactement la même tête. Quand tout réveille, rien ne réveille. Le signal est toujours là quelque part, et aucun humain ne va le trouver à trois heures du matin.

Le pire d’entre eux se déclenchait presque toutes les nuits. Un tenant tranquille, quelques requêtes par heure, l’une échoue, et le moniteur de taux d’erreur annonce cent pour cent d’erreurs et réveille quelqu’un. C’était techniquement vrai chaque fois. Ça ne valait jamais la peine de se réveiller.

Ceci est le deuxième article d’une série sur l’exploitation d’un SaaS multi-tenant sur AWS à l’échelle d’une équipe. Le premier parlait d’une métrique qui ment. Celui-ci parle d’un canal qui crie, et du catalogue d’alertes qu’un bon profil DevOps arrivé dans l’équipe a construit pour le remplacer. J’étais développeur dans cette équipe, pas la personne qui a fait ce travail, et l’avoir vu se faire a changé ma façon de penser la supervision.

Chaque moniteur doit répondre à l’une de deux questions

Tout le catalogue découle d’une seule règle. Avant qu’un moniteur existe, il doit répondre à l’une de ces deux questions :

  1. P1 : est-ce qu’un humain doit agir tout de suite ? Le service est à terre, saturé, ou perd des données. Quelqu’un est réveillé, sur son téléphone, à n’importe quelle heure.
  2. P2 : est-ce qu’un humain doit le savoir dans les prochains jours ? Quelque chose se dégrade, ralentit, ou dérive. Ça part dans un canal et ça se lit aux heures de bureau.

Un moniteur qui ne répond à aucune des deux est supprimé. Pas rétrogradé, supprimé. C’est la partie qui a été la plus difficile à accepter, parce que chaque moniteur a été créé par quelqu’un qui avait une raison. Mais un niveau P3 est un cimetière avec des étapes en plus : personne ne le lit, et son existence permet de se raconter qu’on surveille quelque chose qu’on ne surveille pas.

Le découpage ne porte pas sur la gravité du problème sous-jacent. Il porte sur le fait qu’agir à 3 h du matin change ou non le résultat. Un endpoint lent est un vrai problème et une très mauvaise raison de réveiller quelqu’un, parce que personne ne va profiler une requête à moitié endormi.

Le catalogue P1

P1, c’est la saturation et la panne, rien d’autre. Ce sont les situations où le système ne sert plus le trafic, ou n’en est qu’à quelques minutes.

ComposantSignalForme du seuilPourquoi ça réveille
Service ECSCPU de la taskAu-dessus de 90 pour cent, tenu 5 minutesSaturation, les requêtes s’accumulent derrière
Service ECSMémoire de la taskAu-dessus de 90 pour cent, tenu 5 minutesL’étape suivante est un OOM kill
Service ECSTasks arrêtées avec code de sortie 137Toute occurrenceLe conteneur a été tué par l’OOM killer, la capacité vient de baisser
Service ECSRunning count sous le desired countTenu 5 minutesLes tasks n’arrivent pas à démarrer ou bouclent sur un crash
RDSDatabase load au-dessus du nombre de vCPUTenu 10 minutesLes requêtes attendent le CPU, tout ralentit ensemble
RDSNombre de connexions proche du maximumAu-dessus de 90 pour cent de la limiteL’épuisement des connexions refuse tout nouveau travail
RDSEspace disque libreSous un plancher fixe laissant plusieurs joursUn disque plein sur une base, c’est une panne plus une restauration
ElastiCacheMémoire utilisée et évictionsÉvictions qui décollent de zéroLe cache jette des clés, la charge retombe sur la base
ALB ou NLBNombre de cibles unhealthyAu-dessus de zéro, tenu 3 minutesLa capacité a disparu même si les cibles restantes tiennent
ALBTaux de 5xxPic au-dessus de la référence, avec garde-fou de volumeLe load balancer échoue sur des requêtes qu’il a acceptées
CloudFrontTaux de 5xx originePic au-dessus de la référenceL’edge va bien, l’origine non
API GatewayTaux de 5xxPic au-dessus de la référence, avec garde-fou de volumeMême raisonnement, une couche plus haut

Deux de ces lignes méritent un commentaire.

Le code de sortie 137 est l’alerte la plus utile de la liste. C’est ce que Linux remonte quand l’OOM killer arrête un conteneur, et c’est la défaillance qui se cache le mieux : la task redémarre, le service se rétablit, le dashboard lisse l’incident, et la seule trace est une task arrêtée que personne n’a regardée. Alerter sur toute occurrence transforme un redémarrage invisible et récurrent en ticket.

Le seuil de CPU ne veut dire quelque chose que si la task est dimensionnée honnêtement. C’est le lien direct avec le problème d’autoscaling du premier article : un processus mono-thread sur une task à quatre vCPU ne peut pas dépasser environ 25 pour cent de CPU moyen, donc une alarme à 90 pour cent sur ce service est un moniteur qui ne peut jamais se déclencher. Standardiser à un vCPU par task n’a pas seulement réparé l’autoscaling. Ça a rendu le seuil d’alerte réel.

Le catalogue P2

P2, c’est tout ce qui va vraiment mal et vraiment pas dans l’urgence. Même rigueur, autre destination : un canal, les heures de bureau, pas de téléphone.

ComposantSignalPourquoi ça ne réveille pas
ALBTemps de réponse des cibles et latence p95Vraie dégradation, mais personne ne corrige la latence à 3 h du matin
RDSÂge de la plus vieille transactionUne transaction longue bloque le vacuum et garde des verrous, et le correctif est dans le code
ALB ou API GatewayTaux de 4xxEn général un client ou une intégration qui se comporte mal, parfois un bug livré
ECSDéploiement bloqué en coursAgaçant, déjà visible dans le pipeline, pas encore visible du client
ElastiCacheTaux de hit du cache en baisseUn signal de performance et de coût, à regarder, jamais à réveiller

L’âge de la plus vieille transaction est le piège discret de cette liste. Rien n’a l’air anormal pendant des heures, puis l’autovacuum n’est pas passé sur la table la plus sollicitée depuis la veille, le bloat grossit, et un plan de requête bascule. Le lire une fois par semaine ne coûte rien et évite un incident sincèrement déroutant.

Les garde-fous de volume, ou comment cesser de se faire réveiller par un tenant tranquille

Retour au moniteur qui sonnait presque toutes les nuits. Dans un système multi-tenant, le trafic par tenant varie de plusieurs ordres de grandeur. Un taux seul ne porte aucune information sur le volume qui l’a produit, et une requête échouée sur une, c’est un taux de cent pour cent.

Le correctif est une condition composite. Le moniteur ne se déclenche que si le taux est mauvais et que le volume est assez élevé pour que le taux veuille dire quelque chose :

error_rate > 5%  AND  request_count > 200 sur la même fenêtre de 10 minutes

Deux cents n’est pas un nombre magique. C’est le point où un taux de cinq pour cent représente au moins dix requêtes échouées, ce qui suffit à faire un motif plutôt qu’une coïncidence. Choisissez le plancher à partir de votre propre trafic : ce qui rend le numérateur assez gros pour que le dénominateur cesse d’être du bruit.

Le même garde-fou vaut pour tous les ratios du catalogue : taux de 5xx, taux de 4xx, taux de hit du cache, tout ce qui s’exprime en pourcentage. Un pourcentage sans plancher de volume est un générateur de nombres aléatoires en faible trafic. Ce seul changement a supprimé la majorité des alertes nocturnes, et il a supprimé exactement celles qui avaient appris à tout le monde à ignorer le canal.

Le flapping, et les fenêtres qui l’arrêtent

L’autre moitié du bruit venait de moniteurs qui se déclenchaient et se résolvaient tout seuls avant que quiconque ouvre l’application. Trois correctifs, tous ennuyeux :

Évaluer sur une fenêtre, pas sur un instant. « CPU au-dessus de 90 pour cent pendant 5 minutes » est un moniteur complètement différent de « le CPU a touché 90 pour cent ». La version instantanée se déclenche sur un ramasse-miettes, sur un déploiement, sur un job batch. La version fenêtrée se déclenche quand il y a un problème.

Placer le seuil de rétablissement sous le seuil de déclenchement. Si un moniteur se déclenche à 90 et se rétablit à 90, tout ce qui oscille autour de 90 produit une tempête d’alertes. Rétablir à 80 et l’oscillation produit une alerte. C’est de l’hystérésis, et tous les outils d’alerting le gèrent.

Régler par observation, sur plusieurs semaines. Chaque moniteur qui s’était déclenché puis résolu tout seul deux fois dans la même nuit a vu sa fenêtre élargie, son seuil relevé, ou sa priorité passée en P2. Ça a pris environ un mois de petits ajustements. Il n’y a aucun moyen de trouver ces nombres sur le papier, parce que le bon seuil est une propriété de votre trafic, pas de la métrique.

Seule la production réveille

La dernière règle est la plus simple et elle a valu plus qu’elle ne devrait : la staging et la preprod ne réveillent jamais personne. Elles alertent dans un canal, et c’est tout.

Un environnement dont le but même est de casser ne devrait pas pouvoir réveiller un humain. Les alertes venant d’un endroit où la casse est le résultat attendu sont le moyen le plus rapide d’apprendre à une équipe que les alertes ne veulent rien dire, et cette leçon se généralise aux alertes de production en une semaine environ.

Ce que ça n’a pas réglé

Deux choses, toutes les deux pires que le bruit.

Un moniteur du catalogue surveillait complètement la mauvaise chose. Il suivait un job de nettoyage en aval comme approximation du bon fonctionnement des paiements, donc il était indirect, en retard de la durée d’un timeout, et bruyant pour des raisons qui n’avaient rien à voir avec les paiements. Une alerte bien réglée sur une métrique indirecte reste une alerte sur la mauvaise métrique, et c’est un article à part entière.

Et il y a une classe d’erreurs qu’aucun seuil n’attrape : la défaillance qui demande à un humain de rapprocher quelque chose à la main, souvent financier, qui n’est ni une panne ni un taux. Celles-là finissaient toujours dans le déluge de logs et étaient trouvées plus tard, par hasard. Les router vers une vraie file de travail est un problème différent de l’alerting.

Pour la version solo, sur un seul VPS, de tout ça, où la réponse tient en deux conteneurs et exactement une règle d’alerte, voir le dashboard de statut et les logs centralisés et le suivi d’erreurs qui a remplacé les clients qui m’écrivaient pour des 500. L’échelle change la forme de la réponse. Elle ne change pas la question.

À retenir

Un canal d’alertes est un budget d’attention partagé, et chaque moniteur y prélève. Un moniteur qui se déclenche sans changer ce qu’un humain fait n’est pas gratuit, c’est un retrait, et il en suffit d’un certain nombre pour mettre en faillite tout le canal, y compris les alertes qui comptent.

Écrire le catalogue a surtout consisté à supprimer. Ce qui a survécu répond à l’une des deux questions, porte un garde-fou de volume s’il s’agit d’un ratio, s’évalue sur une fenêtre assez longue pour ignorer un hoquet, et vient de la production. Le résultat est un pager qui sonne quelques fois par mois et qui est lu chaque fois, ce qui est la seule métrique qu’une configuration d’alerting possède vraiment.