Elasticsearch-Cluster 200 TB+

Elasticsearch-Cluster 200 TB+

Viele Menschen haben Probleme mit Elasticsearch. Doch was passiert, wenn man damit Protokolle „in besonders großem Umfang“ speichern möchte? Und ist es auch schmerzlos, den Ausfall mehrerer Rechenzentren zu erleben? Welche Art von Architektur sollten Sie erstellen und auf welche Fallstricke werden Sie stoßen?

Wir bei Odnoklassniki haben uns entschieden, Elasticsearch zu verwenden, um das Problem der Protokollverwaltung zu lösen, und jetzt teilen wir unsere Erfahrungen mit Habr: sowohl über Architektur als auch über Fallstricke.

Ich bin Pjotr ​​Zaitsev und arbeite als Systemadministrator bei Odnoklassniki. Davor war ich auch Administrator und habe mit Manticore Search, Sphinx Search und Elasticsearch gearbeitet. Vielleicht, wenn eine andere ...Suche erscheint, werde ich wahrscheinlich auch damit arbeiten. Darüber hinaus beteilige ich mich ehrenamtlich an zahlreichen Open-Source-Projekten.

Als ich zu Odnoklassniki kam, sagte ich beim Vorstellungsgespräch leichtsinnig, dass ich mit Elasticsearch arbeiten könnte. Nachdem ich den Dreh raus hatte und einige einfache Aufgaben erledigt hatte, wurde mir die große Aufgabe übertragen, das damals bestehende Protokollverwaltungssystem zu reformieren.

Bedarf

Die Systemanforderungen wurden wie folgt formuliert:

  • Als Frontend sollte Graylog zum Einsatz kommen. Da das Unternehmen bereits Erfahrung mit diesem Produkt hatte, kannten Programmierer und Tester es, es war ihnen vertraut und praktisch.
  • Datenvolumen: durchschnittlich 50-80 Nachrichten pro Sekunde, aber wenn etwas kaputt geht, ist der Datenverkehr durch nichts begrenzt, er kann 2-3 Millionen Zeilen pro Sekunde betragen
  • Nachdem wir mit Kunden die Anforderungen an die Geschwindigkeit der Verarbeitung von Suchanfragen besprochen hatten, stellten wir fest, dass das typische Muster bei der Verwendung eines solchen Systems wie folgt aussieht: Menschen suchen nach Protokollen ihrer Bewerbung für die letzten zwei Tage und möchten nicht länger als einen warten zweitens für das Ergebnis einer formulierten Abfrage.
  • Die Administratoren bestanden darauf, dass das System bei Bedarf leicht skalierbar sei, ohne dass sie sich tief in die Funktionsweise vertiefen müssten.
  • Daher ist die einzige Wartungsaufgabe, die diese Systeme regelmäßig erfordern, der Austausch einiger Hardware.
  • Darüber hinaus verfügt Odnoklassniki über eine hervorragende technische Tradition: Jeder Dienst, den wir starten, muss einen Ausfall des Rechenzentrums überstehen (plötzlich, ungeplant und absolut jederzeit).

Die letzte Anforderung bei der Umsetzung dieses Projekts hat uns am meisten gekostet, auf die ich näher eingehen werde.

Mittwoch

Wir arbeiten in vier Rechenzentren, während sich Elasticsearch-Datenknoten (aus verschiedenen nichttechnischen Gründen) nur in drei befinden können.

Diese vier Rechenzentren enthalten etwa 18 verschiedene Protokollquellen – Hardware, Container, virtuelle Maschinen.

Wichtiges Feature: Der Cluster startet in Containern Podman nicht auf physischen Maschinen, sondern auf eigenes Cloud-Produkt One-Cloud. Den Containern werden 2 Kerne garantiert, ähnlich wie bei 2.0 GHz v4, mit der Möglichkeit, die verbleibenden Kerne zu recyceln, wenn sie inaktiv sind.

Mit anderen Worten:

Elasticsearch-Cluster 200 TB+

Topologie

