Aller au contenu principal

Le facteur de réplication n'est pas la tolérance aux pannes : dimensionner un cluster Zaris pour la durabilité sur Kubernetes

· 8 minutes de lecture
Clustron Team
Ingénierie des systèmes distribués

Dimensionner un cluster Zaris pour la durabilité sur Kubernetes

Il existe un raisonnement tentant lorsqu'on déploie un store répliqué sur Kubernetes : j'ai défini replicationFactor: 2, donc chaque partition a une sauvegarde, donc je survis à la perte d'un nœud. Cela semble inattaquable. C'est aussi, pour une taille de cluster étonnamment courante, faux — et faux de la manière silencieuse qui ne se révèle que la première fois qu'un pod meurt réellement.

Zaris sur Kubernetes stocke les données en mémoire et les rend durables par réplication, pas par disque : le volume de données du nœud est un emptyDir, et un pod reprogrammé se reconstruit à partir de la copie détenue par un autre pod. Cette conception est saine — mais elle repose entièrement sur l'existence d'une copie sur un autre pod. Que cette copie existe n'est pas décidé par replicationFactor seul. C'est décidé par l'arithmétique entre votre nombre de pods et votre facteur de réplication, et si vous vous trompez dans cette arithmétique, le store tournera parfaitement, servira chaque lecture et écriture, et détiendra silencieusement une partition qui n'a nulle part où se répliquer.

La forme du déploiement​

Le chart Helm de Zaris déploie le niveau nœud comme un StatefulSet, une instance de nœud par pod (zaris-0, zaris-1, …), chacune propriétaire de shard en mémoire. Un Service headless donne à chaque pod une identité DNS stable, la disponibilité (readiness) est conditionnée à la convergence du cluster, et un PodDisruptionBudget plus une fenêtre de terminaison à drain gracieux protègent l'ensemble pendant les perturbations volontaires.

# values.yaml (defaults)
node:
replicas: 4 # pod count — ONE node instance per pod
replicationFactor: 2 # copies of each partition, on distinct pods

Les notes du chart lui-même sont sans détour sur l'origine de la durabilité :

Les données de nœud sont en emptyDir. La durabilité en mémoire passe par la réplication (RF), pas le disque ; un nœud reprogrammé se reconstruit à partir des réplicas.

Un pod de nœud peut donc être tué et reprogrammé toute la journée sans perte de données — à condition que ses partitions aient un réplica vivant sur un autre pod depuis lequel se reconstruire. Cette condition est tout l'enjeu.

Comment les partitions atterrissent sur les pods​

Une partition dans Zaris est détenue par RF instances de nœud distinctes — un propriétaire actif plus RF−1 réplicas — et les réplicas sont toujours placés sur des pods différents (un réplica sur le même pod que son actif ne serait aucune protection). Le runtime place les partitions de façon gloutonne :

Le runtime remplit une partition jusqu'à son RF complet avant de former la suivante.

Cette seule phrase est la clé de toute l'histoire du dimensionnement. Les instances sont consommées RF par RF. Donc le nombre de partitions entièrement répliquées que vous obtenez est :

fully-replicated partitions = ⌊ instances ÷ RF ⌋

et toutes les instances restantes — instances mod RF d'entre elles — forment une partition en copie unique sans pair pour se répliquer. C'est une véritable partition qui sert. Elle n'a simplement aucune sauvegarde.

Avec quatre pods au RF 2, vous obtenez deux partitions, chacune avec deux copies sur des pods distincts — perdez n'importe quel pod et la copie survivante poursuit tandis que la partition se re-réplique sur le pod reprogrammé. Avec trois pods au RF 2, les pods 0 et 1 forment la partition A au RF complet, mais le pod 2 reste en surplus et forme la partition B avec une copie unique. Ajoutez un quatrième pod et il devient le réplica de B. Jusque-là, B est un risque.

La règle de dimensionnement, sous forme de tableau​

Comme chaque partition a besoin de RF pods distincts et que Kubernetes exécute une instance par pod, la règle est simplement :

Exécutez un nombre de pods qui est un multiple de RF. Avec RF 2, utilisez un nombre pair de pods.

Pods (1 instance chacun)RFPartitions entièrement répliquéesSurvit à la perte d'un pod ?
221Oui
321 (+ 1 en copie unique)Non — perdre le pod en copie unique perd cette partition
422Oui
623Oui

Le schéma se généralise : N partitions au RF 2 nécessitent 2N pods ; au RF 3, elles nécessitent 3N. La mise à l'échelle horizontale consiste à ajouter des pods un RF complet à la fois si vous voulez que chaque nouvelle partition naisse durable.

Pourquoi trois pods au RF 2 est le piège​

