Hinweis.: In diesem Artikel teilt das Unternehmen Banzai Cloud ein Beispiel für die Verwendung seiner speziellen Tools zur Vereinfachung des Betriebs von Kafka innerhalb von Kubernetes. Die enthaltenen Anleitungen zeigen, wie man die optimale Größe der Infrastruktur ermitteln und Kafka selbst konfigurieren kann, um die erforderliche Durchsatzrate zu erreichen.

Apache Kafka ist eine verteilte Streaming-Plattform zur Erstellung zuverlässiger, skalierbarer und leistungsstarker Echtzeitsysteme. Ihre beeindruckenden Fähigkeiten können durch Kubernetes erweitert werden. Dafür haben wir und ein Tool namens . Diese ermöglichen die Ausführung von Kafka in Kubernetes und die Nutzung ihrer verschiedenen Funktionen, wie die feine Konfiguration der Broker-Einstellungen, die skalierbare Anpassung basierend auf Metriken mit Rebalancing, Rack Awareness, sanfte (graceful) Rollouts von Updates usw.
Probieren Sie Supertubes in Ihrem Cluster aus:
curl https://getsupertubes.sh | sh und supertubes install -a --no-democluster --kubeconfigOder wenden Sie sich an . Außerdem können Sie mehr über einige Möglichkeiten von Kafka lesen, die durch Supertubes und den Kafka-Operator automatisiert sind. Darüber haben wir bereits in unserem Blog geschrieben:
- ;
- ;
- ;
- ;
- ;
- ;
- .
Wenn Sie einen Kafka-Cluster in Kubernetes bereitstellen möchten, werden Sie sicherlich auf das Problem stoßen, die optimale Größe der zugrunde liegenden Infrastruktur zu bestimmen und die Konfiguration von Kafka fein abzustimmen, um den Leistungsanforderungen gerecht zu werden. Die maximale Leistung jedes Brokers wird durch die Leistung der zugrunde liegenden Infrastrukturkomponenten wie Speicher, Prozessor, Festplattengeschwindigkeit, Netzwerkbandbreite usw. bestimmt.
Idealerweise sollte die Konfiguration des Brokers so gestaltet sein, dass alle Infrastrukturelemente ihre maximale Leistungsfähigkeit entfalten. In der Praxis gestaltet sich eine solche Einstellung jedoch als recht komplex. Wahrscheinlicher ist, dass Benutzer die Broker-Konfiguration so anpassen, dass sie die Leistung eines oder zweier Komponenten (Festplatte, Speicher oder Prozessor) maximiert. Generell zeigt der Broker die beste Leistung, wenn seine Konfiguration es ermöglicht, das langsamste Element vollständig auszureizen. So erhalten wir eine ungefähre Vorstellung von der Last, die ein Broker bewältigen kann.
Theoretisch können wir auch die Anzahl der Broker abschätzen, die erforderlich ist, um eine bestimmte Last zu bewältigen. In der Praxis gibt es jedoch so viele Anpassungsmöglichkeiten auf verschiedenen Ebenen, dass es äußerst schwierig (wenn nicht gar unmöglich) ist, die potenzielle Leistung einer bestimmten Konfiguration abzuschätzen. Mit anderen Worten, es ist sehr schwierig, eine Konfiguration zu planen, die von einer bestimmten Leistungsanforderung ausgeht.
Für Supertubes-Nutzer verfolgen wir in der Regel den folgenden Ansatz: Wir beginnen mit einer bestimmten Konfiguration (Infrastruktur + Einstellungen), messen dann die Leistung, passen die Broker-Einstellungen an und wiederholen den Prozess erneut. Dies geschieht, bis das Potenzial des langsamsten Komponenten der Infrastruktur vollständig ausschöpft ist.
Auf diese Weise erhalten wir ein klareres Bild davon, wie viele Broker der Cluster benötigt, um mit einer bestimmten Last umzugehen (die Anzahl der Broker hängt auch von anderen Faktoren ab, wie der minimalen Anzahl von Nachrichtenreplikaten zur Gewährleistung der Stabilität, der Anzahl der Partition-Leiter usw.). Darüber hinaus erhalten wir Hinweise darauf, welches infrastrukturelle Komponent vorzugsweise vertikal skaliert werden sollte.
In diesem Artikel geht es um die Schritte, die wir unternehmen, um das Maximum aus den langsamsten Komponenten in den Anfangskonfigurationen herauszuholen und die Durchsatzkapazität des Kafka-Clusters zu messen. Eine hochverfügbare Konfiguration erfordert mindestens drei funktionierende Broker.min.insync.replicas=3), verteilt auf drei verschiedene Verfügbarkeitszonen. Für die Einrichtung, Skalierung und Überwachung der Kubernetes-Infrastruktur verwenden wir unsere eigene Container-Management-Plattform für hybride Clouds — . Sie unterstützt On-Premise (Bare Metal, VMware) und fünf Cloud-Typen (Alibaba, AWS, Azure, Google, Oracle) sowie deren beliebige Kombinationen.
Überlegungen zur Infrastruktur und Konfiguration des Kafka-Clusters
Für die nachfolgenden Beispiele haben wir AWS als Cloud-Dienstanbieter und EKS als Kubernetes-Distribution gewählt. Eine ähnliche Konfiguration kann auch mit — der Kubernetes-Distribution von Banzai Cloud, die von der CNCF zertifiziert ist.
Festplatte
Amazon bietet verschiedene . Die Grundlage der gp2 und io1 sind SSD-Laufwerke, jedoch benötigt für hohe Durchsatzraten gp2 verbraucht angesammelte Credits (I/O-Credits), weshalb wir den Typ bevorzugt haben, der eine stabile hohe Durchsatzrate bietet. io1Instanztypen
Die Leistung von Kafka hängt stark vom Seiten-Cache des Betriebssystems ab, daher benötigen wir Instanzen mit ausreichendem Arbeitsspeicher für die Broker (JVM) und den Seiten-Cache. Die Instanz
c5.2xlarge c5.2xlarge — ein guter Anfang, da er über 16 GB Arbeitsspeicher verfügt und . Ein Nachteil ist, dass er maximale Leistung für nicht länger als 30 Minuten alle 24 Stunden bereitstellen kann. Wenn die Arbeitslast eine maximale Leistung über einen längeren Zeitraum erfordert, sollte man sich andere Instanztypen ansehen. Genau das haben wir getan und uns für c5.4xlargeentschieden. Er bietet eine maximale Durchsatzrate von 593,75 MB/s. Der maximale Durchsatz der EBS-Volumes io1 ist höher als der der Instanz c5.4xlarge, weshalb das langsamste Element der Infrastruktur anscheinend die I/O-Durchsatzrate dieses Instanztyps ist (was auch durch die Ergebnisse unserer Lasttests bestätigt werden sollte).
Netzwerk
Die Netzwerkbandbreite sollte im Vergleich zur Leistung der VM-Instanz und der Festplatte ausreichend hoch sein, da sonst das Netzwerk zum Engpass wird. In unserem Fall unterstützt das Netzwerkinterface c5.4xlarge Geschwindigkeiten von bis zu 10 Gbit/s, was deutlich über der I/O-Durchsatzrate der VM-Instanz liegt.
Bereitstellung von Broker
Broker müssen auf dedizierte Knoten (in Kubernetes geplant) bereitgestellt werden, um Konkurrenz mit anderen Prozessen um CPU-, Speicher-, Netzwerk- und Festplattressourcen zu vermeiden.
Java-Version
Die logische Wahl ist Java 11, da sie mit Docker kompatibel ist und die JVM die Prozessoren und den Speicher, die dem Container zur Verfügung stehen, korrekt erkennt, in dem der Broker ausgeführt wird. Da bekannt ist, dass die CPU-Grenzen wichtig sind, stellt die JVM intern und transparent die Anzahl der GC-Threads und der JIT-Compiler-Threads ein. Wir haben das Kafka-Image verwendet banzaicloud/kafka:2.13-2.4.0, das die Kafka-Version 2.4.0 (Scala 2.13) auf Java 11 enthält.
Wenn Sie mehr über Java/JVM auf Kubernetes erfahren möchten, beachten Sie bitte unsere folgenden Publikationen:
- ;
- .
Broker-Speichereinstellungen
Es gibt zwei Schlüsselaspekte bei der Konfiguration des Broker-Speichers: Einstellungen für die JVM und für das Kubernetes-Pod. Das für das Pod festgelegte Speicherkontingent sollte größer sein als die maximale Heap-Größe, damit in der JVM Platz für den Java-Metaspace bleibt, der im eigenen Speicher liegt, und für den Seitencache des Betriebssystems, den Kafka aktiv nutzt. In unseren Tests haben wir Kafka-Broker mit den Parametern -Xmx4G -Xms2Gund das Speicherkontingent für das Pod betrug 10 Gi. Beachten Sie, dass die JVM-Speichereinstellungen automatisch mit Hilfe von -XX:MaxRAMPercentage und -X:MinRAMPercentageabgerufen werden können, basierend auf dem Speicherkontingent des Pods.
Prozessor-Einstellungen des Brokers
Allgemein kann die Leistung durch Erhöhung des Parallelismus verbessert werden, indem die Anzahl der von Kafka verwendeten Threads erhöht wird. Je mehr Prozessoren Kafka zur Verfügung stehen, desto besser. In unserem Test begannen wir mit einem Limit von 6 Prozessoren und erhöhten die Anzahl schrittweise (in Iterationen) auf 15. Darüber hinaus haben wir num.network.threads=12 in den Broker-Einstellungen, um die Anzahl der Streams zu erhöhen, die Daten aus dem Netzwerk empfangen und diese weitergeben. Nachdem festgestellt wurde, dass die follower-Broker die Replikate nicht schnell genug erhalten können, haben wir num.replica.fetchers auf 4 erhöht, um die Geschwindigkeit zu steigern, mit der die follower-Broker Nachrichten von den Leadern replizieren.
Lastgenerierungstool
Es sollte sichergestellt werden, dass das Potenzial des gewählten Lastgenerators nicht erschöpft wird, bevor der Kafka-Cluster (dessen Benchmark durchgeführt wird) seine maximale Belastung erreicht. Mit anderen Worten, eine Vorabbewertung der Fähigkeiten des Lastgenerators ist erforderlich, und es sollten Instanztypen mit ausreichend Prozessoren und Speicher ausgewählt werden. In diesem Fall wird unser Tool mehr Last erzeugen, als der Kafka-Cluster verarbeiten kann. Nach zahlreichen Experimenten haben wir uns auf drei Instanzen geeinigt, c5.4xlargeauf denen jeweils der Generator läuft.
Benchmarking
Die Leistungsbewertung ist ein iterativer Prozess, der die folgenden Phasen umfasst:
- Infrastruktur (EKS-Cluster, Kafka-Cluster, Lastgenerierungstool sowie Prometheus und Grafana) einrichten;
- Lastgenerierung über einen bestimmten Zeitraum, um zufällige Abweichungen in den gesammelten Leistungskennzahlen herauszufiltern;
- Anpassung der Infrastruktur und Broker-Konfiguration basierend auf den beobachteten Leistungskennzahlen;
- Wiederholung des Prozesses, bis das erforderliche Durchsatzniveau des Kafka-Clusters erreicht ist. Dieser sollte dabei stabil reproduzierbar sein und minimale Durchsatzvariationen aufweisen.
Im folgenden Abschnitt werden die Schritte beschrieben, die während des Benchmarkings des Testclusters durchgeführt wurden.
Werkzeuge
Für die schnelle Bereitstellung der Basis Konfiguration, Lastgenerierung und Leistungsmessung wurden folgende Tools verwendet:
- zur Organisation des EKS-Clusters von Amazon mit (zur Erfassung von Kafka- und Infrastrukturmetriken) sowie (zur Visualisierung dieser Metriken). Wir haben integrierte in Dienste, die föderierte Überwachung, zentrale Protokollsammlung, Schwachstellenscanning, Wiederherstellung nach Ausfällen, Unternehmenssicherheit und vieles mehr bieten.
- — ein Tool zum Lasttest von Kafka-Clustern.
- Grafana-Dashboards zur Visualisierung von Kafka-Metriken und Infrastruktur: , .
- Supertubes CLI für maximal einfache Einrichtung eines Kafka-Clusters in Kubernetes. Zookeeper, Kafka-Operator, Envoy und viele andere Komponenten sind installiert und ordnungsgemäß konfiguriert, um ein produktionsbereites Kafka-Cluster in Kubernetes zu betreiben.
- Zur Installation supertubes CLI befolgen Sie die angegebenen Anweisungen .