Die allgemeine Form der Lösung sah ich zunächst wie folgt aus:

  • 3-4 VIPs befinden sich hinter dem A-Record der Graylog-Domäne. Dies ist die Adresse, an die die Protokolle gesendet werden.
  • Jeder VIP ist ein LVS-Balancer.
  • Danach werden die Protokolle an die Graylog-Batterie gesendet. Einige der Daten liegen im GELF-Format vor, andere im Syslog-Format.
  • Dann wird das alles in großen Mengen an eine Batterie von Elasticsearch-Koordinatoren geschrieben.
  • Und diese wiederum senden Schreib- und Leseanfragen an die entsprechenden Datenknoten.

Elasticsearch-Cluster 200 TB+

Vocabulary

Vielleicht versteht nicht jeder die Terminologie im Detail, deshalb möchte ich etwas näher darauf eingehen.

Elasticsearch verfügt über mehrere Arten von Knoten – Master-, Koordinator- und Datenknoten. Es gibt zwei weitere Typen für unterschiedliche Protokolltransformationen und die Kommunikation zwischen verschiedenen Clustern, wir haben jedoch nur die aufgeführten verwendet.

Master
Es pingt alle im Cluster vorhandenen Knoten an, verwaltet eine aktuelle Cluster-Karte und verteilt sie zwischen den Knoten, verarbeitet die Ereignislogik und führt verschiedene Arten der Cluster-weiten Verwaltung durch.

-Koordinator
Führt eine einzige Aufgabe aus: Akzeptiert Lese- oder Schreibanfragen von Clients und leitet diesen Datenverkehr weiter. Falls eine Schreibanforderung vorliegt, wird der Master höchstwahrscheinlich fragen, in welchen Shard des relevanten Index er ihn einfügen soll, und die Anforderung weiterleiten.

Datenknoten
Speichert Daten, führt von außen eingehende Suchanfragen durch und führt Operationen an darauf befindlichen Shards durch.

Graylog
Dies ist so etwas wie eine Fusion von Kibana mit Logstash in einem ELK-Stack. Graylog kombiniert sowohl eine Benutzeroberfläche als auch eine Protokollverarbeitungspipeline. Unter der Haube betreibt Graylog Kafka und Zookeeper, die als Cluster Konnektivität zu Graylog bereitstellen. Graylog kann Protokolle (Kafka) zwischenspeichern, falls Elasticsearch nicht verfügbar ist, und erfolglose Lese- und Schreibanforderungen wiederholen sowie Protokolle gemäß festgelegten Regeln gruppieren und markieren. Graylog verfügt wie Logstash über die Funktionalität, Zeilen zu ändern, bevor sie in Elasticsearch geschrieben werden.

Darüber hinaus verfügt Graylog über eine integrierte Service-Erkennung, die es ermöglicht, basierend auf einem verfügbaren Elasticsearch-Knoten die gesamte Cluster-Karte abzurufen und sie nach einem bestimmten Tag zu filtern, was es ermöglicht, Anfragen an bestimmte Container zu leiten.

Optisch sieht es in etwa so aus:

Elasticsearch-Cluster 200 TB+

Dies ist ein Screenshot einer bestimmten Instanz. Hier erstellen wir ein Histogramm basierend auf der Suchanfrage und zeigen relevante Zeilen an.

Indizes

Zurück zur Systemarchitektur: Ich möchte näher darauf eingehen, wie wir das Indexmodell erstellt haben, damit alles korrekt funktioniert.

Im obigen Diagramm ist dies die unterste Ebene: Elasticsearch-Datenknoten.

Ein Index ist eine große virtuelle Einheit, die aus Elasticsearch-Shards besteht. Für sich genommen ist jeder der Shards nichts anderes als ein Lucene-Index. Und jeder Lucene-Index besteht wiederum aus einem oder mehreren Segmenten.

Elasticsearch-Cluster 200 TB+

Beim Entwurf haben wir festgestellt, dass wir diese Daten gleichmäßig auf die Datenknoten „verteilen“ müssen, um die Anforderungen an die Lesegeschwindigkeit einer großen Datenmenge zu erfüllen.

Dies führte dazu, dass die Anzahl der Shards pro Index (mit Replikaten) genau der Anzahl der Datenknoten entsprechen sollte. Erstens, um einen Replikationsfaktor von zwei sicherzustellen (das heißt, wir können die Hälfte des Clusters verlieren). Und zweitens, um Lese- und Schreibanfragen auf mindestens der Hälfte des Clusters zu verarbeiten.

