Best Practices für Elasticsearch-Sniffing: Was, wann, warum, wie?
Elasticsearch ist die Grundlage für die Sucherlebnisse in vielen der heute verbreiteten Tools und Anwendungen: von Analyse-Dashboards bis hin zu Landkarten, die die nächstgelegenen Restaurants mit Außensitzen anzeigen, damit Sie mal aus dem Haus kommen. In all diesen Implementierungen kommt für die Verbindung zwischen der Anwendung und dem Cluster ein Elasticsearch-Client zum Einsatz.
Elasticsearch ist ein verteiltes System, und für das Nutzungserlebnis der Endnutzer:innen ist es extrem wichtig, die Verbindung zwischen dem Client und dem Elasticsearch-Cluster zu optimieren. Die typische Konfiguration eines Elasticsearch-Clients ist die URL des Knotens, zu dem Sie eine Verbindung herstellen müssen. Sie können aber noch viel mehr eingreifen, und eine Möglichkeit, diese Verbindung zu optimieren, ist Sniffing.
Hier erfahren Sie, wie Sniffing funktioniert, wann Sie es nutzen sollten und woher Sie wissen, wann Sniffing keine gute Idee ist.
Was ist Sniffing?
Elasticsearch ist ein verteiltes System, also ein System, dessen Indizes sich auf mehreren Knoten befinden, die untereinander verbunden sind und so einen Cluster bilden. So ein verteiltes System hat viele Vorteile, wie z. B. die Fähigkeit, Ausfälle einzelner Komponenten verkraften zu können. Ein weiterer Vorteil besteht darin, dass die Daten auf mehrere Knoten (sogenannte Shards) aufgeteilt sind, was viel schnellere Suchen als bei der Verarbeitung in einem einzelnen Riesenknoten ermöglicht.
Eine typische Client-Konfiguration besteht aus nur einer URL, die auf einen konkreten Knoten des Elasticsearch-Clusters verweist. Diese Konfiguration ist zwar so einfach wie keine zweite, hat aber den großen Nachteil, dass alle Ihre Anfragen an diesen konkreten Koordinierungsknoten gesendet werden. Das bedeutet Stress für diesen einen Knoten, worunter die Gesamt-Performance leiden kann.
Eine Lösung besteht darin, dem Client eine statische Liste von Knoten zu übergeben, sodass Ihre Anfragen gleichmäßig auf die Knoten verteilt werden.
Oder aber Sie aktivieren die Sniffing-Funktion.
Bei einer statischen Liste von Knoten ist nicht garantiert, dass alle Knoten ständig im Einsatz sind und funktionieren. Was, wenn Sie einen Knoten außer Betrieb setzen müssen, um ein Update aufzuspielen, oder wenn Sie neue Knoten hinzufügen?
Wenn Sie Sniffing aktivieren, beginnt der Client den Endpoint _nodes/_all/http aufzurufen. Als Antwort wird eine Liste mit den im Cluster vorhandenen Knoten und deren IP-Adressen zurückgegeben. Der Client wird dann seinen Verbindungspool aktualisieren, um auch alle neu hinzugekommenen Knoten nutzen und den Zustand des Clusters mit dem Verbindungspool des Clients synchron halten zu können. Zu beachten ist dabei, dass selbst dann, wenn die Clients die vollständige Knotenliste herunterladen, die reinen Masterknoten für allgemeine API-Aufrufe nicht verwendet werden.
Sniffing löst dieses „Discovery“-Problem. Warum ist die Funktion dann nicht automatisch aktiviert? Gute Frage!
Wann sollte Sniffing verwendet werden?
Sniffing kann eine zweischneidige Angelegenheit sein. Wenn Sie den Endpoint _nodes/_all/http aufrufen, wird zwar eine Liste der Knoten und der zugehörigen Endpoints zurückgegeben, es muss aber Folgendes bedacht werden:
- Was passiert, wenn sich Ihr Elasticsearch-Cluster in einem eigenen Netzwerk befindet?
- Was, wenn Ihrem Elasticsearch-Cluster ein Load Balancer vorgeschaltet ist?
Die kurze Antwort auf beide Fragen lautet: Sie erhalten vollkommen unnütze IP-Adressen, weil Sie sich in einem anderen Netzwerk befinden.
Das können Sie mit Docker selbst ausprobieren. Richten Sie eine Elasticsearch-Instanz ein (eine reicht) und rufen Sie von Ihrem lokalen Rechner aus _nodes/_all/http auf. Sie werden feststellen, dass die IP-Adresse Ihres Knotens nicht identisch ist mit der IP-Adresse, die Sie gerade verwendet haben.
Starten Sie mit dem folgenden Befehl eine Elasticsearch-Instanz:
docker run \
-p 9200:9200 \
-e "discovery.type=single-node" \
docker.elastic.co/elasticsearch/elasticsearch:7.8.0
Jetzt können Sie mit dem folgenden Befehl die IP-Adresse des Knotens lesen (im folgenden Snippet verwenden wir jq, damit die Antwort einfacher lesbar ist):
curl -s localhost:9200/_nodes/_all/http | jq .nodes[].http.publish_address
Zum Schluss können Sie die im Terminal ausgedruckte IP-Adresse kopieren und versuchen, dieser Adresse eine Anfrage zu senden:
curl {ip_address}:9200 | jq .
Wie Sie sehen, erhalten Sie keine sinnvolle Antwort.
Das bedeutet: Wenn Sie Sniffing in einem Client aktivieren und sich der Cluster in einem anderen Netzwerk befindet, fügt der Client seinem Verbindungspool alle neuen Knoten hinzu. Der Grund: Er hat keine Möglichkeit festzustellen, dass diese IP-Adressen falsch sind. Die Folge ist, dass jede Abfrage, die sich auf einen dieser Knoten bezieht, fehlschlagen muss.
Da der ursprüngliche Knoten mit der korrekten IP-Adresse im Zustand des Clusters nicht mehr vorhanden ist, wird er verworfen, und Sie erhalten sehr schnell die Fehlermeldung, dass es keine lebendigen Verbindungen gibt.
Aber wir können etwas dagegen tun.
Zur Lösung des Problems können Sie Elasticsearch so konfigurieren, dass es sich zwar an seinen Host bindet, aber nach außen einen anderen ankündigt. Genau das tut die Konfigurationsoption http.publish_host. Versuchen Sie jetzt, den Docker-Befehl von oben mit dieser neuen Konfiguration auszuführen:
docker run \
-p 9200:9200 \
-e "discovery.type=single-node" \
-e "http.publish_host=localhost" \
docker.elastic.co/elasticsearch/elasticsearch:7.8.0
Wenn Sie jetzt den folgenden Befehl noch einmal ausführen …
curl -s localhost:9200/_nodes/_all/http | jq .nodes[].http.publish_address
… sehen Sie im Terminal Folgendes:
"localhost/{ip_address}:9200"
Beim Konfigurieren von publish_host sind die offiziellen Clients (ab v7) intelligent genug, statt der IP-Adresse die Host-Adresse zu verwenden.
Die wichtigste Erkenntnis daraus ist, dass Sie Ihre Infrastruktur kennen sollten, bevor Sie Sniffing aktivieren. Für dieses IP-Adressen-Problem gibt es viele Lösungen, ein Patentrezept ist jedoch nicht dabei, weil alles von Ihrer Systemkonfiguration abhängt.
Beim typischen Deployment-Setup befindet sich der Elasticsearch-Cluster im selben Netzwerk wie der Client, aber das kann in der realen Welt nicht repliziert werden, da es zu Sicherheitsproblemen führen würde – und Ihre Infrastruktur ist wahrscheinlich komplexer. Sie könnten für den Umgang mit diesen IP-Adressen den Load Balancer konfigurieren. Oder Sie könnten, wie es Elastic in Elastic Cloud macht, die ausfallenden Knoten dem Proxy überlassen, sodass der Client die Abfragen immer an den Proxy sendet, der sie dann an den entsprechenden Knoten weiterleitet.
Wann sollte auf Sniffing verzichtet werden?
Es gibt viele Situationen, in denen Sniffing zu Problemen führen kann. Dafür zwei Beispiele:
- Der Elasticsearch-Nutzer, mit dem sich Ihr Client authentifiziert, hat nicht die richtigen Berechtigungen (Rolle monitoring_user), um auf die Knoten-API zuzugreifen.
- Sie nutzen die Dienste von Cloud-Anbietern.
Cloud-Anbieter verbergen Elasticsearch in der Regel hinter einem Proxy, was das Sniffing sinnlos macht, weil die zurückgegebenen Adressen und Hostnamen für Ihr Netzwerk ohne Bedeutung sind. Normalerweise übernehmen diese Cloud-Anbieter das Sniffing und Pooling für Sie, sodass sie diese Funktionen nicht selbst aktivieren müssen.
Wenn Sie Elastic Cloud verwenden, umgehen die offiziellen Clients die meisten Operationen, z. B. den Rückgriff auf den Verbindungspool, um Zeit bei Operationen zu sparen, die bereits ausgeführt wurden.
Beim Arbeiten mit Docker oder Kubernetes können andere Probleme auftreten, wie wir oben gesehen haben. Das Sniffing-Ergebnis ist nur dann sinnvoll und nutzbar, wenn Sie die Option publish_host konfigurieren.
Als Faustregel gilt: Wenn sich Elasticsearch in einem anderen Netzwerk als Ihr Client befindet oder wenn es einen Load Balancer gibt, sollte das Sniffing deaktiviert werden, es sei denn, die Infrastruktur ist so konfiguriert, dass Sniffing korrekt genutzt werden kann.
Wie funktioniert Sniffing?
Clients bieten mehrere Sniffing-Strategien. Sehen wir uns diese etwas genauer an:
Sniffing beim Starten
Wenn Sie diese Form des Sniffings aktivieren, versucht der Client nur ein einziges Mal während der Client-Initialisierung oder der ersten Verwendung, eine Sniff-Anfrage auszuführen.
Sniffing bei Verbindungsabbruch
Wenn Sie diese Option aktivieren, versucht der Client immer dann eine Sniff-Anfrage auszuführen, wenn ein Knoten fehlerhaft ist, d. h., wenn eine Verbindung unterbrochen wurde oder der Knoten tot ist.
Sniff-Intervall
Neben dem Sniffing beim Starten und dem Sniffing bei Verbindungsabbruch kann es auch sinnvoll sein, ein Sniffing in regelmäßigen Abständen zuzulassen, nämlich dann, wenn Cluster während der Spitzenzeiten häufig horizontal skaliert werden. Das bietet sich in Situationen an, wo eine Anwendung zwar eine Teilmenge der Knoten gut im Blick hat, aber ohne regelmäßiges Sniffing nicht in der Lage wäre, die Knoten zu finden, die im Rahmen der horizontalen Skalierung hinzugefügt wurden.
Individuelle Konfigurationen
Es kann Situationen geben, in denen Sie den Vorgang des Sniffings genauer steuern können möchten. Dank der Flexibilität der Clients können Sie einen benutzerdefinierten Sniffing-Endpoint konfigurieren oder die Sniffing-Logik komplett außer Kraft setzen und einen eigenen Endpoint bereitstellen.
Fazit
Durch Aktivieren von Sniffing machen Sie Ihre Anwendung widerstands- und anpassungsfähiger. Bevor Sie sich für das Sniffing entscheiden, sollten Sie aber Ihre Infrastruktur kennen, um entscheiden zu können, welche Sniffing-Lösung die geeignetste ist. Es kann durchaus sein, dass es am besten ist, kein Sniffing zu nutzen.
Wenn Sie sich gar keine Gedanken über Sniffing und die Konfiguration des Verbindungspools machen und stattdessen einfach nur einen Verbindungsstring verwenden möchten, können Sie Elastic Cloud nutzen – probieren Sie Elastic Cloud 14 Tage lang kostenlos aus.