EKS-Cluster
Bereiten Sie ein EKS-Cluster mit dedizierten Arbeitsknoten c5.4xlarge in verschiedenen Verfügbarkeitszonen für Pods mit Kafka-Brokern sowie dedizierte Knoten für Lastgenerator und Überwachungsinfrastruktur vor.
banzai cluster create -f https://raw.githubusercontent.com/banzaicloud/kafka-operator/master/docs/benchmarks/infrastructure/cluster_eks_202001.jsonSobald das EKS-Cluster läuft, aktivieren Sie dessen integrierten — dieser wird Prometheus und Grafana im Cluster bereitstellen.
Systemkomponenten von Kafka
Installieren Sie die Systemkomponenten von Kafka (Zookeeper, Kafka-Operator) in EKS mit supertubes CLI:
supertubes install -a --no-democluster --kubeconfigKafka-Cluster
Standardmäßig werden in EKS EBS-Volumes vom Typ verwendet gp2, daher ist es notwendig, eine separate Speicherklasse auf Basis von Volumes zu erstellen io1 für das Kafka-Cluster:
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 Setzen Sie den Parameter für Broker min.insync.replicas=3 und deployen Sie die Broker-Pods auf Knoten in drei verschiedenen Verfügbarkeitszonen:
supertubes cluster create -n kafka --kubeconfig -f https://raw.githubusercontent.com/banzaicloud/kafka-operator/master/docs/benchmarks/infrastructure/kafka_202001_3brokers.yaml --wait --timeout 600Themen
Wir haben parallel drei Instanzen des Lastgenerators gestartet. Jede von ihnen schreibt in ihr eigenes Thema, das bedeutet, insgesamt benötigen wir drei Themen:
supertubes cluster topic create -n kafka --kubeconfig -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 -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 -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
EOFFür jedes Topic beträgt der Replikationsfaktor 3 – der minimal empfohlene Wert für hochverfügbare Produktionseinrichtungen.
Lastgenerierungstool
Wir haben drei Instanzen des Lastgenerators gestartet (jede schrieb in ein separates Topic). Für die Pods des Lastgenerators muss eine Node-Affinität definiert werden, damit sie nur auf den dafür vorgesehenen Knoten geplant werden.
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: 30Einige Punkte, auf die Sie achten sollten:
- Der Lastgenerator erzeugt Nachrichten mit einer Länge von 512 Byte und veröffentlicht diese in Kafka in Stapeln von 500 Nachrichten.
- Mit dem Argument
-required-acks=allEine Veröffentlichung wird als erfolgreich angesehen, wenn alle synchronisierten Replikate der Nachricht von den Kafka-Brokern empfangen und bestätigt wurden. Das bedeutet, dass wir im Benchmark nicht nur die Geschwindigkeit der führenden Broker bei der Empfang von Nachrichten gemessen haben, sondern auch die ihrer Nachfolger, die die Nachrichten replizieren. Dieser Test bewertet nicht die Lesegeschwindigkeit durch die Verbraucher. (Verbraucher) von neu angenommenen Nachrichten, die sich derzeit noch im Seiten-Cache des Betriebssystems befinden, und vergleicht diese mit der Lesegeschwindigkeit von Nachrichten, die auf der Festplatte gespeichert sind. - Der Lastgenerator startet parallel 20 Worker (
-workers=20). Jeder Worker enthält 5 Producer, die gemeinsam die Verbindung des Workers zum Kafka-Cluster nutzen. Insgesamt hat jeder Generator 100 Producer, die alle Nachrichten an das Kafka-Cluster senden.
Überwachung des Clusterzustands
Während des Lasttests des Kafka-Clusters haben wir auch seine Gesundheit überwacht, um sicherzustellen, dass es keine Neustarts von Pods, desynchronisierte Replikate und eine maximale Durchsatzrate mit minimalen Schwankungen gibt:
- Der Lastgenerator gibt eine Standardstatistik über die Anzahl der veröffentlichten Nachrichten und den Fehlerstatus aus. Der Fehleranteil sollte bei
0,00%. - , bereitgestellt durch den Kafka-Operator, bietet ein Überwachungsdashboard, auf dem wir auch den Zustand des Clusters beobachten können. Um dieses Dashboard anzuzeigen, führen Sie aus:
supertubes cluster cruisecontrol show -n kafka --kubeconfig - ISR-Level (Anzahl der "in-sync" Replikate) Shrink und Expansion sind gleich 0.
Messresultate
3 Broker, Nachrichtenformat — 512 Byte
Mit Partitionen, die gleichmäßig auf drei Broker verteilt sind, konnten wir eine Leistung von ~500 MB/s (ca. 990.000 Nachrichten pro Sekunde) erreichen.:



Der Speicherverbrauch der JVM-Virtual-Machine überschritt nicht 2 GB:



Die Festplattendurchsatzrate erreichte die maximale I/O-Durchsatzrate des Knotens auf allen drei Instanzen, auf denen die Broker liefen:



Aus den Speicherbenutzungsdaten der Knoten geht hervor, dass die Systempufferung und das Caching etwa 10-15 GB einnahmen:



3 Broker, Nachrichtenformat — 100 Byte
Mit der Verringerung der Nachrichtengröße sinkt die Durchsatzleistung um etwa 15-20 %: Dies wird durch die Zeit beeinflusst, die für die Verarbeitung jeder Nachricht benötigt wird. Darüber hinaus ist die CPU-Auslastung fast doppelt so hoch.



Da an den Broker-Knoten weiterhin ungenutzte Kerne vorhanden sind, kann die Leistung durch eine Anpassung der Kafka-Konfiguration verbessert werden. Dies ist eine komplexe Aufgabe, daher ist es besser, mit größeren Nachrichten zu arbeiten, um die Durchsatzleistung zu steigern.
4 Broker, Nachrichtenformat – 512 Byte
Die Leistung des Kafka-Clusters kann leicht erhöht werden, indem einfach neue Broker hinzugefügt und das Partitioning ausgewogen bleibt (dies gewährleistet eine gleichmäßige Lastverteilung zwischen den Brokern). In unserem Fall stieg der Durchsatz des Clusters nach dem Hinzufügen eines Brokers auf ~580 MB/s (~1,1 Millionen Nachrichten pro Sekunde). Das Wachstum war geringer als erwartet: Dies ist hauptsächlich auf ein Ungleichgewicht der Partitionen zurückzuführen (nicht alle Broker arbeiten an ihren Grenzen).