Als Lagerzeit haben wir zunächst 30 Tage ermittelt.

Die Verteilung der Shards lässt sich grafisch wie folgt darstellen:

Elasticsearch-Cluster 200 TB+

Das gesamte dunkelgraue Rechteck ist ein Index. Das linke rote Quadrat darin ist der primäre Shard, der erste im Index. Und das blaue Quadrat ist eine Nachbildung einer Scherbe. Sie befinden sich in verschiedenen Rechenzentren.

Wenn wir einen weiteren Shard hinzufügen, geht dieser an das dritte Rechenzentrum. Und am Ende erhalten wir diese Struktur, die es ermöglicht, DC zu verlieren, ohne die Datenkonsistenz zu verlieren:

Elasticsearch-Cluster 200 TB+

Rotation der Indizes, d.h. Nachdem wir einen neuen Index erstellt und den ältesten gelöscht haben, haben wir ihn auf 48 Stunden festgelegt (entsprechend dem Muster der Indexverwendung: Die letzten 48 Stunden werden am häufigsten durchsucht).

Dieses Indexrotationsintervall hat folgende Gründe:

Wenn eine Suchanfrage an einem bestimmten Datenknoten ankommt, ist es aus Performance-Sicht profitabler, wenn ein Shard abgefragt wird, wenn seine Größe mit der Größe der Hüfte des Knotens vergleichbar ist. Dadurch können Sie den „heißen“ Teil des Index auf einem Heap halten und schnell darauf zugreifen. Wenn es viele „heiße Teile“ gibt, nimmt die Geschwindigkeit der Indexsuche ab.

Wenn ein Knoten mit der Ausführung einer Suchabfrage auf einem Shard beginnt, weist er eine Anzahl von Threads zu, die der Anzahl der Hyperthreading-Kerne der physischen Maschine entspricht. Wenn eine Suchanfrage eine große Anzahl von Shards betrifft, wächst die Anzahl der Threads proportional. Dies wirkt sich negativ auf die Suchgeschwindigkeit aus und wirkt sich negativ auf die Indizierung neuer Daten aus.

Um die nötige Suchlatenz bereitzustellen, haben wir uns für den Einsatz einer SSD entschieden. Um Anfragen schnell verarbeiten zu können, mussten die Maschinen, auf denen diese Container gehostet wurden, über mindestens 56 Kerne verfügen. Als bedingt ausreichender Wert wurde der Wert 56 gewählt, der die Anzahl der Threads bestimmt, die Elasticsearch während des Betriebs generiert. In Elasitcsearch hängen viele Thread-Pool-Parameter direkt von der Anzahl der verfügbaren Kerne ab, was sich wiederum direkt auf die erforderliche Anzahl von Knoten im Cluster nach dem Prinzip „weniger Kerne – mehr Knoten“ auswirkt.

Als Ergebnis haben wir herausgefunden, dass ein Shard im Durchschnitt etwa 20 Gigabyte wiegt und es 1 Shards pro Index gibt. Wenn wir sie dementsprechend einmal alle 360 Stunden rotieren lassen, dann haben wir 48 davon. Jeder Index enthält Daten für 15 Tage.

Schaltungen zum Schreiben und Lesen von Daten

Lassen Sie uns herausfinden, wie Daten in diesem System aufgezeichnet werden.

Nehmen wir an, von Graylog geht eine Anfrage an den Koordinator ein. Wir möchten beispielsweise 2-3 Zeilen indizieren.

Der Koordinator, der eine Anfrage von Graylog erhalten hat, befragt den Master: „In der Indexierungsanfrage haben wir ausdrücklich einen Index angegeben, aber in welchen Shard er geschrieben werden soll, wurde nicht angegeben.“

Der Master antwortet: „Schreiben Sie diese Informationen auf Shard Nummer 71“, woraufhin sie direkt an den entsprechenden Datenknoten gesendet werden, wo sich der Primär-Shard Nummer 71 befindet.

Anschließend wird das Transaktionsprotokoll auf einen Replikat-Shard repliziert, der sich in einem anderen Rechenzentrum befindet.

