English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

invité
1 / ?
retour aux leçons

Les trois concepts de défaillance à connaître

Bienvenue

Les systèmes distribués échouent selon des schémas. Une fois que vous maîtrisez ces schémas, chaque post-mortem devient un exercice de reconnaissance plutôt qu'un mystère.

Trois concepts couvrent l'essentiel de l'analyse des pannes en production :

Point de défaillance unique (SPOF) : un composant dont la défaillance entraîne la chute d'un système plus large. Souvent caché : le serveur DNS sur lequel tout le monde dépend ; le certificat contre lequel tout est renouvelé ; le maître de base de données unique.

Échec en cascade : la défaillance d'un composant en déclenche une autre, qui en déclenche une autre encore. Une base de données lente provoque des dépassements de délai (timeouts) dans la couche API, ce qui provoque des nouvelles tentatives (retries), qui surchargent davantage la base de données, ce qui provoque encore plus de dépassements de délai. L'onde de choc se propage.

Rayon d'impact : la part du système qui tombe en panne lorsqu'un composant échoue. Les choix architecturaux déterminent si ce rayon est borné ou non. Un point de défaillance unique (SPOF) a un rayon d'impact illimité. Un service cloisonné (bulkhead) a un rayon d'impact borné.

À la fin de cette leçon, vous serez en mesure de :

- Identifier les points de défaillance unique (SPOF) dans une architecture par inspection

- Reconnaître les motifs d'échec en cascade : troupeau tonnant (thundering herd), tempête de nouvelles tentatives (retry storm), file d'attente de la mort (queue of death)

- Lire une chronologie réelle et distinguer le déclencheur du défaut latent que ce déclencheur a mis en lumière

- Rédiger des actions correctives sans recherche de coupable (blameless) qui ciblent les systèmes plutôt que les personnes, couvrant la prévention / la détection / la récupération

- Raisonner sur les cloisons (bulkheads) et les disjoncteurs (circuit breakers) en tant qu'outils de limitation du rayon d'impact

Identifier le point de défaillance unique

Inspection de l'architecture en couches

Considérons une petite architecture web :

- DNS : api.example.com -> un seul IP de serveur de noms 203.0.113.10 hébergé par un seul fournisseur DNS

- CDN : un seul fournisseur de CDN devant api.example.com

- Ingress : deux machines de proxy inverse derrière un équilibreur de charge

- Backend : six répliques API dans deux zones de disponibilité (trois par zone)

- Base de données : un primaire + une réplique en lecture, dans la même zone de disponibilité

- Cache : cluster Redis, trois nœuds répartis sur les deux mêmes zones de disponibilité

Question : quels composants sont des SPOF ? Indice : les SPOF ne sont pas toujours du type « machine unique » évident. Un cluster de trois machines toutes situées dans une même zone de disponibilité constitue un SPOF en cas de défaillance de cette zone.

Identifiez au moins trois SPOF dans cette architecture. Pour chacun, indiquez ce qui tombe en panne en cas de défaillance et proposez un changement concret permettant d'éliminer le SPOF (sans réécrire l'application).

Trois modèles de cascade classiques

Les pannes se propagent à travers les dépendances

Modèle 1 : Essaim tonitruant (Thundering herd). Une ressource partagée (cache, verrou, base de données) tombe en panne ou redémarre. Tous les clients qui en dépendaient relancent leurs requêtes simultanément. La vague de requêtes submerge le service qui revient en ligne ; les nouvelles tentatives s'accumulent plus vite que la récupération ne peut les absorber ; la récupération n'aboutit jamais.

Modèle 2 : Tempête de réessais (Retry storm). Un service en aval ralentit. Les appelants en amont, au lieu d'échouer, relancent leurs requêtes. Ces réessais multiplient la charge initiale. Le service lent ralentit davantage, déclenchant encore plus de réessais. Finalement, la charge dépasse la capacité même d'une version saine du service.

Modèle 3 : File d'attente de la mort. Une file d'attente de traitement sans rétroaction (backpressure) reçoit des éléments plus vite qu'elle ne les traite. La file grandit sans limite. La mémoire s'épuise ; le consommateur plante ; redémarre ; découvre une file encore plus grande ; plante à nouveau.

