Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Noter. trad.: Dans cet article, Banzai Cloud partage un exemple de la façon dont ses outils personnalisés peuvent être utilisés pour rendre Kafka plus facile à utiliser dans Kubernetes. Les instructions suivantes illustrent comment vous pouvez déterminer la taille optimale de votre infrastructure et configurer Kafka lui-même pour atteindre le débit requis.

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Apache Kafka est une plateforme de streaming distribuée permettant de créer des systèmes de streaming en temps réel fiables, évolutifs et performants. Ses capacités impressionnantes peuvent être étendues à l’aide de Kubernetes. Pour cela nous avons développé Opérateur Kafka Open Source et un outil appelé Supertubes. Ils vous permettent d'exécuter Kafka sur Kubernetes et d'utiliser ses différentes fonctionnalités, telles que le réglage fin de la configuration du courtier, la mise à l'échelle basée sur des métriques avec rééquilibrage, la reconnaissance des racks, le « soft » (gracieux) déployer des mises à jour, etc.

Essayez Supertubes dans votre cluster :

curl https://getsupertubes.sh | sh и supertubes install -a --no-democluster --kubeconfig <path-to-eks-cluster-kubeconfig-file>

Ou contactez documentation. Vous pouvez également en savoir plus sur certaines des capacités de Kafka, dont le travail est automatisé à l'aide de Supertubes et de l'opérateur Kafka. Nous en avons déjà parlé sur le blog :

Lorsque vous décidez de déployer un cluster Kafka sur Kubernetes, vous serez probablement confronté au défi de déterminer la taille optimale de l'infrastructure sous-jacente et à la nécessité d'affiner votre configuration Kafka pour répondre aux exigences de débit. Les performances maximales de chaque courtier sont déterminées par les performances des composants de l'infrastructure sous-jacents, tels que la mémoire, le processeur, la vitesse du disque, la bande passante du réseau, etc.

Idéalement, la configuration du courtier devrait être telle que tous les éléments de l'infrastructure soient utilisés au maximum de leurs capacités. Cependant, dans la vraie vie, cette configuration est assez complexe. Il est plus probable que les utilisateurs configurent les courtiers pour maximiser l'utilisation d'un ou deux composants (disque, mémoire ou processeur). De manière générale, un courtier affiche des performances maximales lorsque sa configuration permet d'utiliser au maximum le composant le plus lent. De cette façon, nous pouvons avoir une idée approximative de la charge qu’un courtier peut gérer.

Théoriquement, on peut également estimer le nombre de courtiers nécessaires pour gérer une charge donnée. Cependant, en pratique, il existe tellement d’options de configuration à différents niveaux qu’il est très difficile (voire impossible) d’évaluer les performances potentielles d’une configuration particulière. En d’autres termes, il est très difficile de planifier une configuration basée sur des performances données.

Pour les utilisateurs de Supertubes, nous adoptons généralement l'approche suivante : nous commençons par une certaine configuration (infrastructure + paramètres), puis mesurons ses performances, ajustons les paramètres du courtier et répétons le processus. Cela se produit jusqu'à ce que le composant le plus lent de l'infrastructure soit pleinement utilisé.

