Apache Kafka Testumgebung aufsetzen

Es gibt unterschiedliche Arten eine Testumgebung für Apache Kafka aufzusetzen. In diesem Artikel geht es darum Apache Kafka auf einem einzelnen Rechner laufen zu lassen. So können wir im kleineren Umfang Tests durchführen und sogar ausprobieren was passiert, wenn einzelne Broker nicht erreichbar sind.

17.11.2021
  • Apache Kafka Testumgebung aufsetzen

    Anatoly Zelenin

    IT-Trainer und Apache Kafka Experte

  • Apache Kafka Testumgebung aufsetzen

    Alexander Kropp

    Dozent und Trainer im Cloud-Umfeld

Es gibt unterschiedliche Arten eine Testumgebung für Apache Kafka aufzusetzen. In diesem Artikel geht es darum, Apache Kafka auf einem einzelnen Rechner laufen zu lassen. So können wir im kleineren Umfang Tests durchführen und sogar ausprobieren, was passiert, wenn einzelne Broker nicht erreichbar sind.

In diesem Artikel möchten wir drei Varianten durchgehen:

  1. Die einfache: Wenn uns ein Kafka Broker reicht, dann können wir einfach die Confluent CLI benutzen, um einen kleinen Apache Kafka Cluster aufzusetzen.
  2. KRaft: KRaft ist auch mit Kafka 3.0 noch ein experimentelles Feature. Für Testumgebungen kann es sich dennoch lohnen, es einmal auszuprobieren. So können wir auf Zookeeper verzichten und somit unser Setup stark vereinfachen. Hier setzen wir selbstständig drei Broker auf demselben Rechner auf und lassen sie auf unterschiedlichen Ports laufen.
  3. Zookeeper: Sonst ist das Aufsetzen einer Testumgebung deutlich umständlicher, da wir uns neben Kafka selbst auch um Zookeeper kümmern mussten. Dies ist aber zur Zeit der normale Weg, um Kafka aufzusetzen.

Schnell und Einfach: Confluent CLI

Mit diesem Ansatz können wir schnell und einfach Apache Kafka testen.

Vorteile:

  • Schnelle und einfache Installation
  • Inklusive Confluent Plattform (ksqlDB, Confluent Control Center, etc…)
  • Immer die aktuellste Kafka Version

Nachteile:

  • Nur ein Broker
  • Daten überleben keinen Neustart

Mit der Confluent CLI können wir uns sehr einfach die Confluent Community Features ausprobieren. Hier starten wir einen Kafka Cluster mit einem Kafka Broker und einem Zookeeper Knoten. Wichtig ist auch hier zu beachten, dass sich dieser Ansatz nicht für Produktion eignet.

Mit der Confluent CLI setzen wir einen solchen Kafka Cluster auf

Zuerst installieren wir die Confluent CLI:

$ curl -sL --http1.1 https://cnfl.io/cli | sh -s

Nun müssen wir lediglich Apache Kafka starten:

$ confluent local services kafka start

Dies dauert beim ersten Mal einige Minuten, da Kafka erst heruntergeladen werden muss. Danach ist Kafka einsatzbereit und wir können mit den Ersten Schritten mit Apache Kafka beginnen.

Stoppen und Deinstallieren

Um unsere Umgebung zu stoppen, nutzen wir einfach folgenden Befehl:

$ confluent local services stop

Um die Umgebung zu löschen, müssen wir sowohl die Confluent CLI löschen als auch die Ordner, die es für Kafka erstellt hat:

$ confluent local destroy
$ rm $(which confluent)
$ rm -r ~/.confluent

Fertig!

Kafka manuell Installieren

Betriebssysteme

Apache Kafka funktioniert in der Java Virtual Machine und ist somit auf allen populären Betriebssystemen lauffähig. Wir haben unser Setup unter folgenden Betriebssystemen getestet: macOS Big Sur 11.4 mit Intel Prozessor, Ubuntu 20.04 LTS. Windows-Nutzern empfehlen wir das Windows-Subsystem für Linux.