Fil conducteur : une petite perturbation initiale déclenche une boucle de rétroaction positive. La réponse du système amplifie la panne au lieu de l'amortir.

Mécanismes d'amortissement

Backoff exponentiel avec jitter. Les clients qui réessument attendent de plus en plus longtemps à chaque tentative, avec un décalage aléatoire. Cela empêche les vagues de réessais synchronisés.

Disjoncteur (Circuit breaker). Un appelant suit le taux d'échec en aval. Au-delà d'un seuil, l'appelant cesse d'appeler pendant une période de refroidissement et échoue immédiatement ses propres requêtes à la place. Cela évite un travail inutile et permet au système aval de se rétablir.

Bulkhead (Cloison). Isoler les ressources par dépendance. Un pool de connexions A pour la base de données, un pool de connexions B séparé pour le cache. Une base de données lente ne peut pas épuiser toutes les connexions ; les appels au cache continuent.

Rejet de charge (Load shedding). En cas de surcharge, abandonner les requêtes à la périphérie plutôt que de les accepter et d'échouer lentement. Un 429 en 1 ms est préférable à un 500 en 30 secondes.

Contre-pression (Backpressure). Ralentir les producteurs lorsque les consommateurs ne peuvent pas suivre. Les files d'attente deviennent bornées ; les émetteurs sont bloqués ; la source originale du travail ressent la friction.

Échec en cascade : déclencheur -> amplification -> effondrement, avec des mécanismes d'amortissement

Diagnostiquer une cascade

L'API d'une équipe s'effondre lors d'un basculement de base de données de routine. Chronologie :

- 14:00:00 — l'opérateur promeut la base de données en attente. Indisponibilité attendue : ~10 secondes.

- 14:00:08 — le nœud principal est indisponible. Les requêtes de la couche API commencent à échouer avec des erreurs de connexion à la base de données.

- 14:00:08 — la couche API effectue des nouvelles tentatives (configuration par défaut : 5 nouvelles tentatives, sans délai exponentiel, espacées de 100 ms).

- 14:00:11 — le nœud en attente est promu et accepte les nouvelles connexions.

- 14:00:11 — la couche API ouvre des milliers de nouvelles connexions à la base de données simultanément (chaque réplique × chaque requête concurrente × chaque nouvelle tentative).

- 14:00:13 — le pool de connexions du nouveau nœud principal est épuisé ; les nouvelles connexions sont rejetées.

- 14:00:13-14:05:00 — les répliques de la couche API épuisent leurs pools de connexions, lèvent des exceptions, s'arrêtent, redémarrent, et le cycle recommence.

- 14:05:00 — l'opérateur arrête manuellement le trafic de la couche API ; la base de données se stabilise.

- 14:10:00 — La restauration progressive du trafic est terminée. Durée totale de l'interruption : ~10 minutes (contre ~10 secondes attendues).

Identifiez le motif de cascade en jeu, nommez les mécanismes d'amortissement qui auraient pu l'empêcher (au moins deux) et expliquez pourquoi le basculement de l'instance principale vers l'instance de secours (qui devait être une coupure de 10 secondes) a au lieu de cela provoqué une interruption de 10 minutes.

DNS SERVFAIL : Deux défauts aggravants

Un post-mortem de forme réelle

Ce qui suit est une version assainie d'un incident réel. Les noms des fournisseurs ont été modifiés, les adresses IP ont été anonymisées ; la forme, la chronologie et les leçons sont réelles.

Résumé

Le site example.com a retourné SERVFAIL depuis tous les résolveurs DNS publics pendant environ 3 à 4 heures. Les 46 autres zones sur le même maître DNS n'ont pas été affectées. Cause racine : deux défauts aggravants.

1. Le fournisseur A (un fournisseur DNS secondaire) a ajouté une nouvelle adresse IP de synchronisation interne qui n'était pas dans la liste d'autorisation allow-axfr-ips du serveur principal.

2. La zone example.com présentait un conflit CNAME en violation de la RFC, vieux de plusieurs années (demo.example.com possédait à la fois des enregistrements CNAME et MX/TXT au même nom) qui a causé le rejet de la zone par le fournisseur A lors d'un AXFR neuf.

Chronologie (UTC)

- ~15:00 — Le fournisseur A ajoute la nouvelle IP de synchronisation 198.51.100.42 à son infrastructure