Elasticsearch-Cluster 200 TB+

Von Graylog geht eine Suchanfrage an den Koordinator. Der Koordinator leitet es entsprechend dem Index weiter, während Elasticsearch Anfragen nach dem Round-Robin-Prinzip zwischen dem Primär-Shard und dem Replikat-Shard verteilt.

Elasticsearch-Cluster 200 TB+

Die 180 Knoten reagieren ungleichmäßig, und während sie antworten, sammelt der Koordinator Informationen an, die bereits von schnelleren Datenknoten „ausgespuckt“ wurden. Wenn danach entweder alle Informationen eingetroffen sind oder die Anfrage ein Timeout erreicht hat, wird alles direkt an den Client weitergeleitet.

Das gesamte System verarbeitet Suchanfragen der letzten 48 Stunden im Durchschnitt in 300–400 ms, mit Ausnahme von Suchanfragen mit einem führenden Platzhalter.

Blumen mit Elasticsearch: Java-Setup

Elasticsearch-Cluster 200 TB+

Damit alles so funktioniert, wie wir es ursprünglich wollten, haben wir sehr viel Zeit damit verbracht, eine Vielzahl von Dingen im Cluster zu debuggen.

Der erste Teil der entdeckten Probleme hing mit der Art und Weise zusammen, wie Java in Elasticsearch standardmäßig vorkonfiguriert ist.

Das erste Problem
Wir haben sehr viele Berichte gesehen, dass auf Lucene-Ebene, wenn Hintergrundjobs ausgeführt werden, die Zusammenführung von Lucene-Segmenten mit einem Fehler fehlschlägt. Gleichzeitig war in den Protokollen klar, dass es sich um einen OutOfMemoryError-Fehler handelte. Anhand der Telemetrie sahen wir, dass die Hüfte frei war, und es war nicht klar, warum diese Operation fehlschlug.

Es stellte sich heraus, dass Lucene-Indexverschmelzungen außerhalb der Hüfte auftreten. Und Container sind hinsichtlich des Ressourcenverbrauchs recht streng begrenzt. Nur der Heap konnte in diese Ressourcen passen (der heap.size-Wert entsprach ungefähr dem RAM), und einige Off-Heap-Vorgänge stürzten mit einem Speicherzuordnungsfehler ab, wenn sie aus irgendeinem Grund nicht in die ~500 MB passten, die vor dem Limit übrig blieben.

Der Fix war recht trivial: Die für den Container verfügbare RAM-Menge wurde erhöht, woraufhin wir vergaßen, dass wir solche Probleme überhaupt hatten.

Problem zwei
4–5 Tage nach dem Start des Clusters stellten wir fest, dass Datenknoten regelmäßig aus dem Cluster herausfielen und nach 10–20 Sekunden in ihn eintraten.

Als wir anfingen, es herauszufinden, stellte sich heraus, dass dieser Off-Heap-Speicher in Elasticsearch in keiner Weise kontrolliert wird. Als wir dem Container mehr Speicher zur Verfügung stellten, konnten wir die direkten Pufferpools mit verschiedenen Informationen füllen, und diese wurden erst gelöscht, nachdem der explizite GC von Elasticsearch aus gestartet wurde.

In einigen Fällen dauerte dieser Vorgang ziemlich lange und während dieser Zeit gelang es dem Cluster, diesen Knoten als bereits beendet zu markieren. Dieses Problem ist gut beschrieben hier.

Die Lösung war wie folgt: Wir haben die Fähigkeit von Java eingeschränkt, den Großteil des Speichers außerhalb des Heaps für diese Vorgänge zu verwenden. Wir haben es auf 16 Gigabyte begrenzt (-XX:MaxDirectMemorySize=16g), um sicherzustellen, dass expliziter GC viel häufiger aufgerufen und viel schneller verarbeitet wird, wodurch der Cluster nicht länger destabilisiert wird.

Problem drei
Wenn Sie denken, dass die Probleme mit „Knoten, die den Cluster im unerwartetsten Moment verlassen“, vorbei sind, irren Sie sich.