Der RAM-Verbrauch der JVM blieb unter 2 GB:




Die Arbeit der Broker mit Speicherlaufwerken wurde durch ein Ungleichgewicht der Partitionen beeinträchtigt:




Fazit
Der oben dargestellte iterative Ansatz kann erweitert werden, um komplexere Szenarien abzudecken, die Hunderte von Consumer, Repartitionierungen, rollierende Updates, Neuprogrammierungen von Pods usw. umfassen. All dies ermöglicht es uns, die Grenzen der Möglichkeiten des Kafka-Clusters unter verschiedenen Bedingungen zu bewerten, Engpässe in seiner Leistung zu identifizieren und Wege zur Bekämpfung dieser zu finden.
Wir haben Supertubes entwickelt, um die Bereitstellung des Clusters, seine Konfiguration, die Hinzufügung/Entfernung von Brokern und Themen, die Reaktion auf Benachrichtigungen und die Gewährleistung des ordnungsgemäßen Betriebs von Kafka in Kubernetes insgesamt zu erleichtern. Unser Ziel ist es, Ihnen zu helfen, sich auf die Hauptaufgabe („Nachrichten in Kafka zu erzeugen und zu konsumieren“) zu konzentrieren, während die gesamte harte Arbeit Supertubes und dem Kafka-Operator überlassen bleibt.
Wenn Sie sich für Technologien und Open Source-Projekte von Banzai Cloud interessieren, abonnieren Sie das Unternehmen auf , oder .
P.S. vom Übersetzer
Lesen Sie auch in unserem Blog:
- «»;
- «»;
- «».
Quelle: habr.com