- 15:02 — première occurrence de AXFR-out denied pour 198.51.100.42 dans les journaux du DNS principal (aucune alerte sur ce signal)

- ~18:00 — Fenêtre d'expiration SOA atteinte ; le fournisseur A supprime la zone example.com de son cache

- ~18:30 — SERVFAIL détecté de manière externe

- ~19:45 — Cause racine identifiée

- 20:00 — 198.51.100.42 ajouté à allow-axfr-ips ; redémarrage du serveur principal

- 20:05 — NOTIFY envoyé ; AXFR initié ; la zone est ENCORE en SERVFAIL (conflit CNAME)

- 20:07 — check-zone révèle 1 erreur : conflit CNAME sur demo.example.com

- 20:09 — Enregistrement CNAME remplacé par un enregistrement A ; vérification de la zone propre (0 erreur)

- 20:10 — NOTIFY envoyé ; AXFR terminé ; le Fournisseur A commence à servir la zone

- 20:11 — dig @8.8.8.8 example.com A renvoie l'IP correcte — RÉSOLU

Pourquoi seulement example.com ?

Les 47 zones partagent le même DNS primaire. Le blocage IP AXFR a affecté toutes les zones. Mais seul example.com avait le conflit CNAME, & seul example.com avait besoin d'un nouvel AXFR au moment où le refus a été appliqué. Les autres zones avaient déjà été actualisées avant le refus ou n'en avaient pas encore besoin.

Défaut latent

Le conflit CNAME sur demo.example.com existait depuis des années. Il fonctionnait parce que le primaire servait la zone depuis sa base de données (tolérant les violations de la RFC) & le Fournisseur A servait à partir de données en cache obsolètes d'avant l'introduction de la violation. Lorsque le Fournisseur A a purgé son cache & a eu besoin de données fraîches, la violation est apparue.

Déclencheur

Le fournisseur A a silencieusement ajouté une nouvelle IP de synchronisation. La liste d'autorisation de l'instance principale ne l'incluait pas. Le transfert AXFR a été refusé. Trois heures plus tard (expiration du SOA), le fournisseur A a supprimé la zone. Le défaut latent est apparu lorsque le système a tenté de récupérer.

Rédiger des actions correctives sans recherche de coupable

Sans recherche de coupable : cibler les systèmes, pas les personnes

Une action corrective sans recherche de coupable désigne ce que le système devrait faire différemment, et non ce qu'une personne devrait faire différemment. « Former l'opérateur » est une recherche de coupable. « Ajouter une vérification automatisée qui détecte cela avant le déploiement » est sans recherche de coupable.

Les bonnes actions correctives sans recherche de coupable se regroupent en trois dimensions :

- Prévention : rendre le problème plus difficile ou impossible

- Détection : le détecter plus tôt s'il se produit

- Récupération : limiter les dégâts lorsqu'il se produit

Chaque élément doit préciser (1) le changement système spécifique, (2) l'équipe responsable, et (3) la dimension qu'il sert.

Rédigez trois éléments d'action sans blâme traitant la post-mortem DNS-SERVFAIL ci-dessus. Répartissez-les entre la prévention / la détection / la récupération (un par dimension). Chaque élément doit préciser un changement système spécifique et une équipe responsable. Ne ciblez AUCUN humain comme cause.

Des compartiments qui coulent sans le navire

Emprunté de l'ingénierie navale

Les navires disposent de cloisons étanches : des parois verticales qui divisent la coque en compartiments. Un compartiment peut se remplir d'eau sans faire couler le navire ; un autre peut échouer sans affecter le reste.

Les systèmes distribués empruntent le même mot et la même idée.

Motif des cloisons : isoler les ressources par dépendance. Un service qui appelle trois API en aval utilise trois pools de connexions distincts, trois pools de threads distincts et trois budgets de réessais distincts. Une dépendance en aval lente ou défaillante ne peut pas consommer les ressources allouées aux deux autres.

Sans cloisons : une dépendance lente épuise le pool de threads partagé ; les appels aux autres dépendances sont bloqués en attendant des threads ; le service entier devient non réactif.

Avec cloisons : une dépendance lente épuise son propre pool ; les appels à celle-ci échouent rapidement ; les appels aux autres dépendances continuent normalement ; le rayon de l'explosion reste limité à la dépendance défaillante.