Als wir die Arbeit mit Indizes konfiguriert haben, haben wir mmapfs ausgewählt Reduzieren Sie die Suchzeit auf frischen Scherben mit toller Segmentierung. Das war ein ziemlicher Fehler, denn bei der Verwendung von mmapfs wird die Datei in den RAM gemappt und wir arbeiten dann mit der gemappten Datei. Aus diesem Grund stellt sich heraus, dass wir, wenn der GC versucht, Threads in der Anwendung zu stoppen, sehr lange zum Sicherheitspunkt gehen und die Anwendung auf dem Weg dorthin nicht mehr auf die Anfragen des Masters reagiert, ob sie aktiv ist . Dementsprechend geht der Master davon aus, dass der Knoten nicht mehr im Cluster vorhanden ist. Danach, nach 5–10 Sekunden, funktioniert der Garbage Collector, der Knoten erwacht zum Leben, tritt erneut in den Cluster ein und beginnt mit der Initialisierung von Shards. Es fühlte sich alles sehr nach der „Inszenierung an, die wir verdienten“ und war für nichts Ernstes geeignet.

Um dieses Verhalten zu beseitigen, sind wir zunächst auf Standard-NIOFs umgestiegen und haben dann, als wir von der fünften Version von Elastic auf die sechste migrierten, HybridFS ausprobiert, bei dem dieses Problem nicht reproduziert wurde. Sie können mehr über Speichertypen lesen hier.

Problem vier
Dann gab es noch ein weiteres sehr interessantes Problem, das wir in Rekordzeit behandelt haben. Wir haben es zwei bis drei Monate lang gefangen, weil sein Muster absolut unverständlich war.

Manchmal gingen unsere Koordinatoren zum Full GC, normalerweise irgendwann nach dem Mittagessen, und kehrten nie von dort zurück. Gleichzeitig sah es bei der Protokollierung der GC-Verzögerung so aus: Alles läuft gut, gut, gut, und dann läuft plötzlich alles ganz schlecht.

Zuerst dachten wir, wir hätten einen bösen Benutzer, der eine Anfrage stellte, die den Koordinator aus dem Arbeitsmodus brachte. Wir haben sehr lange Anfragen protokolliert und versucht herauszufinden, was passiert ist.

Als Ergebnis stellte sich heraus, dass in dem Moment, in dem ein Benutzer eine große Anfrage startet und diese an einen bestimmten Elasticsearch-Koordinator gelangt, einige Knoten länger antworten als andere.

Und während der Koordinator auf eine Antwort von allen Knoten wartet, sammelt er die Ergebnisse, die von den Knoten gesendet wurden, die bereits geantwortet haben. Für GC bedeutet dies, dass sich unsere Heap-Nutzungsmuster sehr schnell ändern. Und der von uns verwendete GC konnte diese Aufgabe nicht bewältigen.

Die einzige Lösung, die wir gefunden haben, um das Verhalten des Clusters in dieser Situation zu ändern, ist die Migration auf JDK13 und die Verwendung des Shenandoah-Garbage Collectors. Damit war das Problem gelöst, unsere Koordinatoren stürzten nicht mehr.

Hier endeten die Probleme mit Java und die Bandbreitenprobleme begannen.

„Beeren“ mit Elasticsearch: Durchsatz

Elasticsearch-Cluster 200 TB+

Probleme mit dem Durchsatz führen dazu, dass unser Cluster stabil arbeitet, aber bei Spitzen in der Anzahl indizierter Dokumente und bei Manövern reicht die Leistung nicht aus.

Das erste aufgetretene Symptom: Während einiger „Explosionen“ in der Produktion, wenn plötzlich eine sehr große Anzahl von Protokollen generiert wird, beginnt der Indizierungsfehler es_rejected_execution in Graylog häufig zu blinken.

Dies lag daran, dass thread_pool.write.queue auf einem Datenknoten standardmäßig nur 200 Anfragen zwischenspeichern kann, bis Elasticsearch die Indizierungsanfrage verarbeiten und die Informationen in den Shard auf der Festplatte hochladen kann. Und in Elasticsearch-Dokumentation Über diesen Parameter wird sehr wenig gesagt. Es werden lediglich die maximale Anzahl an Threads und die Standardgröße angegeben.