Kafka herunterladen

Die aktuelle Kafka-Version findet sich unter https://kafka.apache.org/downloads. Zum Zeitpunkt des Schreibens ist die Version 3.0 aktuell. Wir empfehlen den binären Download in der aktuellen Scala-Version, welche zum Zeitpunkt des Schreibens kafka_2.13-3.0.0.tgz ist. Wir laden die Datei herunter und entpacken sie an einem von uns gewählten Ort (z. B. ~/kafka):

$ wget "https://downloads.apache.org/kafka/3.0.0/kafka_2.13-3.0.0.tgz"
$ tar xfz kafka_2.13-3.0.0.tgz
$ rm kafka_2.13-3.0.0.tgz
$ cp -a kafka_2.13-3.0.0/. ~/kafka
$ rm -r kafka_2.13-3.0.0

Im Ordner ~/kafka finden wir folgende Ordner und Dateien:

  • LICENSE, NOTICE: Dateien mit Lizenzinformationen
  • bin: Kommandozeilenwerkzeuge zur Interaktion mit Kafka und Zookeeper
  • config: Konfigurationsdateien
  • libs: Java-Bibliotheksdateien (*.jar)
  • site-docs: Dokumentation

Damit wir beim Ausführen der Kommandozeilenwerkzeuge nicht immer den gesamten Pfad angeben müssen, fügen wir den bin-Ordner zur PATH-Variablen hinzu:

$ export PATH=~/kafka/bin:"$PATH"

Natürlich können wir diese Zeile auch in unsere ~/.bashrc, ~/.zshrc o. Ä. hinzufügen, je nachdem, welche Shell wir benutzen.

KRaft-basiert

Wir starten einen Kafka-Cluster bestehend aus drei Brokern. Normalerweise würden wir dies auf getrennten Servern aufsetzen, aber einfachheitshalber starten wir alles auf einem Computer und nutzen verschiedene Ports:

Kafka starten

Da wir mehrere Kafka-Broker auf einem Rechner starten können , legen wir für jeden Broker einen eigenen Ordner an:

$ mkdir -p ~/kafka/data/kafka1
$ mkdir -p ~/kafka/data/kafka2
$ mkdir -p ~/kafka/data/kafka3

Nun erstellen wir für jeden Broker eine Konfigurationsdatei im Ordner ~/kafka/config an: kafka1.properties für Broker 1, kafka2.properties und kafka3.properties für die beiden anderen Broker. Wir zeigen hier nur die Konfigurationsdatei für den Broker 1. Für die beiden anderen Broker muss sie entsprechend angepasst werden:

# Bei kafka1.properties
node.id=1
log.dirs=[PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/kafka1
advertised.listeners=PLAINTEXT://localhost:9092
listeners=PLAINTEXT://:9092,CONTROLLER://:9082
 
# Bei kafka2.properties
#node.id=2
#log.dirs=[PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/kafka2
#advertised.listeners=PLAINTEXT://localhost:9093
#listeners=PLAINTEXT://:9093,CONTROLLER://:9083
 
# Bei kafka3.properties
#node.id=3
#log.dirs=[PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/kafka3
#advertised.listeners=PLAINTEXT://localhost:9094
#listeners=PLAINTEXT://:9094,CONTROLLER://:9084
 
process.roles=broker,controller
controller.quorum.voters=1@localhost:9082,2@localhost:9083,3@localhost:9084
inter.broker.listener.name=PLAINTEXT
controller.listener.names=CONTROLLER
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT...

Wir müssen nun eine ID für unseren Cluster generieren:

$ export CLUSTER_ID=$(kafka-storage.sh random-uuid)
$ echo $CLUSTER_ID
9WP_eaVgSBu8rtqAHruDtw

Die Cluster-ID haben wir nun in der Variablen $CLUSTER_ID gespeichert. Diese ändert sich bei jedem Aufruf des Befehls. Wir müssen nun unsere erstellten Verzeichnisse für diesen Cluster formatieren:

$ kafka-storage.sh format -t $CLUSTER_ID \
   -c ./config/kafka1.properties
Formatting [PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/kafka1
$ kafka-storage.sh format -t $CLUSTER_ID \
   -c ./config/kafka2.properties
Formatting [PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/kafka2
$ kafka-storage.sh format -t $CLUSTER_ID \
   -c ./config/kafka3.properties
Formatting [PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/kafka3

Falls die Befehle nicht erfolgreich sind, haben wir einen (Tipp-)Fehler in der Konfiguration. Die Fehlermeldungen sind recht aussagekräftig. Wir können nun in drei unterschiedlichen Terminals unsere drei Broker starten. Wir dürfen aber vorher nicht vergessen, unsere PATH-Variable zu setzen (zum Beispiel mit dem Befehl export PATH=~/kafka/bin:"$PATH").

# Terminal 1
$ kafka-server-start.sh ~/kafka/config/kafka1.properties
# Terminal 2
$ kafka-server-start.sh ~/kafka/config/kafka2.properties
# Terminal 3
$ kafka-server-start.sh ~/kafka/config/kafka3.properties

Die Broker produzieren jeweils sehr viele Zeilen an Ausgaben. Nach einigen Sekunden sollten sie bei folgender Ausgabe stehenbleiben:

[…] INFO Kafka Server started (kafka.server.KafkaRaftServer)
[…] INFO [BrokerLifecycleManager id=2] The broker has been unfenced. Transitioning from RECOVERY to RUNNING. (kafka.server.BrokerLifecycleManager)
[…] INFO [Controller 2] Unfenced broker: UnfenceBrokerRecord(id=2, epoch=5) (org.apache.kafka.controller.ClusterControlManager)

Diese Ausgabe besagt, dass der Broker gestartet und nun im Zustand RUNNING ist. Falls wir stattdessen viele Fehlermeldungen haben und einer der Broker abstürzt, haben wir einen (Tipp-)Fehler in der Konfiguration. Oft liegt es daran, dass die Port-Konfiguration oder die Verzeichniskonfiguration fehlerhaft sind. Kafkas Fehlermeldungen sind meistens ausführlich. Wenn wir diese Zeile nicht sehen, ist entweder der Broker fehlerhaft konfiguriert oder Zookeeper läuft nicht. Um zu überprüfen, dass alles wirklich läuft, können wir in einem weiteren Fenster folgenden Befehl ausführen:

$ kafka-broker-api-versions.sh \
    --bootstrap-server localhost:9092
localhost:9092 (id: 1 rack: null) -> (
# Viel Text…
)
localhost:9093 (id: 1 rack: null) -> (
# Viel Text…
)
localhost:9094 (id: 1 rack: null) -> (
# Viel Text…
)

Wir stoppen die Broker wieder mit STRG+C. Dies dauert einige Sekunden, weil der Broker Zeit braucht, sich sauber abzumelden. Da unsere Broker gleichzeitig auch eine koordinative Rolle übernehmen, beschweren sie sich zwischenzeitlich, dass nicht genug Broker vorhanden sind. Nun starten wir Kafka im Hintergrund mithilfe des Daemon Modes:

$ kafka-server-start.sh -daemon \
    ~/kafka/config/kafka1.properties
$ kafka-server-start.sh -daemon \
    ~/kafka/config/kafka2.properties
$ kafka-server-start.sh -daemon \
    ~/kafka/config/kafka3.properties

Obwohl die Befehle sich sofort beenden, benötigt Kafka dennoch einige Sekunden, um zu starten. Wenn wir möchten, können wir das Kommandozeilenwerkzeug kafka-broker-api-versions.sh nutzen, um sicherzugehen, dass alle Broker wirklich funktionieren:

$ kafka-broker-api-versions.sh \
  --bootstrap-server localhost:9092
localhost:9092 (id: 1 rack: null) -> (
# Viele Informationen zu kafka1
)
localhost:9093 (id: 2 rack: null) -> (
# Viele Informationen zu kafka2
)
localhost:9094 (id: 3 rack: null) -> (
# Viele Informationen zu kafka3
)

Nun haben wir unsere Kafka-Testumgebung laufen. Nach einem Neustart des Rechners starten die Broker nicht von selbst. Unser hier beschriebenes Setup ist sehr nützlich, wenn wir Dinge auf unserem eigenen Rechner testen möchten. Wir sollten keinesfalls diese Konfigurationsdateien für mehr als lokale Tests benutzen, schon gar nicht in Produktionsumgebungen! Dafür ist dieses Setup zu unsicher, und es läuft auf einem einzigen Rechner, was jegliche Annahmen, die Kafka trifft, ad absurdum führt. Für das Testen und Ausprobieren ist es aber vollkommen ausreichend.

Einzelne Broker stoppen

Das mit Kafka ausgelieferte Skript kafka-server-stop.sh stoppt leider alle Broker auf unserem Rechner statt nur einen gewünschten Broker. Um nur einen Broker herunterzufahren, benutzen wir folgendes Skript. Am besten speichern wir es in der Datei ~/kafka/bin/kafka-broker-stop.sh:

#!/bin/bash
BROKER_ID="$1"

if [ -z "$BROKER_ID" ]; then
  echo "usage ./kafka-broker-stop.sh [BROKER-ID]"
  exit 1
fi

PIDS=$(ps ax | grep -i 'kafka\.Kafka' | grep java | grep "kafka${BROKER_ID}.properties" | grep -v grep | awk '{print $1}')

if [ -z "$PIDS" ]; then
  echo "No kafka server to stop"
  exit 1
else
  kill -s TERM $PIDS
fi

Wir müssen das Skript nur noch ausführbar machen und können es danach benutzen:

$ chmod +x ~/kafka/bin/kafka-broker-stop.sh

Umgebung aufräumen

Nach unseren Tests ist es sinnvoll, die Testumgebung wieder herunterzufahren. Dafür nutzen wir folgende Befehle:

$ kafka-broker-stop.sh 1
$ kafka-broker-stop.sh 2
$ kafka-broker-stop.sh 3

Optional können wir danach den ~/kafka-Ordner löschen, um alle Spuren unserer Installation zu beseitigen.

Zookeeper-basiert

Wir erstellen ein Zookeeper-Ensemble mit drei Servern und starten drei Kafka-Broker. Normalerweise würden wir dies auf getrennten Servern aufsetzen, aber einfachheitshalber starten wir alles auf einem Computer und nutzen dafür verschiedene Ports:

Ein Kafka Cluster mit Zookeeper

Zookeeper starten

Bevor wir Kafka starten können müssen wir unser Zookeeper-Ensemble, bestehend aus drei Zookeeper-Servern, konfigurieren und starten.

Wir beginnen indem wir für jeden Zookeeper-Knoten einen Ordner zu erstellen, um darin die Zookeeper-Daten zu speichern:

$ mkdir -p ~/kafka/data/zk1
$ mkdir -p ~/kafka/data/zk2
$ mkdir -p ~/kafka/data/zk3

In jedem dieser Ordner erstellen wir eine Datei myid, in die wir die ID des Zookeeper-Knotens schreiben, dem dieser Ordner gehört. Wir gehen hier sehr einfach vor: zk1 bekommt die ID 1 und so weiter:

$ echo 1 > ~/kafka/data/zk1/myid
$ echo 2 > ~/kafka/data/zk2/myid
$ echo 3 > ~/kafka/data/zk3/myid

Nun erstellen wir den Ordner ~/kafka/config und darin drei Konfigurationsdateien; zk1.properties bis zk3.properties. Alle drei Dateien sind bis auf den clientPort und das dataDir identisch. Wir zeigen hier beispielhaft die Datei zk1.properties. Die Dateien für die anderen beiden Zookeeper müssen wir entsprechend anpassen:

# Bei zk1.properties
clientPort=2181
dataDir=[PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/zk1
# Bei zk2.properties
#clientPort=2182
#dataDir=[PFAD-ZU-kafka]/data/zk2
# Bei zk3.properties
#clientPort=2183
#dataDir=[PFAD-ZU-kafka]/data/zk3
# Rest bleibt bei allen gleich.
server.1=localhost:2888:3888
server.2=localhost:2889:3889
server.3=localhost:2890:3890

# Nicht in Produktion benutzen!
client.secure=false
tickTime=2000
initLimit=10
syncLimit=5

Nun können wir Zookeeper starten. Standardmäßig startet Zookeeper im Vordergrund. Das heißt, wir benötigen pro Zookeeper ein eigenes Terminal (Wir sollten nicht vergessen, immer die PATH-Variable anzupassen). Bis alle Zookeeper-Server gestartet sind werden wir viele Fehlermeldungen sehen. Danach sollten keine Fehler mehr auftreten.

$ zookeeper-server-start.sh ~/kafka/config/zk1.properties
$ zookeeper-server-start.sh ~/kafka/config/zk2.properties
$ zookeeper-server-start.sh ~/kafka/config/zk3.properties

Um zu testen, ob Zookeeper erfolgreich gestartet ist können wir die Zookeeper Shell benutzen:

$ zookeeper-shell.sh localhost:2181 ls /
Connecting to localhost:2181

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zookeeper]

Wenn dies funktioniert, können wir alle Zookeeper stoppen. Um Terminals zu sparen, starten wir nun Zookeeper mit dem zusätzlichen Flag -daemon im Hintergrund. Danach sollten wir noch testen, ob der zookeeper-shell.sh-Befehl funktioniert:

$ zookeeper-server-start.sh -daemon ~/kafka/config/zk1.properties
$ zookeeper-server-start.sh -daemon ~/kafka/config/zk2.properties
$ zookeeper-server-start.sh -daemon ~/kafka/config/zk3.properties

Kafka starten

Nachdem Zookeeper erfolgreich läuft können wir nun endlich Kafka konfigurieren und es danach starten. Ähnlich wie bei Zookeeper legen wir für jeden Kafka-Broker einen eigenen Ordner an:

$ mkdir -p ~/kafka/data/kafka1
$ mkdir -p ~/kafka/data/kafka2
$ mkdir -p ~/kafka/data/kafka3

Nun erstellen wir für jeden Broker eine Konfigurationsdatei im Ordner ~/kafka/config: kafka1.properties für Broker 1 und kafka2.properties und kafka3.properties für die beiden anderen Broker. Wieder zeigen wir hier nur die Konfigurationsdatei für den Broker 1. Für die beiden anderen Broker muss die Datei entsprechend angepasst werden:

# Bei kafka1.properties
broker.id=1
log.dirs=[PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/kafka1
listeners=PLAINTEXT://:9092

# Bei kafka2.properties
#broker.id=2
#log.dirs=[PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/kafka2
#listeners=PLAINTEXT://:9093

# Bei kafka3.properties
#broker.id=3
#log.dirs=[PFAD-ZUM-NUTZER-VERZEICHNIS]/kafka/data/kafka3
#listeners=PLAINTEXT://:9094

# Rest bleibt bei allen gleich.
zookeeper.connect=localhost:2181

Wir starten testweise einen Broker, um zu überprüfen, ob die Konfigurationsdatei korrekt ist und das Zusammenspiel mit Zookeeper einwandfrei funktioniert:

$ kafka-server-start.sh ~/kafka/config/kafka1.properties

Nach sehr vielen Log-Meldungen sollte nach einigen Sekunden Ruhe einkehren und wir sollten folgende Zeile sehen:

[…] INFO [broker-1-to-controller-send-thread]: Recorded new controller,
    from now on will use broker localhost:9092 (id: 1 rack: null)
    (kafka.server.BrokerToControllerRequestThread)

Wenn wir diese Zeile nicht sehen ist entweder der Broker fehlerhaft konfiguriert oder Zookeeper läuft nicht. Um zu überprüfen, dass alles wirklich läuft, können wir in einem weiteren Fenster folgenden Befehl ausführen:

$ kafka-broker-api-versions.sh \
    --bootstrap-server localhost:9092
localhost:9092 (id: 1 rack: null) -> (
# Viel Text…
)

Wir stoppen die Broker wieder mit STRG+C. Dies dauert einige Sekunden, die der Broker benötigt, um sich sauber abzumelden. Nun starten wir Kafka, ähnlich wie Zookeeper, im Hintergrund mithilfe des Daemon-Modes:

$ kafka-server-start.sh -daemon \
    ~/kafka/config/kafka1.properties
$ kafka-server-start.sh -daemon \
    ~/kafka/config/kafka2.properties
$ kafka-server-start.sh -daemon \
    ~/kafka/config/kafka3.properties

Obwohl die Befehle sich sofort beenden benötigt Kafka dennoch einige Sekunden, um zu starten. Wenn wir möchten können wir das Kommandozeilenwerkzeug kafka-broker-api-versions.sh nutzen, um sicherzugehen, dass alle Broker wirklich funktionieren:

$ kafka-broker-api-versions.sh \
  --bootstrap-server localhost:9092
localhost:9092 (id: 1 rack: null) -> (
# Viele Informationen zu kafka1
)
localhost:9093 (id: 2 rack: null) -> (
# Viele Informationen zu kafka2
)
localhost:9094 (id: 3 rack: null) -> (
# Viele Informationen zu kafka3
)

Nun läuft unsere Kafka-Testumgebung. Nach einem Neustart des Rechners starten weder Zookeeper noch Broker von selbst. Wir nutzen die Befehle zookeeper-server-start.sh und kafka-server-start.sh, um die Server zu starten. Wichtig ist dabei, erst alle Zookeeper und danach erst die Kafka-Broker zu starten!

Unser hier beschriebenes Setup ist sehr nützlich, wenn wir Dinge auf unserem eigenen Rechner testen möchten. Wir sollten keinesfalls diese Konfigurationsdateien für mehr als lokale Tests benutzen; schon gar nicht in Produktionsumgebungen! Dafür ist dieses Setup zu unsicher, und es läuft auf einem einzigen Rechner, was jegliche Annahmen, die Kafka trifft, ad absurdum führt. Für das Testen und Ausprobieren ist es aber vollkommen ausreichend.

Einzelne Broker stoppen

Das mit Kafka ausgelieferte Skript kafka-server-stop.sh stoppt leider alle Broker auf unserem Rechner statt nur einen gewünschten Broker. Um nur einen Broker herunterzufahren, benutzen wir dasselbe Script wie in der KRaft-Version oben.

Umgebung aufräumen

Nach unseren Tests ist es sinnvoll, die Testumgebung wieder herunterzufahren. Auch hier ist es wichtig, auf die Reihenfolge zu achten: Wir stoppen erst alle Kafka-Broker und Zookeeper:

$ kafka-broker-stop.sh 1
$ kafka-broker-stop.sh 2
$ kafka-broker-stop.sh 3
$ zookeeper-server-stop.sh ~/kafka/config/zk1.properties
$ zookeeper-server-stop.sh ~/kafka/config/zk2.properties
$ zookeeper-server-stop.sh ~/kafka/config/zk3.properties

Optional können wir danach den ~/kafka-Ordner löschen, um alle Spuren unserer Installation zu beseitigen.

War dieser Artikel hilfreich? Dies ist ein Auszug aus unserem Apache Kafka Buch.

Das Buch ist direkt bei uns, beim Hanser-Verlag, Amazon, und überall wo es Bücher gibt erhältlich. Für Unterstützung und Fragen Rund um Kafka schreib uns einfach oder buche dir ein Kennenlerngespräch hier.

Neuerscheinung: Das deutsche Apache Kafka Buch im Hanser Verlag.
This is default text for notification bar