Disjoncteurs

Motif du disjoncteur (Circuit breaker) : un wrapper à état autour d'une dépendance en aval qui suit le taux d'échec. Trois états :

- Fermé (normal) : les appels passent. Les échecs sont comptés.

- Ouvert (déclenché) : une fois un seuil d'échec atteint (par exemple, 50 % d'échecs au cours des 30 dernières secondes), le disjoncteur s'ouvre. Les appels échouent immédiatement sans tenter d'accéder à la dépendance. Cela évite à l'appelant de gaspiller des ressources et protège la dépendance de la charge alors qu'elle est en mauvaise santé.

- Demi-ouvert (test) : après une période de refroidissement, le disjoncteur laisse passer une petite fraction des appels. S'ils réussissent, il se referme pour revenir à la normale. S'ils échouent, il rouvre pour une nouvelle période de refroidissement.

L'insight clé : le disjoncteur empêche les efforts inutiles pendant les périodes de mauvaise santé connues et donne à la dépendance en aval une chance de récupérer sans charge continue.

Les cloisons (Bulkheads) limitent le rayon de l'explosion. Les disjoncteurs empêchent l'explosion de se maintenir.

Limiter le rayon de l'explosion

Votre service API appelle quatre services en aval : le Service Utilisateur, le Service de Recommandation, le Service de Notification et une API de Paiement tierce. L'équipe a entendu que « le Service de Recommandation est un peu instable » et souhaite s'assurer que, lorsqu'il échoue, le reste du système reste sain.

Aujourd'hui, le service utilise un seul pool de threads partagé de 200 threads & un seul pool de connexions HTTP partagé. Les quatre dépendances en aval sont en concurrence pour ces ressources. Il n'y a pas de disjoncteurs.

Proposez une conception de cloisonnement (bulkhead) + disjoncteur pour ce service API. Soyez précis : comment partitionnez-vous les pools de threads / connexions entre les quatre dépendances, quels seuils de disjoncteur sont-ils pertinents pour le service de recommandation instable, & que devrait faire l'API destinée aux utilisateurs lorsque le service de recommandation est ouvert (circuit ouvert) ?

Concevoir une revue des modes de défaillance

Synthèse

Vous avez appris à identifier les points de défaillance unique (SPOF) par inspection, à reconnaître les schémas de défaillance en cascade, à distinguer le déclencheur du défaut latent lors de la lecture d'une analyse post-mortem, à rédiger des actions correctives sans blâme couvrant la prévention / la détection / la récupération, et à limiter le rayon d'impact à l'aide de cloisons (bulkheads) + disjoncteurs (circuit breakers) + dégradation gracieuse.

Appliquez les cinq.

Votre équipe lance un nouveau service search.example.com qui dépend de trois services en aval : un index de recherche principal (index.example.com), un service d'analyse (analytics.example.com) et un service de recommandations (recs.example.com). L'équipe souhaite que vous dirigiez une « revue des modes de défaillance » avant le lancement.

Décrivez la revue des modes de défaillance que vous dirigeriez. Incluez : comment vous identifieriez les SPOF (une technique), comment vous préviendriez la défaillance en cascade entre le service de recherche et ses trois services en aval (deux motifs), un élément d'action concret pour le service de recommandations (que l'équipe a signalé comme le moins fiable), et le suivi (monitoring) que vous exigeriez pour être en place au moment du lancement.

Où va ce cours ensuite

Où va ce cours ensuite

Vous savez maintenant identifier un SPOF, reconnaître une cascade, lire un post-morter de manière productive, rédiger des éléments d'action sans blâme et limiter la portée des incidents par conception.

La dernière leçon de ce cours (cs_distsys_observability_and_capacity) enseigne quoi mesurer pour détecter un problème avant que les utilisateurs ne le fassent. Vérifications de santé, points d'entrée de version, les quatre signaux dorés à un niveau de proxy et comment les décisions de capacité de pointe sont liées aux données observées.

Leçon complémentaire : geometry_of_failure_modes_and_blast_radius dérive la centralité d'intermédiarité (quel nœud du graphe est le goulot d'étranglement) et la coupe minimale (la limite de la portée des incidents).

Bravo. En avant.