Natürlich haben wir diesen Wert verdreht und Folgendes herausgefunden: Konkret werden in unserem Setup bis zu 300 Anfragen recht gut zwischengespeichert, und ein höherer Wert ist mit der Tatsache behaftet, dass wir wieder in Full GC fliegen.

Da es sich außerdem um Stapel von Nachrichten handelt, die innerhalb einer Anfrage eingehen, war es notwendig, Graylog so zu optimieren, dass es nicht oft und in kleinen Stapeln schreibt, sondern in großen Stapeln oder einmal alle 3 Sekunden, wenn der Stapel noch nicht vollständig ist. In diesem Fall stellt sich heraus, dass die Informationen, die wir in Elasticsearch schreiben, nicht in zwei Sekunden, sondern in fünf Sekunden verfügbar sind (was uns recht gut passt), aber die Anzahl der Wiederholungen, die durchgeführt werden müssen, um durchzukommen, ist groß Der Informationsstapel wird reduziert.

Dies ist besonders wichtig in den Momenten, in denen irgendwo etwas abgestürzt ist und wütend darüber berichtet wird, um kein völlig überlastetes Elastic zu erhalten, und nach einiger Zeit Graylog-Knoten, die aufgrund verstopfter Puffer nicht mehr funktionsfähig sind.

Als es bei uns zu denselben Explosionen in der Produktion kam, erhielten wir außerdem Beschwerden von Programmierern und Testern: In dem Moment, in dem sie diese Protokolle wirklich brauchten, wurden sie ihnen nur sehr langsam zur Verfügung gestellt.

Sie fingen an, es herauszufinden. Einerseits war klar, dass sowohl Suchanfragen als auch Indexierungsanfragen im Wesentlichen auf denselben physischen Maschinen verarbeitet wurden und es auf die eine oder andere Weise zu gewissen Rückgängen kommen würde.

Dies konnte jedoch teilweise dadurch umgangen werden, dass in der sechsten Version von Elasticsearch ein Algorithmus erschien, der es ermöglicht, Abfragen zwischen relevanten Datenknoten zu verteilen, und zwar nicht nach dem Zufalls-Round-Robin-Prinzip (dem Container, der die Indizierung durchführt und die Primärdaten enthält). Der Shard kann sehr ausgelastet sein, es wird keine Möglichkeit geben, schnell zu antworten), sondern diese Anfrage an einen weniger ausgelasteten Container mit einem Replikat-Shard weiterzuleiten, der viel schneller antwortet. Mit anderen Worten, wir sind bei use_adaptive_replica_selection: true angekommen.

Das Lesebild beginnt so auszusehen:

Elasticsearch-Cluster 200 TB+

Der Übergang zu diesem Algorithmus ermöglichte es, die Abfragezeit in den Momenten, in denen wir einen großen Protokollfluss zu schreiben hatten, erheblich zu verkürzen.

Das Hauptproblem war schließlich die problemlose Entfernung des Rechenzentrums.

Was wir vom Cluster unmittelbar nach dem Verlust der Verbindung mit einem DC wollten:

  • Wenn wir im ausgefallenen Rechenzentrum einen aktuellen Master haben, wird dieser erneut ausgewählt und als Rolle auf einen anderen Knoten in einem anderen DC verschoben.
  • Der Master entfernt schnell alle unzugänglichen Knoten aus dem Cluster.
  • Anhand der verbleibenden wird er verstehen: In dem verlorenen Rechenzentrum hatten wir diese und jene primären Shards, er wird in den verbleibenden Rechenzentren schnell ergänzende Replikat-Shards fördern und wir werden die Daten weiterhin indizieren.
  • Infolgedessen wird sich der Schreib- und Lesedurchsatz des Clusters allmählich verschlechtern, aber im Allgemeinen wird alles funktionieren, wenn auch langsam, aber stabil.

Wie sich herausstellte, wollten wir so etwas:

Elasticsearch-Cluster 200 TB+

Und wir haben Folgendes bekommen:

Elasticsearch-Cluster 200 TB+

Wie ist das passiert?

Als das Rechenzentrum zusammenbrach, wurde unser Master zum Flaschenhals.

Warum?

