Configuration de la réplication Kafka : garantir la durabilité et la disponibilité des données

Configurez la réplication Kafka, l'ISR, les accusés de réception des producteurs et la connaissance des racks sans compromettre la durabilité.

Configuration de la réplication Kafka : garantir la durabilité et la disponibilité des données

La configuration de la réplication Kafka est l'étape où un cluster cesse d'être un tas de courtiers pour se comporter comme un système fiable en cas de panne. Les paramètres ne sont pas compliqués en eux-mêmes : facteur de réplication, réplicas synchronisés, accusés de réception des producteurs, élection du leader et placement des racks. La difficulté réside dans le fait qu'ils ne prennent leur sens que lorsqu'ils sont combinés.

Un sujet avec trois réplicas peut encore perdre des données acquittées si les producteurs utilisent des accusés de réception faibles. Un producteur utilisant acks=all peut encore échouer à écrire si min.insync.replicas est trop strict par rapport au nombre de courtiers actifs. Un cluster réparti sur plusieurs zones de disponibilité peut encore rencontrer des problèmes si tous les réplicas d'une partition chaude se retrouvent dans le même domaine de défaillance. La réplication n'est pas une simple case à cocher.

La façon dont j'aime concevoir la réplication Kafka est simple : pour chaque partition, Kafka conserve plusieurs copies, en choisit une pour accepter les lectures et les écritures, et maintient les autres copies suffisamment à jour pour que l'une d'elles puisse prendre le relais. Votre travail consiste à décider combien de copies sont suffisantes, combien doivent être synchronisées avant qu'une écriture soit considérée comme réussie, et si le cluster doit privilégier la disponibilité plutôt que la sécurité des données.

Un sujet Kafka est divisé en partitions. Chaque partition a un réplica leader et zéro ou plusieurs réplicas followers. Les producteurs écrivent sur le leader. Les consommateurs lisent normalement depuis le leader. Les followers récupèrent les enregistrements du leader et maintiennent leurs journaux locaux alignés. Si le courtier du leader tombe en panne, Kafka élit un nouveau leader parmi les réplicas considérés comme des candidats sûrs.

Cette liste de candidats sûrs est l'ISR, abréviation de in-sync replicas (réplicas synchronisés). Un réplica est dans l'ISR lorsqu'il suit le leader suffisamment près selon les règles de retard de réplica de Kafka. Si un follower cesse de récupérer les données, prend trop de retard ou que le courtier disparaît, Kafka le retire de l'ISR. Lorsqu'il rattrape son retard, il peut le réintégrer.

Ce détail est important car l'ISR est ce qui rend la durabilité de Kafka plus qu'un vœu pieux. Avec acks=all, le leader n'accuse pas réception d'une demande de production tant que l'enregistrement n'a pas été répliqué sur les réplicas synchronisés requis. L'exigence exacte est contrôlée par min.insync.replicas. Si le sujet a replication.factor=3 et min.insync.replicas=2, Kafka exige au moins deux réplicas synchronisés avant qu'une écriture avec acks=all puisse réussir.

Cette combinaison est courante en production car elle offre un équilibre pratique. Un courtier peut tomber en panne et le sujet peut toujours accepter des écritures fortement acquittées. Si un deuxième courtier tombe en panne avant que le premier ne revienne, les producteurs utilisant acks=all devraient commencer à voir des erreurs telles que NotEnoughReplicas ou NotEnoughReplicasAfterAppend. C'est gênant lors d'un incident, mais c'est généralement le comportement correct. Kafka refuse de faire comme si une écriture était durable alors qu'il n'y a pas assez de copies sûres.

Voici la base de référence typique pour un cluster normal de trois courtiers ou plus :

default.replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false

Ces valeurs ne rendent pas automatiquement chaque charge de travail sûre, mais elles constituent un point de départ sain. default.replication.factor=3 signifie que les nouveaux sujets obtiennent trois copies, sauf indication contraire dans la commande de création. min.insync.replicas=2 signifie qu'au moins deux réplicas doivent être synchronisés pour les écritures fortes. unclean.leader.election.enable=false indique à Kafka de ne pas élire un réplica obsolète comme leader simplement pour maintenir une partition accessible en écriture.

Ne définissez pas un facteur de réplication supérieur au nombre de vos courtiers. Kafka ne peut pas placer trois réplicas sur trois courtiers différents s'il n'existe que deux courtiers. Dans les petits clusters de développement, replication.factor=1 est acceptable car la commodité prime sur la tolérance aux pannes. En production, 1 signifie qu'une seule perte de courtier peut rendre les données indisponibles et peut perdre définitivement les enregistrements stockés uniquement sur ce courtier.

Le côté producteur doit correspondre au côté sujet. Pour les données importantes, utilisez acks=all. Activez également l'idempotence sauf si vous avez une raison spécifique de ne pas le faire. Dans les clients Kafka modernes, les producteurs idempotents sont le choix normal pour réduire les doublons causés par les nouvelles tentatives.

acks=all
enable.idempotence=true
retries=2147483647
max.in.flight.requests.per.connection=5