De cette façon, nous avons une idée plus claire du nombre de courtiers dont un cluster a besoin pour gérer une certaine charge (le nombre de courtiers dépend également d'autres facteurs, tels que le nombre minimum de répliques de messages pour assurer la résilience, le nombre de partitions dirigeants, etc). De plus, nous obtenons un aperçu des composants d’infrastructure qui nécessitent une mise à l’échelle verticale.

Cet article parlera des étapes que nous suivons pour tirer le meilleur parti des composants les plus lents dans les configurations initiales et mesurer le débit d'un cluster Kafka. Une configuration hautement résiliente nécessite au moins trois courtiers en cours d'exécution (min.insync.replicas=3), répartis sur trois zones d’accessibilité différentes. Pour configurer, faire évoluer et surveiller l'infrastructure Kubernetes, nous utilisons notre propre plateforme de gestion de conteneurs pour les cloud hybrides - Pipeline. Il prend en charge sur site (bare metal, VMware) et cinq types de cloud (Alibaba, AWS, Azure, Google, Oracle), ainsi que toute combinaison d'entre eux.

Réflexions sur l'infrastructure et la configuration du cluster Kafka

Pour les exemples ci-dessous, nous avons choisi AWS comme fournisseur de cloud et EKS comme distribution Kubernetes. Une configuration similaire peut être implémentée en utilisant PKE - Distribution Kubernetes de Banzai Cloud, certifiée CNCF.

disque

Amazon propose divers Types de volumes EBS. Au coeur gp2 и io1 il existe cependant des disques SSD pour garantir un débit élevé gp2 consomme les crédits accumulés (Crédits E/S), nous avons donc préféré le type io1, qui offre un débit élevé et constant.

Types d'instances

Les performances de Kafka dépendent fortement du cache de pages du système d'exploitation. Nous avons donc besoin d'instances disposant de suffisamment de mémoire pour les courtiers (JVM) et le cache de pages. Exemple c5.2xlarge - un bon début, puisqu'il dispose de 16 Go de mémoire et optimisé pour fonctionner avec EBS. Son inconvénient est qu’il n’est capable de fournir des performances maximales que pendant 30 minutes maximum toutes les 24 heures. Si votre charge de travail nécessite des performances optimales sur une période plus longue, vous souhaiterez peut-être envisager d'autres types d'instances. C'est exactement ce que nous avons fait, en nous arrêtant à c5.4xlarge. Il offre un débit maximal dans 593,75 Mo/s. Débit maximum d'un volume EBS io1 supérieur à l'instance c5.4xlarge, donc l'élément le plus lent de l'infrastructure est probablement le débit d'E/S de ce type d'instance (ce que nos tests de charge devraient également confirmer).

Réseau

Le débit du réseau doit être suffisamment important par rapport aux performances de l'instance de VM et du disque, sinon le réseau devient un goulot d'étranglement. Dans notre cas, l'interface réseau c5.4xlarge prend en charge des vitesses allant jusqu'à 10 Gbit/s, ce qui est nettement supérieur au débit d'E/S d'une instance de VM.

Déploiement du courtier

Les courtiers doivent être déployés (programmés dans Kubernetes) sur des nœuds dédiés pour éviter d'entrer en concurrence avec d'autres processus pour les ressources CPU, mémoire, réseau et disque.

VersionJava

Le choix logique est Java 11 car il est compatible avec Docker dans le sens où la JVM détermine correctement les processeurs et la mémoire disponibles pour le conteneur dans lequel le courtier s'exécute. Sachant que les limites du CPU sont importantes, la JVM définit en interne et de manière transparente le nombre de threads GC et de threads JIT. Nous avons utilisé l'image Kafka banzaicloud/kafka:2.13-2.4.0, qui inclut Kafka version 2.4.0 (Scala 2.13) sur Java 11.

Si vous souhaitez en savoir plus sur Java/JVM sur Kubernetes, consultez nos articles suivants :

Paramètres de mémoire du courtier

La configuration de la mémoire du courtier comporte deux aspects clés : les paramètres de la JVM et du pod Kubernetes. La limite de mémoire définie pour un pod doit être supérieure à la taille maximale du tas afin que la JVM dispose de suffisamment d'espace pour le métaespace Java, qui réside dans sa propre mémoire, et pour le cache des pages du système d'exploitation, que Kafka utilise activement. Lors de nos tests, nous avons lancé les courtiers Kafka avec des paramètres -Xmx4G -Xms2G, et la limite de mémoire pour le pod était 10 Gi. Veuillez noter que les paramètres de mémoire de la JVM peuvent être obtenus automatiquement en utilisant -XX:MaxRAMPercentage и -X:MinRAMPercentage, en fonction de la limite de mémoire du pod.

Paramètres du processeur du courtier

De manière générale, vous pouvez améliorer les performances en augmentant le parallélisme en augmentant le nombre de threads utilisés par Kafka. Plus il y a de processeurs disponibles pour Kafka, mieux c'est. Lors de notre test, nous avons commencé avec une limite de 6 processeurs et avons progressivement (au fil des itérations) augmenté leur nombre jusqu'à 15. De plus, nous avons défini num.network.threads=12 dans les paramètres du courtier pour augmenter le nombre de threads qui reçoivent des données du réseau et les envoient. Découvrant immédiatement que les courtiers suiveurs ne pouvaient pas recevoir de répliques assez rapidement, ils levèrent num.replica.fetchers à 4 pour augmenter la vitesse à laquelle les courtiers suiveurs répliquaient les messages des dirigeants.

Outil de génération de charge

Vous devez vous assurer que le générateur de charge sélectionné ne manque pas de capacité avant que le cluster Kafka (qui est évalué) n'atteigne sa charge maximale. En d'autres termes, il est nécessaire de procéder à une évaluation préliminaire des capacités de l'outil de génération de charge, ainsi que de sélectionner des types d'instances avec un nombre suffisant de processeurs et de mémoire. Dans ce cas, notre outil produira plus de charge que ce que le cluster Kafka peut gérer. Après de nombreuses expériences, nous avons opté pour trois exemplaires c5.4xlarge, dont chacun avait un générateur en marche.

Analyse comparative

La mesure des performances est un processus itératif qui comprend les étapes suivantes :

  • mise en place d'infrastructures (cluster EKS, cluster Kafka, outil de génération de charge, ainsi que Prometheus et Grafana) ;
  • générer une charge pendant une certaine période pour filtrer les écarts aléatoires dans les indicateurs de performance collectés ;
  • ajuster l'infrastructure et la configuration du courtier en fonction des indicateurs de performance observés ;
  • répéter le processus jusqu'à ce que le niveau requis de débit du cluster Kafka soit atteint. Dans le même temps, il doit être systématiquement reproductible et présenter des variations minimes de débit.

La section suivante décrit les étapes effectuées au cours du processus d'analyse comparative du cluster de test.

Outils

Les outils suivants ont été utilisés pour déployer rapidement une configuration de base, générer des charges et mesurer les performances :

  • Pipeline Cloud Banzai pour organiser un cluster EKS depuis Amazon c Prométhée (pour collecter des métriques Kafka et d'infrastructure) et grafana (pour visualiser ces métriques). Nous avons profité intégré в Pipeline des services qui fournissent une surveillance fédérée, une collecte centralisée des journaux, une analyse des vulnérabilités, une reprise après sinistre, une sécurité de niveau entreprise et bien plus encore.
  • Sangrenel — un outil pour tester la charge d'un cluster Kafka.
  • Tableaux de bord Grafana pour visualiser les métriques et l'infrastructure Kafka : KubernetesKafka, Exportateur de nœuds.
  • Supertubes CLI pour le moyen le plus simple de configurer un cluster Kafka sur Kubernetes. Zookeeper, l'opérateur Kafka, Envoy et de nombreux autres composants sont installés et correctement configurés pour exécuter un cluster Kafka prêt pour la production sur Kubernetes.
    • Pour l'installation supertubes CLI utilisez les instructions fournies ici.

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Groupe EKS

Préparer un cluster EKS avec des nœuds de travail dédiés c5.4xlarge dans différentes zones de disponibilité pour les pods avec des courtiers Kafka, ainsi que des nœuds dédiés pour le générateur de charge et l'infrastructure de surveillance.

banzai cluster create -f https://raw.githubusercontent.com/banzaicloud/kafka-operator/master/docs/benchmarks/infrastructure/cluster_eks_202001.json

Une fois le cluster EKS opérationnel, activez son service de surveillance — elle déploiera Prometheus et Grafana dans un cluster.

Composants du système Kafka

Installez les composants du système Kafka (Zookeeper, kafka-operator) dans EKS à l'aide de la CLI supertubes :

supertubes install -a --no-democluster --kubeconfig <path-to-eks-cluster-kubeconfig-file>

Cluster Kafka

Par défaut, EKS utilise des volumes EBS de type gp2, vous devez donc créer une classe de stockage distincte basée sur les volumes io1 pour le cluster Kafka :

kubectl create -f - <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: "50"
  fsType: ext4
volumeBindingMode: WaitForFirstConsumer
EOF

Définir le paramètre pour les courtiers min.insync.replicas=3 et déployez des pods de courtier sur des nœuds dans trois zones de disponibilité différentes :

supertubes cluster create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f https://raw.githubusercontent.com/banzaicloud/kafka-operator/master/docs/benchmarks/infrastructure/kafka_202001_3brokers.yaml --wait --timeout 600

Les sujets

Nous avons exécuté trois instances de générateur de charge en parallèle. Chacun d'eux écrit sur son propre sujet, c'est-à-dire que nous avons besoin de trois sujets au total :

supertubes cluster topic create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f -<<EOF
apiVersion: kafka.banzaicloud.io/v1alpha1
kind: KafkaTopic
metadata:
  name: perftest1
spec:
  name: perftest1
  partitions: 12
  replicationFactor: 3
  retention.ms: '28800000'
  cleanup.policy: delete
EOF

supertubes cluster topic create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f -<<EOF
apiVersion: kafka.banzaicloud.io/v1alpha1
kind: KafkaTopic
metadata:
    name: perftest2
spec:
  name: perftest2
  partitions: 12
  replicationFactor: 3
  retention.ms: '28800000'
  cleanup.policy: delete
EOF

supertubes cluster topic create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f -<<EOF
apiVersion: kafka.banzaicloud.io/v1alpha1
kind: KafkaTopic
metadata:
  name: perftest3
spec:
  name: perftest3
  partitions: 12
  replicationFactor: 3
  retention.ms: '28800000'
  cleanup.policy: delete
EOF

Pour chaque sujet, le facteur de réplication est de 3, soit la valeur minimale recommandée pour les systèmes de production hautement disponibles.

Outil de génération de charge

Nous avons lancé trois copies du générateur de charge (chacune a été écrite dans un sujet distinct). Pour les pods Load Generator, vous devez définir l'affinité des nœuds afin qu'ils soient planifiés uniquement sur les nœuds qui leur sont alloués :

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: loadtest
  name: perf-load1
  namespace: kafka
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: loadtest
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: loadtest
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: nodepool.banzaicloud.io/name
                operator: In
                values:
                - loadgen
      containers:
      - args:
        - -brokers=kafka-0:29092,kafka-1:29092,kafka-2:29092,kafka-3:29092
        - -topic=perftest1
        - -required-acks=all
        - -message-size=512
        - -workers=20
        image: banzaicloud/perfload:0.1.0-blog
        imagePullPolicy: Always
        name: sangrenel
        resources:
          limits:
            cpu: 2
            memory: 1Gi
          requests:
            cpu: 2
            memory: 1Gi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30

Quelques points à noter :

  • Le générateur de charge génère des messages d'une longueur de 512 octets et les publie sur Kafka par lots de 500 messages.
  • Utiliser un argument -required-acks=all La publication est considérée comme réussie lorsque toutes les répliques synchronisées du message sont reçues et confirmées par les courtiers Kafka. Cela signifie que dans le benchmark, nous avons mesuré non seulement la vitesse à laquelle les dirigeants reçoivent des messages, mais également la vitesse à laquelle leurs partisans reproduisent les messages. Le but de ce test n'est pas d'évaluer la vitesse de lecture du consommateur (consommateurs) messages récemment reçus qui restent encore dans le cache des pages du système d'exploitation, et sa comparaison avec la vitesse de lecture des messages stockés sur le disque.
  • Le générateur de charge fait fonctionner 20 travailleurs en parallèle (-workers=20). Chaque travailleur contient 5 producteurs qui partagent la connexion du travailleur au cluster Kafka. En conséquence, chaque générateur compte 100 producteurs, et ils envoient tous des messages au cluster Kafka.

Surveillance de la santé du cluster

Lors des tests de charge du cluster Kafka, nous avons également surveillé son état de santé pour nous assurer qu'il n'y avait pas de redémarrage de pod, pas de réplicas désynchronisés et un débit maximal avec des fluctuations minimales :

  • Le générateur de charge écrit des statistiques standard sur le nombre de messages publiés et le taux d'erreur. Le taux d'erreur devrait rester le même 0,00%.
  • Cruise Control, déployé par kafka-operator, fournit un tableau de bord où l'on peut également surveiller l'état du cluster. Pour afficher ce panneau, procédez :
    supertubes cluster cruisecontrol show -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file>
  • Niveau ISR (nombre de répliques « synchronisées ») le retrait et l'expansion sont égaux à 0.

Résultats de mesure

3 courtiers, taille du message - 512 octets

Avec des partitions réparties uniformément sur trois courtiers, nous avons pu atteindre des performances ~500 Mb/s (environ 990 XNUMX messages par seconde):

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

La consommation mémoire de la machine virtuelle JVM n'a pas dépassé 2 Go :

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Le débit du disque a atteint le débit maximal du nœud d'E/S sur les trois instances sur lesquelles les courtiers étaient exécutés :

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

D'après les données sur l'utilisation de la mémoire par les nœuds, il s'ensuit que la mise en mémoire tampon et la mise en cache du système ont nécessité environ 10 à 15 Go :

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

3 courtiers, taille du message - 100 octets

À mesure que la taille du message diminue, le débit chute d'environ 15 à 20 % : le temps passé à traiter chaque message l'affecte. De plus, la charge du processeur a presque doublé.

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Étant donné que les nœuds courtiers ont encore des cœurs inutilisés, les performances peuvent être améliorées en modifiant la configuration de Kafka. Ce n'est pas une tâche facile, donc pour augmenter le débit, il est préférable de travailler avec des messages plus volumineux.

4 courtiers, taille du message - 512 octets

Vous pouvez facilement augmenter les performances d'un cluster Kafka en ajoutant simplement de nouveaux courtiers et en maintenant un équilibre des partitions (cela garantit que la charge est uniformément répartie entre les courtiers). Dans notre cas, après avoir ajouté un courtier, le débit du cluster a augmenté jusqu'à ~580 Mb/s (~1,1 million de messages par seconde). La croissance s'est avérée moindre que prévu : cela s'explique principalement par le déséquilibre des cloisons (tous les courtiers ne travaillent pas au maximum de leurs capacités).

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

La consommation mémoire de la machine JVM est restée inférieure à 2 Go :

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Le travail des courtiers avec les lecteurs était affecté par le déséquilibre des partitions :

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

Déterminer la taille appropriée pour un cluster Kafka dans Kubernetes

résultats

L'approche itérative présentée ci-dessus peut être étendue pour couvrir des scénarios plus complexes impliquant des centaines de consommateurs, un partitionnement, des mises à jour progressives, des redémarrages de pods, etc. Tout cela nous permet d’évaluer les limites des capacités du cluster Kafka dans diverses conditions, d’identifier les goulots d’étranglement dans son fonctionnement et de trouver des moyens de les combattre.

Nous avons conçu Supertubes pour déployer rapidement et facilement un cluster, le configurer, ajouter/supprimer des courtiers et des sujets, répondre aux alertes et garantir que Kafka en général fonctionne correctement sur Kubernetes. Notre objectif est de vous aider à vous concentrer sur la tâche principale (« générer » et « consommer » les messages Kafka) et de laisser tout le travail acharné à Supertubes et à l'opérateur Kafka.

Si vous êtes intéressé par les technologies Banzai Cloud et les projets Open Source, abonnez-vous à l'entreprise sur GitHub, LinkedIn ou Twitter.

PS du traducteur

A lire aussi sur notre blog :

Source: habr.com

Ajouter un commentaire