Tatsache ist, dass der Master über einen TaskBatcher verfügt, der für die Verteilung bestimmter Aufgaben und Ereignisse im Cluster verantwortlich ist. Jeder Knotenausgang, jede Heraufstufung eines Shards vom Replikat zum Primärknoten, jede Aufgabe, irgendwo einen Shard zu erstellen – all dies geht zuerst an TaskBatcher, wo es sequentiell und in einem Thread verarbeitet wird.

Zum Zeitpunkt des Rückzugs eines Rechenzentrums stellte sich heraus, dass alle Datenknoten in den verbleibenden Rechenzentren es für ihre Pflicht hielten, den Master zu informieren: „Wir haben diese und jene Shards und diese und jene Datenknoten verloren.“

Gleichzeitig schickten die überlebenden Datenknoten alle diese Informationen an den aktuellen Master und versuchten auf die Bestätigung zu warten, dass er sie akzeptierte. Darauf warteten sie nicht, da der Meister Aufgaben schneller erhielt, als er beantworten konnte. Bei den Knoten kam es zu einer Zeitüberschreitung bei sich wiederholenden Anfragen, und der Master versuchte zu diesem Zeitpunkt nicht einmal, sie zu beantworten, sondern war völlig in die Aufgabe vertieft, die Anfragen nach Priorität zu sortieren.

In Terminalform stellte sich heraus, dass die Datenknoten den Master so sehr überhäuften, dass dieser auf vollständigen GC umstieg. Danach wechselte unsere Master-Rolle zu einem nächsten Knoten, es passierte genau das Gleiche und infolgedessen brach der Cluster vollständig zusammen.

Wir haben Messungen durchgeführt und vor der Version 6.4.0, in der dies behoben wurde, reichte es aus, nur 10 von 360 Datenknoten gleichzeitig auszugeben, um den Cluster vollständig herunterzufahren.

Es sah ungefähr so ​​aus:

Elasticsearch-Cluster 200 TB+

Nach Version 6.4.0, in der dieser schreckliche Fehler behoben wurde, beendeten Datenknoten den Master nicht mehr. Aber das machte ihn nicht „klüger“. Nämlich: Wenn wir 2, 3 oder 10 (eine beliebige Anzahl außer eins) Datenknoten ausgeben, erhält der Master eine erste Nachricht, die besagt, dass Knoten A gegangen ist, und versucht, Knoten B, Knoten C und Knoten D darüber zu informieren.

Und im Moment kann man dem nur begegnen, indem man für Versuche, jemandem etwas mitzuteilen, eine Zeitüberschreitung von etwa 20 bis 30 Sekunden festlegt und so die Geschwindigkeit steuert, mit der das Rechenzentrum den Cluster verlässt.

Im Prinzip passt dies zu den Anforderungen, die zunächst im Rahmen des Projekts an das Endprodukt gestellt wurden, aus Sicht der „reinen Wissenschaft“ ist dies jedoch ein Fehler. Was übrigens von den Entwicklern in Version 7.2 erfolgreich behoben wurde.

Darüber hinaus stellte sich heraus, dass es beim Ausfall eines bestimmten Datenknotens wichtiger war, Informationen über seinen Ausgang zu verbreiten, als dem gesamten Cluster mitzuteilen, dass sich auf ihm bestimmte Primär-Shards befanden (um einen Replikat-Shard in einem anderen Datenknoten zu befördern). Zentrum im primären, und in Informationen könnte auf sie geschrieben werden).

Daher werden die freigegebenen Datenknoten nicht sofort als veraltet markiert, wenn alles bereits zum Erliegen gekommen ist. Dementsprechend müssen wir warten, bis alle Pings an die freigegebenen Datenknoten abgelaufen sind, und erst danach beginnt unser Cluster, uns mitzuteilen, dass wir dort, dort und dort weiterhin Informationen aufzeichnen müssen. Mehr dazu können Sie hier lesen hier.

Daher dauert der Rückzug eines Rechenzentrums heute während der Hauptverkehrszeit etwa 5 Minuten. Für einen so großen und schwerfälligen Koloss ist das ein ziemlich gutes Ergebnis.