Trois est le nombre qui mord, car trois pods ressemblent à un cluster sain et hautement disponible. Il passe la disponibilité (readiness). Il sert le trafic. kubectl get pods montre zaris-0..2 tous 1/1 Ready. Rien n'est manifestement anormal.

Ce qui est anormal reste invisible jusqu'à ce que le pod en copie unique — celui qui détient l'unique copie de la partition B — soit le pod qui meurt. À cet instant, les clés de B sont perdues, définitivement, car il n'y a jamais eu de deuxième copie depuis laquelle se reconstruire. Ce n'est pas un bug ; c'est la même mise en garde que faire tourner une seule machine, juste appliquée à une partition plutôt qu'à tout le store. La carte ne se régénère que lorsque toutes les copies d'une partition sont perdues, et pour une partition en copie unique « toutes les copies » signifie un pod.

Cela a été mesuré, non supposé. Sur un cluster kind :

  • À replicas=4 (2 partitions × RF 2), tuer le pod propriétaire actif d'une partition a perdu 0 clé sur 200 — le réplica a pris le relais et le RF s'est auto-restauré sur le pod reprogrammé.
  • À replicas=3, tuer le pod en copie unique a perdu purement et simplement les clés de cette partition.

Même store, même réglage de RF, même chaos. La seule variable était de savoir si le nombre de pods était un multiple du RF.

Les garde-fous que le chart vous offre​

Le chart ne laisse pas cela au folklore. Installez un cluster dont le nombre de pods n'est pas un multiple du RF et helm install affiche un avertissement de durabilité dans ses notes :

⚠  DURABILITY WARNING
node.replicas (3) is not a multiple of node.replicationFactor (2), so at least one
partition is SINGLE-COPY and will LOSE its data if that pod is lost. For fault
tolerance set node.replicas to a multiple of 2 (e.g. 4).

Au-delà de l'avertissement, plusieurs valeurs par défaut sont fixées pour qu'un cluster correctement dimensionné se comporte bien sous perturbation :

  • Disponibilité conditionnée à la convergence. /readyz ne passe qu'une fois la carte des partitions et la propriété actives, de sorte qu'un pod Ready est routable — Kubernetes n'enverra pas de trafic à un nœud qui n'a pas encore pris possession.
  • PodDisruptionBudget. Par défaut, au plus un nœud est volontairement perturbé à la fois, de sorte qu'un drain ou une mise à jour progressive ne peut pas retirer les deux copies d'une partition en même temps.
  • Terminaison à drain gracieux. Une fenêtre de grâce de terminaison laisse un nœud sur le départ transmettre proprement plutôt que d'être démoli en plein vol.
  • Mise à l'échelle horizontale à chaud. Avec la découverte Kubernetes activée (le défaut), le registre est synthétisé à partir des ordinaux de pod et du DNS headless, de sorte qu'ajouter des pods est un simple kubectl scale statefulset — de nouveaux pods rejoignent et la carte des partitions grandit sans redémarrer les existants. Ajoutez-les un multiple de RF à la fois et chaque nouvelle partition est durable dès la naissance.

Rien de tout cela n'aide pourtant la partition en copie unique. Un PDB qui maintient tous-sauf-un pods debout n'est aucune protection quand l'un de ces pods est le seul foyer d'une partition. Les garde-fous supposent que vous avez correctement dimensionné le cluster ; ils ne sauvent pas un cluster qui ne l'était pas.

Règles pratiques​

Tout le sujet se comprime en quelques habitudes :

  1. Choisissez d'abord le RF, puis faites du nombre de pods un multiple de celui-ci. RF 2 → nombres de pods pairs ; RF 3 → multiples de trois. Le chart par défaut livre 4 pods au RF 2 précisément parce que c'est la plus petite forme tolérante aux pannes.
  2. Traitez un nombre de pods impair comme réservé à l'évaluation. Trois pods au RF 2 convient à une démo sur kind ; ce n'est pas une posture de durabilité de production, et le chart vous le dit au moment de l'installation.
  3. Faites la mise à l'échelle horizontale par RF. Ajouter un seul pod à un cluster durable crée temporairement une partition en copie unique jusqu'à l'arrivée du pod suivant. Si vous grandissez pour la durabilité, grandissez d'un ensemble de réplication complet à la fois.
  4. Souvenez-vous que le volume est emptyDir. Il n'y a pas de disque de secours. La réplication est votre durabilité, ce qui est exactement pourquoi la copie doit réellement exister sur un autre pod.

Le facteur de réplication vous dit combien de copies une partition veut. Le nombre de pods décide si elle peut les avoir. Alignez ces deux nombres et la perte d'un pod devient un non-événement — le réplica sert, le pod reprogrammé se reconstruit, et pas une clé n'est perdue. Laissez-les dériver hors d'alignement et vous avez bâti un cluster hautement disponible jusqu'au moment précis où cela compte.