Ne copiez pas aveuglément la valeur de retry dans chaque client sans comprendre votre version client et vos exigences de livraison. L'idée importante est qu'une production Kafka durable nécessite généralement des nouvelles tentatives, l'idempotence et acks=all ensemble. Si vous définissez acks=1, le leader peut accuser réception d'un enregistrement avant que les followers ne l'aient copié. Si ce leader meurt au mauvais moment, un enregistrement acquitté peut disparaître. Cela est acceptable pour certains flux de télémétrie. Ce n'est pas acceptable pour les paiements, les pistes d'audit, les mouvements de stock ou tout ce qu'une équipe en aval considère comme une source de vérité.

Lorsque vous créez un sujet, définissez les choix de réplication délibérément plutôt que de vous fier aux valeurs par défaut du courtier qui peuvent être présentes :

kafka-topics.sh --create   --bootstrap-server broker1:9092   --topic orders.v1   --partitions 12   --replication-factor 3   --config min.insync.replicas=2

Le nombre de partitions est distinct de la réplication. Douze partitions avec un facteur de réplication de trois signifie trente-six réplicas de partition au total. Cela a des coûts de stockage, de réseau, de descripteurs de fichiers et de métadonnées du contrôleur. La réplication améliore la durabilité, mais elle n'est pas gratuite.

Pour les sujets existants, modifier min.insync.replicas est simple :

kafka-configs.sh --alter   --bootstrap-server broker1:9092   --entity-type topics   --entity-name orders.v1   --add-config min.insync.replicas=2

Modifier le facteur de réplication pour un sujet existant dépend de la version de Kafka et des outils. Les versions récentes de Kafka prennent en charge kafka-reassign-partitions.sh et, dans certains cas, des workflows de modification de sujet qui facilitent les augmentations. Dans les clusters plus anciens, augmenter la réplication implique généralement de générer et d'exécuter un plan de réaffectation des partitions. Diminuer la réplication est plus sensible car vous supprimez des copies. Traitez-le comme une opération planifiée, pas comme une commande tapée à la légère lors d'un incident bruyant.

Une réaffectation doit être limitée si le sujet est volumineux ou si le cluster est déjà occupé. Le rattrapage de réplication lit les anciennes données des réplicas existants et les écrit sur les nouveaux. Cela peut voler de la capacité disque et réseau aux producteurs et consommateurs en direct. Un runbook sûr comprend généralement une fenêtre de maintenance, une sortie --describe avant et après, des limites de réaffectation et un plan de retour arrière.

Vous pouvez inspecter un sujet comme ceci :

kafka-topics.sh --describe   --bootstrap-server broker1:9092   --topic orders.v1

Regardez trois champs dans la sortie : Leader, Replicas et Isr. Replicas est l'ensemble attribué. Isr est l'ensemble actuellement synchronisé. Si Replicas est 1,2,3 mais que Isr est 1,2, le courtier 3 est en retard ou indisponible pour cette partition. Si de nombreuses partitions montrent un courtier manquant dans l'ISR, examinez le disque, le réseau, l'état du processus et les journaux de ce courtier. Si seulement quelques partitions chaudes sont affectées, le leader peut être surchargé ou la partition peut avoir un trafic inhabituellement élevé.

L'élection de leader non sain mérite une attention particulière. Si tous les réplicas synchronisés d'une partition sont perdus, Kafka a deux choix. Il peut laisser la partition indisponible jusqu'à ce qu'un réplica sûr revienne, ou il peut élire un réplica désynchronisé et risquer de perdre des enregistrements qui ont été acquittés sur l'ancien leader. unclean.leader.election.enable=false choisit la sécurité. true choisit la disponibilité au risque de perte de données.

Il existe des charges de travail où une élection non saine peut être défendable : données de clics éphémères, métriques jetables, ou un pipeline où les systèmes en amont peuvent tout rejouer. Pour la plupart des données métier, laissez-le désactivé. Perdre la disponibilité d'une partition est douloureux, mais une perte silencieuse de données est pire car les consommateurs peuvent continuer comme si de rien n'était.

La réplication avec connaissance des racks aide à faire face à une autre classe de pannes. Si vos courtiers sont répartis sur des racks, des zones ou des hôtes avec des chemins d'alimentation/réseau partagés, indiquez à Kafka où se trouve chaque courtier :

broker.rack=zone-a

Définissez la valeur correcte sur chaque courtier. Kafka essaiera de répartir les réplicas sur les racks afin qu'une seule panne de zone soit moins susceptible de supprimer toutes les copies d'une partition. Ce n'est pas magique. Vous avez toujours besoin d'assez de courtiers dans chaque zone, d'assez de disque et d'un placement minutieux des partitions. Mais sans broker.rack, Kafka n'a aucun moyen de savoir que deux courtiers partagent le même domaine de défaillance.