Im Ergebnis sind wir zu folgendem Entschluss gekommen:

  • Wir verfügen über 360 Datenknoten mit 700-Gigabyte-Festplatten.
  • 60 Koordinatoren für die Weiterleitung des Datenverkehrs über dieselben Datenknoten.
  • 40 Master, die wir seit Versionen vor 6.4.0 als eine Art Vermächtnis hinterlassen haben – um den Rückzug des Rechenzentrums zu überstehen, waren wir mental darauf vorbereitet, mehrere Maschinen zu verlieren, um auch in diesem Jahr garantiert über ein Quorum an Mastern zu verfügen das Worst-Case-Szenario
  • Alle Versuche, Rollen auf einem Container zu kombinieren, führten früher oder später dazu, dass der Knoten unter Last kaputt ging.
  • Der gesamte Cluster verwendet eine Heap-Größe von 31 Gigabyte: Alle Versuche, die Größe zu reduzieren, führten entweder dazu, dass einige Knoten bei umfangreichen Suchanfragen mit dem führenden Platzhalter abstürzten oder dass Elasticsearch selbst den Leistungsschalter auslöste.
  • Um die Suchleistung sicherzustellen, haben wir außerdem versucht, die Anzahl der Objekte im Cluster so gering wie möglich zu halten, um so wenige Ereignisse wie möglich im Engpass zu verarbeiten, den wir im Master hatten.

Schließlich geht es um die Überwachung

Um sicherzustellen, dass dies alles wie vorgesehen funktioniert, überwachen wir Folgendes:

  • Jeder Datenknoten meldet unserer Cloud, dass er existiert und dass sich auf ihm bestimmte Shards befinden. Wenn wir irgendwo etwas löschen, meldet der Cluster nach 2-3 Sekunden, dass wir in Zentrum A die Knoten 2, 3 und 4 gelöscht haben – das bedeutet, dass wir in anderen Rechenzentren unter keinen Umständen die Knoten löschen können, auf denen sich nur ein Shard befindet links.
  • Da wir die Art des Verhaltens des Meisters kennen, schauen wir uns die Anzahl der anstehenden Aufgaben sehr genau an. Denn selbst eine feststeckende Aufgabe kann, wenn sie nicht rechtzeitig abläuft, theoretisch in einer Notfallsituation der Grund dafür sein, dass beispielsweise die Heraufstufung eines Replikat-Shards im Primärserver nicht funktioniert, weshalb die Indizierung nicht mehr funktioniert.
  • Wir schauen uns auch die Garbage-Collector-Verzögerungen sehr genau an, da wir damit bereits bei der Optimierung große Schwierigkeiten hatten.
  • Ablehnungen nach Thread, um im Voraus zu verstehen, wo der Engpass liegt.
  • Nun, Standardmetriken wie Heap, RAM und I/O.

Beim Aufbau der Überwachung müssen Sie die Funktionen des Thread-Pools in Elasticsearch berücksichtigen. Elasticsearch-Dokumentation Beschreibt Konfigurationsoptionen und Standardwerte für Suche und Indizierung, schweigt jedoch völlig über thread_pool.management. Diese Threads verarbeiten insbesondere Abfragen wie _cat/shards und andere ähnliche, die beim Schreiben von Überwachungen praktisch zu verwenden sind. Je größer der Cluster, desto mehr solcher Anfragen werden pro Zeiteinheit ausgeführt, und das oben erwähnte thread_pool.management wird nicht nur nicht in der offiziellen Dokumentation vorgestellt, sondern ist auch standardmäßig auf 5 Threads beschränkt, was danach sehr schnell verworfen wird welche Überwachung nicht mehr richtig funktioniert.

Was ich abschließend sagen möchte: Wir haben es geschafft! Wir konnten unseren Programmierern und Entwicklern ein Werkzeug an die Hand geben, das in nahezu jeder Situation schnell und zuverlässig Auskunft über das Geschehen in der Produktion geben kann.

Ja, es stellte sich als ziemlich kompliziert heraus, aber wir haben es dennoch geschafft, unsere Wünsche in bestehende Produkte zu integrieren, die wir nicht selbst patchen und neu schreiben mussten.

Elasticsearch-Cluster 200 TB+

Source: habr.com

Kommentar hinzufügen