Santé des services
Quarante tuiles vertes ne répondent pas à la question de 3 h du matin : quelque chose qui compte pour mes utilisateurs est-il réellement cassé ? Le tableau Santé des services, lui, y répond. Il consolide la santé RED de chaque service en un statut de groupe, dispose les groupes par tier de criticité, et applique une règle que les dashboards laissent d'habitude à la charge du lecteur : un service ne peut pas s'afficher vert quand une dépendance critique est rouge.
Le tout est dérivé des traces que vous envoyez déjà : pas de nouveau stockage, rien à instrumenter. Livré à lui-même, le hub n'émet aucun trafic vers vos services — les sondes de point d'entrée sont la seule exception, délibérée, et uniquement si vous les déclarez.
Du RED au statut
Chaque service reçoit l'un de quatre états, calculé à partir de ses données RED sur la fenêtre sélectionnée :
healthy— dans son budget d'erreur et son objectif de latence.degraded— au-delà du taux d'erreur d'avertissement, ou au-delà de son objectif de p95.down— au-delà du taux d'erreur critique.idle— pas assez de trafic pour juger. Un filtre de trafic passe en premier : un service silencieux s'affiche idle, jamais down — pas de réveil à 3 h du matin parce qu'un batch s'est endormi.
Chaque statut porte une raison en langage clair, pas seulement une couleur :
error rate 4.2% ≥ 1% budget, ou p95 780ms ≥ 500ms objective. Les seuils se
résolvent par précédence — surcharges par service > surcharges par tier >
valeurs par défaut — vous pouvez donc tenir le T0 à 0,5 % pendant que la
longue traîne vit avec les valeurs intégrées.
Groupes et tiers
Le regroupement est hybride, si bien que le tableau est utile avant toute configuration :
- Les groupes nommés viennent de la configuration : un sélecteur associe les services par nom ou par namespace Kubernetes, et leur assigne un tier de criticité (T0/T1/T2).
- Tout le reste se regroupe automatiquement par namespace au tier par
défaut. Les services sans label de namespace se rassemblent dans un groupe
(unlabeled)jusqu'à ce que la configuration les réclame.
Le tableau /health dispose les groupes en couloirs par tier, le plus
critique en premier — le coup d'œil de l'astreinte commence là où le rayon
d'impact est le plus large.
Propagation des dépendances
Votre service checkout peut être parfaitement sain et totalement inutile, parce que payments est down. La santé des services l'encode :
- Les dépendances viennent de la carte des services que vous avez déjà — aucune déclaration supplémentaire.
- Une arête est critique quand sa cible est un service T0 (ou quand vous
la forcez avec
criticalEdges). - Une dépendance critique qui passe
downentraîne ses dépendants au moins endegraded. Chaque membre rapporte à la fois son statut de base et son statut effectif, si bien que le panneau de détail peut dire exactement cela : sain en soi, dégradé parce que payments est down.
La propagation se fait sur un saut, sans risque de cycle, et uniquement pour
down — des limites v1 assumées (voir plus bas).
Cas d'usage
- Le coup d'œil de l'astreinte. Un seul écran, des couloirs par tier, le pire en premier. Si le couloir T0 est vert, retournez vous coucher — et s'il ne l'est pas, la raison vous dit pourquoi avant même d'avoir ouvert un dashboard.
- Encoder la règle d'or au lieu de la re-déduire. « Checkout dépend de payments » vit dans la topologie, pas dans la tradition orale. Les nouveaux arrivants voient la chaîne de dépendances dans le panneau de détail.
- Re-classer en plein incident. Promouvez en T0 un service devenu soudain critique dans Paramètres → Groupes, et la lecture de santé suivante en tient compte — sans restart, sans redéploiement, sans PR. Les installations GitOps peuvent continuer à le faire depuis les valeurs du chart.
- Alimenter une status page.
GET /api/v1/health/groupsrenvoie tout le tableau en JSON — statut global, consolidations par groupe, raisons. Branchez-le sur une status page externe ou un écran mural.
Configuration
Le module est activé par défaut (modules.serviceHealth.enabled) — c'est
de la valeur gratuite tirée de données que vous collectez déjà. Le désactiver
retire les routes d'API et l'entrée de menu, rien d'autre, car il ne possède
aucun stockage.
Les groupes, depuis l'interface
Paramètres → Groupes est l'endroit où la plupart des équipes devraient définir leurs groupes : un nom, un niveau de criticité et les espaces de noms ou services couverts. L'enregistrement s'applique à la lecture de santé suivante. Les écritures exigent le rôle administrateur ; quiconque peut voir le tableau peut voir les définitions qui le sous-tendent.
Les services qu'aucun groupe ne revendique sont toujours rassemblés par espace
de noms au niveau defaultTier : une installation qui ne configure rien
conserve la vue qu'elle a aujourd'hui.
Les groupes, depuis les valeurs du chart
Le regroupement vit aussi dans le bloc Helm serviceGroups, rendu en ConfigMap
que le hub lit (montée sur AVURUOBS_GROUPS_CONFIG) et recharge à chaud en
~15 s après un kubectl edit — la bonne primitive pour une installation
GitOps :
serviceGroups:
defaultTier: T2 # tier des services qu'aucun groupe ne réclame
groups:
- name: payments
tier: T0
selector:
namespaces: [payments]
services: []
# Seuils façon SLO. Précédence : services > tiers > defaults > intégrés.
thresholds:
defaults:
errorRateWarn: 0.01
errorRateCrit: 0.05
latencyP95ObjectiveMs: 500
minSampleCount: 5
tiers:
T0: { errorRateWarn: 0.005, errorRateCrit: 0.02, latencyP95ObjectiveMs: 300 }
services:
payments: { latencyP95ObjectiveMs: 800 }
# Forcer certaines arêtes critiques même si la cible n'est pas T0 (rare).
criticalEdges: []
# - { from: web, to: legacy-billing }
La validation échoue bruyamment : un tier invalide ou un sélecteur vide est
rejeté au démarrage (et un tier invalide échoue déjà au helm template contre
le schéma de valeurs du chart). L'interface est tenue aux mêmes règles : rien de
ce que vous y enregistrez ne fera échouer le prochain démarrage du hub.
Les seuils, criticalEdges et defaultTier restent pour l'instant réservés au
chart — ils vivent dans le même objet mais forment une surface plus tranchante
que l'appartenance à un groupe.
Quand les deux définissent le même nom
Le chart l'emporte. Une installation qui gère ses groupes dans Git ne doit
pas les voir écrasés depuis un navigateur : les groupes déclarés dans le chart
s'affichent en lecture seule et toute tentative d'en créer ou d'en modifier un
est refusée plutôt qu'enregistrée — le conflit apparaît au moment de
l'enregistrement, pas au prochain helm upgrade. Si les valeurs du chart
revendiquent plus tard un nom que vous aviez déjà créé, ce groupe est signalé
comme remplacé dans l'éditeur au lieu de cesser silencieusement de regrouper
quoi que ce soit : vous pouvez le déplacer ou le supprimer.
Les deux sources sont fusionnées dans un unique résolveur partagé par l'API de santé et l'évaluateur d'alertes : le regroupement sur lequel les alertes se déclenchent est donc toujours celui qu'affiche le tableau.
API
GET /api/v1/health/groups?start&end— le tableau entier : statut global, statut par groupe avec raison de consolidation et RED agrégé, et les membres avec statut de base vs effectif et leurs dépendances.GET /api/v1/health/groups/{name}— un seul groupe, pour les drill-downs.
Voir la référence API.
Limites de la v1
- La propagation se fait sur un saut et seulement pour
down. Une dépendancedegradedne se propage pas, et les chaînes transitives (A→B→C) ne cascadent pas au-delà du premier saut. - La latence de groupe est le p95 du pire membre, pas un vrai quantile au niveau du groupe.
- Les services sans label se rassemblent dans
(unlabeled)jusqu'à ce qu'un groupe les nomme. - Les groupes sont valables pour toute l'instance, pas par projet.
:::note Cette page s'étoffe La santé des services est le signal dont se nourrissent les alertes — une règle peut vous prévenir quand un groupe, un service ou un tier va mal. Voir l'entrée du changelog, la Feuille de route et l'État des fonctionnalités. :::
Sondes de point d'entrée : la santé quand personne n'appelle
Tout ce qui précède est dérivé de requêtes faites par d'autres. Reste une question à laquelle cela ne peut structurellement pas répondre : un groupe sans trafic dans la fenêtre est soit au repos, soit mort, et aucune télémétrie observée ne le dira. À 3 h du matin, c'est la seule question qui compte.
Une sonde de point d'entrée est une requête HTTP planifiée rattachée à un groupe — la requête que nous faisons, pour que le silence cesse d'être ambigu :
serviceGroups:
groups:
- name: core
tier: T0
selector:
namespaces: [storefront]
checks:
- id: core-login
url: https://app.example.com/api/health
interval: 60s
expect:
status: 200
maxLatency: 800ms
- Un groupe silencieux dont la sonde passe est rapporté
healthy— pasidle. Quelque chose sert ; nous avons demandé. - Un groupe silencieux dont la sonde échoue est rapporté
down. Personne d'autre ne l'appelle, et la seule chose qui l'a fait n'a pas été servie. - Un groupe avec du trafic et une sonde en échec est au minimum
degraded: le trafic dit que quelque chose fonctionne, la sonde dit que quelque chose ne fonctionne pas.
Deux échecs, jamais un seul
Une sonde ne fait pas bouger un groupe tant qu'elle n'a pas échoué deux fois de suite. Un échec isolé, c'est un redémarrage progressif, un bail perdu ou un paquet tombé — et un tableau qui y réagit est un tableau que l'on cesse de lire. Le premier échec reste visible sur la fiche du groupe, distingué d'une sonde qui est réellement en échec.
Une sonde est du trafic, pas un canal parallèle
Chaque sonde émet son propre span. Une vérification apparaît donc dans vos métriques RED, sur la carte des services et dans l'explorateur de traces exactement comme n'importe quel autre client — et une sonde en échec sur ce tableau renvoie directement vers la trace de la requête qui a échoué, au lieu de vous laisser corréler à la main.
Les spans de sonde sont classés comme trafic auxiliaire par le mécanisme qui écarte déjà les spans de health check du RED destiné aux utilisateurs : vos chiffres ne bougent donc pas parce que vous avez commencé à sonder.
Le hub envoie ces spans à la passerelle en client OTLP ordinaire, à travers le même récepteur et la même vérification de clé d'ingestion que tout le reste. Il n'écrit jamais directement dans le stockage.
:::info Aucune sonde, aucun coût N'en déclarez aucune et rien ne s'exécute : pas d'ordonnanceur, pas de lignes, aucun changement de comportement. :::
Les consulter
GET /api/v1/checks liste ce qui est déclaré ; GET /api/v1/checks/{id}/results
renvoie les résultats récents d'une sonde, avec l'identifiant de trace de chacune.
Ce que les sondes ne sont pas
- Pas un remplacement des probes Kubernetes. Liveness et readiness servent l'orchestrateur ; celles-ci servent les humains, et s'exécutent depuis l'extérieur du pod.
- Pas des parcours scriptés. Requêtes simples uniquement — les enchaînements connexion-puis-navigation sont une question plus vaste.
- Pas protégées contre les cibles internes, volontairement. Une sonde existe pour interroger vos propres services, précisément sur les réseaux privés qu'une protection SSRF bloquerait. Les URL sont une configuration que vous écrivez, jamais une entrée utilisateur ; ce qui est imposé, c'est un délai d'expiration strict et le refus de suivre une redirection vers un autre hôte.