Surveillez la réplication en continu. Les signes d'alerte précoce les plus utiles sont les partitions sous-répliquées, les partitions hors ligne, les événements de rétrécissement de l'ISR et les erreurs de production liées à des réplicas insuffisants. Dans les configurations basées sur Prometheus, les équipes surveillent couramment les métriques des courtiers Kafka pour les partitions sous-répliquées et hors ligne, puis associent ces alertes aux métriques de disque, réseau et JVM des courtiers.

Une bonne question en cas d'incident est : l'ISR a-t-il rétréci parce qu'un courtier est mort, parce que la réplication ne peut pas suivre, ou parce que le réseau n'est pas fiable ? Le correctif diffère. Un courtier mort nécessite une reprise de service. Un courtier lent peut nécessiter un remplacement de disque, une enquête sur les E/S ou moins de leaders de partition. Un problème réseau peut se manifester par des déconnexions répétées et un retard du récupérateur même si le CPU et le disque semblent normaux.

Les redémarrages roulants des courtiers sont un autre endroit où les paramètres de réplication montrent leur valeur. Redémarrez un courtier à la fois. Attendez que les partitions retrouvent un ISR sain avant de redémarrer le courtier suivant. Si vous redémarrez les courtiers trop rapidement avec min.insync.replicas=2, les producteurs peuvent commencer à échouer car trop peu de réplicas sont synchronisés. Cet échec est attendu, mais vous pouvez l'éviter avec de la patience et une surveillance.

La liste de contrôle pratique est courte. Utilisez un facteur de réplication de trois pour la plupart des sujets de production. Utilisez min.insync.replicas=2 avec le producteur acks=all pour les données importantes. Gardez l'élection de leader non sain désactivée sauf si les données sont explicitement jetables. Répartissez les réplicas sur les domaines de défaillance avec la connaissance des racks. Surveillez la santé de l'ISR, pas seulement la disponibilité des courtiers. Et testez vos hypothèses en redémarrant un courtier dans une fenêtre contrôlée avant qu'une vraie panne ne le fasse pour vous.

Un détail qui aide lors des révisions est de séparer la durabilité de la disponibilité en langage clair. La durabilité demande : « Après que Kafka a dit que l'écriture a réussi, combien de pannes peuvent se produire avant que cet enregistrement acquitté ne soit en danger ? » La disponibilité demande : « Les producteurs et les consommateurs peuvent-ils encore utiliser la partition maintenant ? » Des paramètres forts réduisent parfois la disponibilité car Kafka rejettera les écritures plutôt que d'accepter des données faiblement répliquées. Ce n'est pas un échec de Kafka. C'est Kafka qui honore le contrat que vous avez configuré.

Par exemple, imaginez un sujet avec un facteur de réplication de trois, min.insync.replicas=2 et des producteurs utilisant acks=all. Le courtier 1 est le leader, les courtiers 2 et 3 sont les followers. Si le courtier 3 tombe en panne, l'ISR devient 1,2. Les écritures réussissent toujours car deux réplicas sont synchronisés. Si le courtier 2 tombe ensuite en panne avant que le courtier 3 ne revienne, l'ISR devient seulement 1. Les écritures échouent. Certaines équipes voient cela pour la première fois en production et demandent pourquoi Kafka est en panne alors que le leader est toujours en vie. La réponse est que le sujet est toujours disponible pour certaines lectures, mais il n'est pas sûr pour des écritures fortement acquittées.

Vous devez également penser à la récupération des consommateurs. La réplication protège les copies côté courtier des enregistrements. Elle ne protège pas automatiquement les offsets des consommateurs contre toutes les erreurs de workflow. Les offsets des consommateurs sont également stockés dans Kafka, généralement dans __consumer_offsets, donc ce sujet interne a également besoin d'une réplication saine. Si les sujets utilisateur sont soigneusement configurés mais que les sujets internes ont été créés avec une réplication faible lors d'une première construction du cluster, le comportement de basculement peut encore être pire que prévu. Vérifiez la réplication des sujets internes dans le cadre d'une revue de préparation à la production.

Dans les clusters multi-locataires, tous les sujets ne méritent pas la même configuration. Un sujet de métriques jetable avec un volume élevé et une faible valeur métier peut utiliser une rétention plus courte et tolérer des garanties plus faibles. Un sujet de facturation ne le devrait pas. L'erreur est de laisser les valeurs par défaut accidentelles décider de cette distinction. Mettez par écrit les classes de sujets : flux d'événements critiques, télémétrie rejouable, sujets d'état compactés, sujets de développement temporaires. Ensuite, associez chaque classe aux paramètres de réplication, d'ISR, de rétention et de producteur.

Lors des incidents, évitez de modifier les paramètres de durabilité simplement pour faire taire les erreurs, à moins que tout le monde ne comprenne le compromis. Réduire min.insync.replicas de 2 à 1 peut faire bouger les producteurs, mais cela signifie également que les écritures acquittées peuvent vivre sur un seul courtier. Activer l'élection de leader non sain peut restaurer la disponibilité des partitions, mais des réplicas obsolètes peuvent perdre des enregistrements. Parfois, l'entreprise peut choisir ce compromis. Cela doit être une décision consciente lors d'un incident, pas un raccourci caché de l'opérateur.