Logs sind ein entscheidender Bestandteil des Systems, der ermöglicht zu verstehen, ob es wie erwartet funktioniert oder nicht. In einer Microservices-Architektur wird die Arbeit mit Logs zu einer eigenen Disziplin in einem speziellen Wettbewerb. Hier müssen viele Fragen gleichzeitig geklärt werden:
- Wie man Logs aus der Anwendung schreibt;
- Wohin man Logs schreibt;
- Wie man Logs zur Speicherung und Verarbeitung transportiert;
- Wie man Logs verarbeitet und speichert.
Der Einsatz der heutzutage beliebten Containerisierungstechnologien fügt noch mehr Komplexität auf dem Weg zur Lösung hinzu.
Genau darüber spricht der Vortrag von Yuri Bushmelev: „Karte der Stolpersteine beim Sammeln und Liefern von Logs“

Wer interessiert ist, ist herzlich eingeladen, weiterzulesen.
Ich heiße Yuri Bushmelev. Ich arbeite bei Lazada. Heute werde ich darüber berichten, wie wir unsere Logs erstellt haben, wie wir sie gesammelt haben und was wir darin festhalten.

Woher kommen wir? Wer sind wir? Lazada ist der führende Online-Shop in sechs Ländern Südostasiens. Diese Länder sind auf unsere Rechenzentren verteilt. Insgesamt gibt es derzeit 4 Rechenzentren. Warum ist das wichtig? Weil einige Entscheidungen darauf basieren, dass es zwischen den Zentren eine sehr schwache Verbindung gibt. Wir verwenden eine Mikrodienstarchitektur. Ich war überrascht zu entdecken, dass wir bereits 80 Mikrodienste haben. Als ich mit der Protokollierung begann, gab es nur 20. Darüber hinaus gibt es einen ziemlich großen Teil von PHP Legacy, mit dem wir ebenfalls leben müssen. All dies generiert derzeit mehr als 6 Millionen Nachrichten pro Minute im gesamten System. Im Folgenden werde ich zeigen, wie wir versuchen, damit umzugehen, und warum dies so ist.

Mit diesen 6 Millionen Nachrichten müssen wir irgendwie umgehen. Was sollen wir mit ihnen machen? 6 Millionen Nachrichten, die:
- aus der Anwendung gesendet werden müssen
- für die Lieferung empfangen werden müssen
- für die Analyse und Speicherung geliefert werden müssen.
- analysiert werden müssen
- irgendwie gespeichert werden müssen.

Als wir drei Millionen Nachrichten erreicht hatten, sah ich ungefähr so aus. Denn wir haben mit wenigen Cent begonnen. Klar, dass dort Anwendungsprotokolle geschrieben werden. Zum Beispiel, konnte keine Verbindung zur Datenbank hergestellt werden, die Verbindung zur Datenbank wurde erfolgreich hergestellt, aber etwas konnte nicht gelesen werden. Aber zusätzlich dazu schreibt jeder unserer Mikrodienste auch ein Zugriffsprotokoll. Jede Anfrage, die an den Mikrodienst gesendet wird, wird im Protokoll festgehalten. Warum machen wir das? Die Entwickler möchten die Möglichkeit haben, eine Trace-Analyse durchzuführen. In jedem Zugriffsprotokoll gibt es ein Feld traceid, mit dem eine spezielle Schnittstelle die gesamte Kette aufbereitet und schön den Trace präsentiert. Der Trace zeigt, wie die Anfrage verlaufen ist, und hilft unseren Entwicklern, schneller mit unidentifizierten Problemen umzugehen.

Wie damit umgehen? Jetzt möchte ich kurz einige Optionen vorstellen — wie diese Problematik generell gelöst wird. Wie man die Aufgabe des Sammelns, Übertragens und Speicherns von Protokollen angeht.

Wie schreibt man aus einer Anwendung heraus? Es gibt verschiedene Möglichkeiten. Insbesondere gibt es Best Practices, wie uns trendige Kollegen erzählen. Es gibt alte Schule in zwei Varianten, wie es die Großväter beigebracht haben. Und es gibt andere Methoden.

Die Situation bei der Protokollierung ist ähnlich. Es gibt nicht viele Lösungen für diesen speziellen Teil. Es sind zwar schon einige vorhanden, aber es könnten noch mehr sein.

Bei der Lieferung und der anschließenden Analyse hingegen explodiert die Anzahl der Variationen förmlich. Ich werde jetzt nicht jede Option beschreiben. Ich glaube, die gängigsten Optionen sind jedem, der sich für das Thema interessiert, bekannt.

Ich zeige Ihnen, wie wir das bei Lazada gemacht haben und wie alles ursprünglich begann.

Vor einem Jahr kam ich zu Lazada, und ich wurde für ein Projekt zur Protokollierung eingesetzt. Es lief ungefähr so ab: Das Protokoll der Anwendung wurde sowohl in stdout als auch in stderr geschrieben. Alles war modern gestaltet. Aber die Entwickler haben es dann aus den Standard-Streams entfernt, und ab da sollten die Infrastruktur-Spezialisten das irgendwie klären. Zwischen den Infrastruktur-Spezialisten und den Entwicklern gibt es auch Release-Manager, die gesagt haben: „Äh… na gut, lassen Sie uns das einfach per Shell in eine Datei packen, und das war's“. Da alles in einem Container war, wurde es direkt im Container verpackt, das Verzeichnis wurde gemappt und dort abgelegt. Ich denke, dass allen klar ist, was daraus geworden ist.

Schauen wir uns einmal die vorhergehenden Schritte an. Wie wir diese Logs transportiert haben. Jemand hat sich für td-agent entschieden, der tatsächlich fluentd ist, aber nicht genau das gleiche wie fluentd. Ich habe das Verhältnis dieser beiden Projekte nie ganz verstanden, aber sie scheinen sich um dasselbe Thema zu drehen. Dieser fluentd, der in Ruby geschrieben ist, las die Logdateien und parste sie mithilfe bestimmter regulärer Ausdrücke in JSON. Danach wurden sie an Kafka gesendet. Übrigens hatten wir in Kafka für jede API vier separate Themen. Warum vier? Weil es live gibt, staging, und weil stdout und stderr vorhanden sind. Die Entwickler erstellen diese, und die Infrastrukturteams müssen sie in Kafka anlegen. Allerdings wurde Kafka von einer anderen Abteilung verwaltet. Daher musste ein Ticket erstellt werden, damit sie vier Themen für jede API anlegen konnten. Das wurde oft vergessen. Insgesamt war das ein riesiges Chaos.

Was haben wir danach damit gemacht? Wir haben es in Kafka geschickt. Dann gingen die Logger die Hälfte in Logstash. Die andere Hälfte der Logs wurde aufgeteilt. Ein Teil ging in einen Graylog, ein anderer Teil in einen anderen Graylog. Am Ende landete alles in einem Elasticsearch-Cluster. Das bedeutet, dass all dieses Durcheinander letztendlich dort landete. So sollte man das nicht machen!

So sieht es aus, wenn man es aus der Ferne von oben betrachtet. Das sollte man so nicht machen! Hier sind sofort die problematischen Stellen mit Zahlen markiert. Tatsächlich gibt es mehr, aber 6 sind ganz besonders problematisch, bei denen man etwas unternehmen muss. Darüber werde ich gleich separat berichten.

Hier (1, 2, 3) werden die Dateien geschrieben, und entsprechend gibt es hier gleich drei Probleme.
Erstens (1) müssen wir sie irgendwo schreiben. Es wäre nicht immer wünschenswert, dem API die Möglichkeit zu geben, direkt in eine Datei zu schreiben. Es wäre ideal, wenn das API in einem Container isoliert ist, und noch besser – wenn es nur lesbar ist. Ich bin Sysadmin, deshalb habe ich eine etwas alternative Sichtweise auf diese Dinge.
Ein zweiter Punkt (2,3) – wir erhalten viele Anfragen über die API. Die API schreibt viele Daten in eine Datei. Die Dateien wachsen. Wir müssen sie rotieren. Denn sonst kommen wir mit dem Speicherplatz nicht mehr klar. Die Rotation ist schwierig, weil sie über einen Shell-Redirect in ein Verzeichnis erfolgt. Wir können das nicht einfach rotieren. Man kann der Anwendung nicht sagen, sie soll die Deskriptoren neu öffnen. Denn die Entwickler schauen einen an, als wäre man verrückt: „Welche Deskriptoren? Wir schreiben doch direkt in stdout.“ Die Infrastruktur-Engineers haben in logrotate einen copytruncate-Befehl implementiert, der einfach eine Kopie der Datei macht und das Original kürzt. In der Regel geht der Speicherplatz genau zwischen diesen Kopierprozessen aus.
(4) Wir hatten verschiedene Formate in den verschiedenen APIs. Diese unterschieden sich ein wenig, aber es mussten unterschiedliche reguläre Ausdrücke geschrieben werden. Da alles mit Puppet gesteuert wurde, gab es ein großes Bündel von Klassen mit ihren eigenen Problemen. Zudem konnte td-agent die meiste Zeit viel Speicher verbrauchen, träge sein und einfach so tun, als ob er arbeiten würde, ohne tatsächlich etwas zu tun. Von außen war es unmöglich zu erkennen, dass er nichts tat. Im besten Fall stürzte er ab und wurde später wieder hochgefahren. Genauer gesagt, es kam ein Alert, und jemand ging manuell wieder himmelhoch.

(6) Die größte Herausforderung war Elasticsearch. Das lag daran, dass wir eine alte Version verwendeten und zu diesem Zeitpunkt keine dedizierten Master-Server hatten. Wir hatten heterogene Logs, deren Felder sich überschnitten. Verschiedene Logs unterschiedlicher Anwendungen konnten mit denselben Feldnamen erstellt werden, jedoch unterschiedliche Daten enthalten. Ein Log könnte beispielsweise einen Integer im Feld 'level' geliefert haben, während ein anderes Log einen String im selben Feld lieferte. Bei fehlendem statischen Mapping ergibt sich eine interessante Situation: Wenn nach der Indexrotation in Elasticsearch zuerst eine Nachricht mit einem String ankommt, läuft alles gut. Kommt jedoch zuerst eine Nachricht mit einem Integer, werden alle nachfolgenden Nachrichten mit einem String einfach verworfen, weil die Feldtypen nicht übereinstimmen.

Wir begannen, uns diese Fragen zu stellen. Wir beschlossen, keine Schuldigen zu suchen.

Es gibt einiges zu tun! Eine offensichtliche Sache ist, dass wir Standards etablieren müssen. Einige Standards hatten wir bereits, andere haben wir etwas später eingeführt. Glücklicherweise wurde zu diesem Zeitpunkt bereits ein einheitliches Log-Format für alle APIs genehmigt. Es ist direkt in den Interaktionsstandards der Dienste definiert. Folglich müssen diejenigen, die Protokolle erhalten möchten, diese in diesem Format schreiben. Wer seine Protokolle nicht in diesem Format schreibt, erhält von uns keine Garantien.
Darüber hinaus sollten wir einen einheitlichen Standard für die Art und Weise der Aufzeichnung, Lieferung und Sammlung von Protokollen festlegen. Konkret geht es darum, wo sie geschrieben werden und wie sie geliefert werden. Die ideale Situation wäre, wenn in den Projekten dieselbe Bibliothek verwendet wird. Es gibt eine spezielle Logging-Bibliothek für Go und eine separate Bibliothek für PHP. Alle, die bei uns tätig sind, müssen diese nutzen. Momentan würde ich sagen, dass wir das zu etwa 80 % erreichen. Aber einige halten weiterhin an ihren alten Gewohnheiten fest.
Auf der Folie beginnt es gerade, das „SLA für die Log-Zustellung“ sichtbar zu werden. Es existiert noch nicht, aber wir arbeiten daran. Denn es ist äußerst praktisch, wenn die Infrastruktur sagt, dass wir, wenn Sie in einem bestimmten Format an einen bestimmten Ort schreiben und nicht mehr als N Nachrichten pro Sekunde senden, dies mit einer bestimmten Wahrscheinlichkeit zustellen werden. Das nimmt einem eine Menge Kopfzerbrechen. Wenn ein SLA vorhanden ist, ist das einfach großartig!

Wie haben wir das Problem angegangen? Die größte Hürde war der td-agent. Es war unklar, wohin unsere Logs verschwinden. Werden sie zugestellt? Werden sie gesammelt? Wo sind sie überhaupt? Daher wurde als erster Schritt beschlossen, den td-agent zu ersetzen. Kurz habe ich hier einige Alternativen skizziert.
Fluentd. Erstens habe ich damit in meinem vorherigen Job gearbeitet, und auch dort ist es manchmal ausgefallen. Zweitens ist es im Grunde dasselbe, nur spezialisierter.
Filebeat. Warum war es für uns praktisch? Weil es in Go geschrieben ist, und wir haben viel Expertise in Go. Das heißt, wenn nötig, könnten wir es irgendwie anpassen. Deshalb haben wir es nicht gewählt, um keinen Anreiz zu haben, es für uns umzuschreiben.
Für Sysadmins bleibt die evidente Lösung eine Vielzahl von Sys-Logs, wie syslog-ng, rsyslog oder nxlog.
Oder man könnte etwas Eigenes schreiben, aber das haben wir verworfen, ebenso wie filebeat. Wenn man etwas schreibt, sollte es besser nützlich für das Geschäft sein. Für die Logzustellung ist es besser, eine fertige Lösung zu wählen.
Daher schloss sich die Wahl im Grunde auf syslog-ng oder rsyslog. Ich habe mich für rsyslog entschieden, einfach weil wir in Puppet bereits Klassen für rsyslog hatten, und ich keinen offensichtlichen Unterschied zwischen ihnen gefunden habe. Da syslog hier und da, ja, die Dokumentation ist bei manchen schlechter, bei manchen besser. Der eine kann so, der andere anders.

Ein paar Worte zu rsyslog. Zunächst einmal ist es großartig, weil es viele Module bietet. Es verwendet die benutzerfreundliche RainerScript (eine moderne Konfigurationssprache). Ein genialer Vorteil ist, dass wir veraltetes Verhalten von td-agent mit den Standardmitteln simulieren konnten, sodass sich für die Anwendungen nichts geändert hat. Wir ersetzen also td-agent durch rsyslog und lassen alles andere vorerst unberührt. Damit erhalten wir sofort eine funktionierende Lieferung. Darüber hinaus ist mmnormalize ein hervorragendes Werkzeug in rsyslog. Es ermöglicht das Parsen von Protokollen, jedoch nicht mit Grok oder regexp. Stattdessen erstellt es einen abstrakten Syntaxbaum. Es interpretiert Protokolle wie ein Compiler Quellcode interpretiert. Das ermöglicht eine sehr schnelle Verarbeitung, verbraucht wenig CPU und ist insgesamt wirklich eine tolle Sache. Es gibt viele andere Vorteile. Ich werde nicht näher darauf eingehen.

rsyslog hat allerdings auch eine Reihe von Nachteilen. Diese sind ähnlich wie die Vorteile. Die Hauptprobleme sind, dass man wissen muss, wie man es konfiguriert, und die passende Version auswählen muss.

Wir haben entschieden, dass wir die Protokolle über einen Unix-Socket schreiben werden. Und zwar nicht in /dev/log, weil dort ein Durcheinander aus Systemprotokollen herrscht, durch journald in diesem Pipeline. Daher werden wir in einen benutzerdefinierten Socket schreiben. Wir werden ihn an ein separates Regelset anhängen. So stören wir uns nicht gegenseitig. Alles wird transparent und nachvollziehbar sein. Genau so haben wir es gemacht. Das Verzeichnis mit diesen Sockets ist standardisiert und wird in alle Container weitergeleitet. Die Container können den benötigten Socket sehen, öffnen und in ihn schreiben.
Warum nicht in eine Datei? Weil alle gelesen haben , der zu versuchen schrieb, eine Datei in Docker zu leiten, und dabei festgestellt wurde, dass sich nach einem Neustart von rsyslog der Dateideskriptor ändert und Docker diese Datei verliert. Es hält etwas anderes offen, aber nicht mehr den Socket, in den geschrieben wird. Wir haben entschieden, dieses Problem zu umgehen und gleichzeitig das Problem der Sperrung zu umgehen.

Rsyslog führt die im Slide angegebenen Aktionen durch und sendet die Protokolle entweder an Relay oder an Kafka. Kafka entspricht der alten Methode. Relay — ich habe versucht, reines rsyslog für die Protokollübertragung zu verwenden. Ohne Message Queue, mit den Standardmitteln von rsyslog. Im Prinzip funktioniert das.

Es gibt jedoch Feinheiten, wie man diese dann in diesen Teil (Logstash/Graylog/ES) integriert. Dieser Teil (rsyslog-rsyslog) wird zwischen den Rechenzentren verwendet. Hier wird eine komprimierte TCP-Verbindung eingesetzt, die hilft, Bandbreite zu sparen und somit die Wahrscheinlichkeit erhöht, dass wir Protokolle aus einem anderen Rechenzentrum erhalten, wenn die Verbindung überlastet ist. Denn in Indonesien gibt es ernsthafte Probleme. Dort ist dies ein ständiges Thema.

Wir haben darüber nachgedacht, wie wir eigentlich überwachen können, wie wahrscheinlich es ist, dass die Protokolle, die wir aus der Anwendung aufgezeichnet haben, den anderen Endpunkt erreichen. Daher haben wir uns entschieden, Metriken zu erstellen. rsyslog hat ein eigenes Statistik-Modul, das verschiedene Zähler enthält. Zum Beispiel kann es Ihnen die Größe der Warteschlange anzeigen oder wie viele Nachrichten in einem bestimmten Aktionsbereich eingegangen sind. Daraus lassen sich bereits Erkenntnisse gewinnen. Außerdem gibt es benutzerdefinierte Zähler, die Sie anpassen können, um beispielsweise die Anzahl der Nachrichten anzuzeigen, die von einer bestimmten API aufgezeichnet wurden. Darüber hinaus habe ich den rsyslog_exporter in Python geschrieben und wir haben alles an Prometheus gesendet und Grafiken erstellt. Die Metriken von Graylog waren sehr gewünscht, aber wir hatten bisher nicht die Gelegenheit, sie einzurichten.

Was war das Problem? Wir haben festgestellt, dass unsere Live-APIs unerwartet 50.000 Nachrichten pro Sekunde senden. Das betrifft nur die Live-APIs, nicht die Staging-Umgebung. Graylog zeigt uns jedoch nur 12.000 Nachrichten pro Sekunde an. Daher stellte sich die berechtigte Frage, wo die restlichen Nachrichten geblieben sind. Daraus folgerten wir, dass Graylog einfach überlastet ist. Nach Prüfung bestätigte sich, dass Graylog mit Elasticsearch diese Datenmenge nicht bewältigen kann.
Darüber hinaus gab es weitere Entdeckungen, die wir im Verlauf gemacht haben.
Die Aufzeichnung in den Socket wurde blockiert. Wie kam es dazu? Als ich rsyslog zur Zustellung verwendet habe, brach plötzlich die Verbindung zwischen den Rechenzentren zusammen. Die Lieferung stoppte an einem Ort und auch an einem anderen. Dies führte zu einer Überlastung bei der Maschine mit der API, die in den rsyslog-Socket schreibt. Dort füllte sich die Warteschlange. Anschließend füllte sich die Warteschlange für das Schreiben in den Unix-Socket, die standardmäßig auf 128 Pakete begrenzt ist. Der nächste write() im Anwendungscode wurde blockiert. Als wir uns die Bibliothek ansehen, die wir in unseren Go-Anwendungen verwenden, stand dort, dass das Schreiben in den Socket im nicht blockierenden Modus erfolgt. Wir waren überzeugt, dass nichts blockiert. , die darüber geschrieben hat. Es gibt jedoch einen Punkt. Rund um diesen Aufruf gab es eine endlose Schleife, in der ständig versucht wurde, eine Nachricht in den Socket zu stecken. Das haben wir übersehen. Die Bibliothek musste neu geschrieben werden. Seitdem hat sie sich mehrmals geändert, aber jetzt haben wir alle Blockierungen in den Untereinheiten beseitigt. Daher können wir rsyslog stoppen, ohne dass etwas ausfällt.
Wir müssen die Größe der Warteschlangen überwachen, um nicht auf dieselben Probleme zu stoßen. Erstens können wir überwachen, wann wir anfangen, Nachrichten zu verlieren. Zweitens können wir erkennen, dass wir grundsätzlich Probleme mit der Zustellung haben.
Und noch ein unangenehmer Punkt – die Amplikation um das Zehnfache in einer Microservices-Architektur ist sehr einfach. Wir haben nicht so viele eingehende Anfragen, aber aufgrund des Graphen, in dem diese Nachrichten laufen, und der Access-Logs erhöhen wir tatsächlich die Last der Logs ungefähr um das Zehnfache. Leider hatte ich nicht die Zeit, die genauen Zahlen zu berechnen, aber Microservices sind eben so. Das sollte man im Hinterkopf behalten. Im Moment ist das Logsammelsystem die am stärksten belastete Komponente bei Lazada.

Wie löst man das Problem mit Elasticsearch? Wenn Sie schnell alle Protokolle an einem Ort abrufen möchten, um nicht auf allen Maschinen suchen zu müssen, verwenden Sie ein Dateispeicher. Das funktioniert garantiert. Es kann auf jedem Server eingerichtet werden. Man muss nur ein paar Festplatten anschließen und syslog installieren. Danach sind alle Protokolle garantiert an einem Ort vorhanden. Dann können Sie in Ruhe Elasticsearch, Graylog oder etwas anderes konfigurieren. Aber Sie haben bereits alle Protokolle und können sie so lange speichern, wie der Speicherplatz reicht.

Zum Zeitpunkt meines Vortrags sah das Schema so aus. Wir haben nahezu aufgehört, in die Datei zu schreiben. Höchstwahrscheinlich schalten wir die Reste jetzt ab. Auf den lokalen Maschinen, auf denen die API läuft, werden wir auch nicht mehr in Dateien schreiben. Erstens gibt es ein Dateispeicher, das sehr gut funktioniert. Zweitens ist der Platz auf diesen Maschinen ständig knapp, und man muss ihn fortlaufend überwachen.
Dieser Teil mit Logstash und Graylog ist wirklich problematisch. Deshalb müssen wir uns davon trennen. Wir müssen uns für etwas entscheiden.

Wir haben uns entschieden, Logstash und Kibana abzulehnen. Der Grund ist, dass wir eine Sicherheitsabteilung haben. Was hat das miteinander zu tun? Nun, Kibana erlaubt ohne X-Pack und ohne Shield keine differenzierten Zugriffsrechte auf die Logs. Daher haben wir Graylog verwendet. Es ist nicht mein Favorit, aber es funktioniert. Wir haben neue Hardware gekauft, haben frisches Graylog installiert und alle Logs mit strikten Formaten in eine separate Graylog-Instanz verschoben. Dadurch haben wir das Problem mit den verschiedenen Typen identischer Felder organisatorisch gelöst.

Was genau ist in das neue Graylog integriert? Wir haben einfach alles in Docker aufgenommen. Wir haben einige Server genommen, drei Instanzen von Kafka ausgerollt und 7 Server mit Graylog in Version 2.3 (weil wir Elasticsearch in Version 5 wollten) aufgebaut. Das Ganze haben wir auf RAID-Systemen mit HDDs laufen lassen. Wir haben eine Indexierungsrate von bis zu 100.000 Nachrichten pro Sekunde erreicht und gesehen, dass wir 140 Terabyte Daten pro Woche verarbeiten.

Und wieder die gleichen Probleme! Uns stehen zwei Verkaufsaktionen bevor. Wir haben über 6 Millionen Nachrichten umgezogen. Graylog schafft es nicht, damit hinterherzukommen. Wir müssen uns irgendwie wieder durchschlagen.

So haben wir überlebt. Wir haben noch ein paar Server und SSDs hinzugefügt. Momentan leben wir auf diese Weise. Jetzt verarbeiten wir bereits 160.000 Nachrichten pro Sekunde. Wir haben noch nicht die Grenze erreicht, daher ist es bisher unklar, wie viel wir tatsächlich aus diesem System herausholen können.

Das sind unsere Pläne für die Zukunft. Davon ist wahrscheinlich High Availability am wichtigsten. Das haben wir momentan noch nicht. Einige Maschinen sind gleich konfiguriert, aber der gesamte Traffic läuft bisher über eine Maschine. Wir müssen Zeit investieren, um das Failover zwischen ihnen einzurichten.
Metriken mit Graylog sammeln.
Ein Rate Limit einrichten, damit uns eine überlastete API nicht den Bandbreite und alles andere kaputt macht.
Und schließlich einen SLA mit den Entwicklern vereinbaren, dass wir bis zu einem bestimmten Umfang betreuen können. Wenn ihr mehr schreibt, tut mir leid.
Und die Dokumentation schreiben.

Kurz zusammengefasst, die Ergebnisse von allem, was wir durchgemacht haben. Erstens, Standards. Zweitens, Syslog – ein Kuchen. Drittens, Rsyslog funktioniert genau so, wie es auf der Folie steht. Und lasst uns zu den Fragen übergehen.
Fragen.
Frage: Warum haben Sie sich entschlossen, nichts zu nehmen… (Filebeat?)
Antwort: Wir müssen in eine Datei schreiben. Das wollte ich wirklich nicht. Wenn dein API Tausende von Nachrichten pro Sekunde schreibt, ist es einfach nicht praktikabel, sogar einmal pro Stunde zu rotieren. Man könnte in eine Pipe schreiben. Daraufhin fragten die Entwickler: „Was passiert, wenn der Prozess, in den wir schreiben, abstürzt?“ Ich konnte nichts darauf antworten und sagte: „Na gut, dann machen wir das nicht.“
Frage: Warum schreiben Sie die Logs nicht einfach in HDFS?
Antwort: Das ist der nächste Schritt. Wir haben am Anfang darüber nachgedacht, aber da wir momentan keine Ressourcen dafür haben, bleibt es eine langfristige Lösung.
Frage: Ein Spaltenformat wäre passender.
Antwort: Ich verstehe alles. Wir sind beide dafür.
Frage: Sie schreiben in rsyslog. Dort kann man sowohl TCP als auch UDP verwenden. Aber wenn Sie UDP verwenden, wie gewährleisten Sie dann die Zustellung?
Antwort: Es gibt zwei Punkte. Erstens sage ich sofort allen, dass wir die Lieferung von Logs nicht garantieren. Denn wenn Entwickler kommen und sagen: „Lass uns da Finanzdaten reinschreiben, und ihr legt die irgendwo ab, für den Fall, dass etwas passiert“, antworten wir: „Super! Fangt an, euch beim Schreiben in den Socket zu blockieren und macht das in Transaktionen, damit ihr uns das garantiert in den Socket schreibt und sicherstellt, dass wir es auf der anderen Seite erhalten haben.“ In diesem Moment ist es allen sofort nicht mehr wichtig. Wenn es nicht wichtig ist, welche Fragen haben wir dann? Wenn ihr keine Garantie für das Schreiben in den Socket geben wollt, warum sollten wir dann die Lieferung garantieren? Wir geben unser Bestes. Wir bemühen uns wirklich, so viel wie möglich und so gut wie möglich zu liefern, aber wir geben keine 100% Garantie. Daher sollte man keine Finanzdaten dort schreiben. Dafür sind Datenbanken mit Transaktionen da.
Frage: Wenn die API eine Nachricht im Log generiert und die Kontrolle an Microservices übergibt, habt ihr dann nicht das Problem, dass Nachrichten von verschiedenen Microservices in der falschen Reihenfolge ankommen? Dadurch entsteht Verwirrung.
Antwort: Es ist normal, dass sie in unterschiedlicher Reihenfolge ankommen. Darauf sollte man vorbereitet sein. Denn bei jeder Netzwerk-Zustellung ist die Reihenfolge nicht garantiert, oder man muss speziell Ressourcen dafür aufwenden. Wenn wir uns Dateispeicher betrachten, speichert jede API ihre Logs in ihren eigenen Dateien. Genauer gesagt, rsyslog ordnet diese in Verzeichnissen an. Jede API hat ihre eigenen Logs, in die man gehen und nachsehen kann, und dann können sie anhand des Zeitstempels in diesen Logs abgeglichen werden. Wenn sie in Graylog schauen, werden sie dort nach Zeitstempel sortiert. Dort ist alles in Ordnung.
Frage: Der Zeitstempel kann sich um Millisekunden unterscheiden.
Antwort: Der Zeitstempel wird von der API selbst generiert. Darin liegt eigentlich der ganze Trick. Wir haben NTP. Die API generiert den Zeitstempel bereits in der Nachricht selbst. Dieser wird nicht von rsyslog hinzugefügt.
Frage: Die Interaktion zwischen den Rechenzentren ist nicht ganz klar. Innerhalb eines Rechenzentrums ist es klar, wie die Logs gesammelt und verarbeitet werden. Wie erfolgt die Interaktion zwischen den Rechenzentren? Oder lebt jedes Rechenzentrum sein eigenes Leben?
Antwort: Fast. In jedem Land befindet sich unsere Infrastruktur in einem bestimmten Rechenzentrum. Derzeit gibt es keine Verteilung, sodass ein Land in mehreren Rechenzentren gehostet wird. Daher ist eine Zusammenführung nicht notwendig. Innerhalb jedes Rechenzentrums gibt es einen Log Relay. Dies ist ein Rsyslog-Server. Tatsächlich betreiben wir zwei Management-Server, die identisch eingerichtet sind. Aktuell läuft der gesamte Verkehr über einen von ihnen, der alle Protokolle aggregiert. Dieser Server hat eine Festplattenwarteschlange für den Notfall. Er komprimiert die Logs und sendet sie an unser zentrales Rechenzentrum in Singapur, wo sie dann an Graylog weitergeleitet werden. Außerdem gibt es in jedem Rechenzentrum einen eigenen Dateispeicher. Sollte die Verbindung mal ausfallen, haben wir dort alle Logs. Sie werden dort verbleiben und bleiben erhalten.
Frage: Erhalten Sie die Logs in Notfallsituationen von dort?
Antwort: Kann man dorthin (zum Dateispeicher) gehen und nachsehen?
Frage: Wie überwachen Sie, dass keine Logs verloren gehen?
Antwort: Wir verlieren sie tatsächlich, und wir überwachen das. Die Überwachung wurde vor einem Monat gestartet. In der Bibliothek, die die Go API nutzt, gibt es Metriken. Sie kann zählen, wie oft sie nicht in den Socket schreiben konnte. Aktuell gibt es eine raffinierte Heuristik. Es gibt einen Puffer. Er versucht, Nachrichten aus diesem zu schreiben. Wenn der Puffer überläuft, beginnt er, diese zu verwerfen. Und zählt, wie viele er verworfen hat. Wenn die Zähler beginnen, überzulaufen, erfahren wir davon. Sie kommen jetzt auch nach Prometheus, und in Grafana kann man die Diagramme ansehen. Man kann Benachrichtigungen einrichten. Aber momentan ist unklar, an wen sie gesendet werden sollen.
Frage: Speichern Sie Protokolle in Elasticsearch mit einer Sicherung. Wie viele Replikate haben Sie?
Antwort: Eine Replikat.
Frage: Ist das nur ein Replikat?
Antwort: Das ist Master und Replikat. Die Daten werden in zwei Exemplaren gespeichert.
Frage: Haben Sie die Größe des rsyslog-Puffers irgendwie angepasst?
Antwort: Wir schreiben Datagramme in einen benutzerdefinierten Unix-Socket. Das setzt uns sofort eine Grenze von 128 Kilobyte. Mehr können wir nicht darin speichern. Das haben wir in den Standard aufgenommen. Wer Zugriff auf die Speicherplatz haben möchte, muss 128 Kilobyte beachten. Die Bibliotheken kürzen die Nachrichten und setzen ein Flag, das angibt, dass die Nachricht gekürzt wurde. In unserem Standard für die Nachricht gibt es ein spezielles Feld, das zeigt, ob sie beim Schreiben gekürzt wurde oder nicht. So können wir diesen Aspekt ebenfalls nachvollziehen.
Frage: Schreiben Sie kaputte JSON-Daten?
Antwort: Kaputte JSON-Daten werden entweder während des Relays verworfen, weil das Paket zu groß ist, oder von Graylog zurückgewiesen, weil es die JSON-Struktur nicht parsen kann. Aber es gibt einige Details, die behoben werden müssen, und diese hängen größtenteils mit rsyslog zusammen. Ich habe bereits einige Issues dort eingetragen, an denen noch gearbeitet werden muss.
Frage: Warum Kafka? Haben Sie es mit RabbitMQ versucht? Funktioniert Graylog bei solch hohen Lasten nicht?
Antwort: Graylog stellt sich als nicht ideal für uns heraus. Tatsächlich sind wir mit Graylog nicht zufrieden. Es gibt viele Probleme damit. Es ist eine eigenwillige Lösung. Und ehrlich gesagt, brauchen wir es nicht. Ich würde es vorziehen, direkt von rsyslog in Elasticsearch zu schreiben und dann Kibana zu nutzen. Doch wir müssen zuerst die Sicherheitsfragen klären. Das könnte eine mögliche Entwicklungsrichtung für uns sein, wenn wir Graylog abschaffen und Kibana verwenden. Logstash wäre nicht sinnvoll, denn alles, was ich brauche, kann ich auch mit rsyslog erledigen. Es hat sogar ein Modul zum Schreiben in Elasticsearch. Mit Graylog versuchen wir, irgendwie zurechtzukommen. Wir haben es sogar ein wenig optimiert. Aber es gibt noch Raum für Verbesserungen.
Über Kafka. Das hat sich historisch so ergeben. Als ich kam, war sie bereits vorhanden und es wurden schon Logs geschrieben. Wir haben einfach unseren Cluster hochgefahren und die Logs dorthin verschoben. Wir verwalten ihn und wissen, wie es ihm geht. Zur RabbitMQ... wir haben keine gute Erfahrung mit RabbitMQ. Aber unsere RabbitMQ-Instanz läuft gut. Wir haben sie in der Produktion und hatten einige Probleme damit. Kurz vor der Verkaufsaktion wurde sie optimiert und funktioniert jetzt gut. Aber davor war ich nicht bereit, sie in die Produktion zu bringen. Es gibt noch einen weiteren Punkt. Graylog kann die Version AMQP 0.9 lesen, während rsyslog die Version AMQP 1.0 schreiben kann. Es gibt keine Lösung, die beides kann. Entweder das eine oder das andere. Daher nutzen wir momentan nur Kafka. Aber auch da gibt es eigene Nuancen. Denn omkafka der rsyslog-Version, die wir verwenden, kann den gesamten Nachrichtenpuffer verlieren, den sie von rsyslog abgezogen hat. Momentan nehmen wir das in Kauf.
Frage: Nutzen Sie Kafka, weil es bereits vorhanden war? Für keine anderen Zwecke wird es nicht verwendet?
Antwort: Kafka, die verwendet wird, wird vom Data Science Team genutzt. Es ist ein ganz eigenes Projekt, über das ich leider nichts sagen kann. Ich bin unwissend darüber. Es war in der Verantwortung des Data Science Teams. Als die Logs eingerichtet wurden, haben wir beschlossen, sie zu verwenden, um nicht zusätzlich unsere eigene einzurichten. Jetzt haben wir Graylog aktualisiert und dabei die Kompatibilität verloren, da dort eine ältere Version von Kafka ist. Wir mussten unsere eigene einrichten. So haben wir auch diese vier Topics für jede API losgeworden. Wir haben ein breites Topic für alle Live-Umgebungen erstellt, ein breites Topic für alle Staging-Umgebungen und schieben einfach alles dort hinein. Graylog filtert das alles parallel heraus.
Frage: Warum ist dieses Schamanenwerk mit den Sockets notwendig? Haben Sie versucht, den Log-Treiber Syslog für Container zu verwenden?
Antwort: Zu der Zeit, als wir uns mit diesem Thema befassten, war unsere Beziehung zu Docker angespannt. Es war Docker 1.0 oder 0.9. Docker selbst war irgendwie seltsam. Zweitens, wenn man auch noch Logs hineinschieben will… Ich habe das unbegründete Gefühl, dass er alle Logs durch sich selbst, durch den Docker-Daemon, leitet. Wenn eine API verrückt spielt, stoßen die anderen APIs auf das Problem, dass sie stdout und stderr nicht senden können. Ich weiß nicht, wohin das führt. Ich habe das Gefühl, dass wir in diesem Fall den Syslog-Treiber von Docker nicht verwenden sollten. Unsere Abteilung für funktionale Tests hat ihren eigenen kleinen Graylog-Cluster für Logs. Sie nutzen die Log-Treiber von Docker und scheinbar läuft alles gut. Aber sie schreiben GELF direkt in Graylog. Zu dem Zeitpunkt, als wir damit anfingen, mussten wir sicherstellen, dass es einfach funktioniert. Eventuell, wenn jemand kommt und sagt, dass es schon seit Jahren problemlos läuft, werden wir es ausprobieren.
Frage: Die Lieferung zwischen den Rechenzentren erfolgt über rsyslog. Warum nicht über Kafka?
Antwort: Wir machen es tatsächlich auf beide Arten. Aus zwei Gründen. Wenn der Kanal völlig überlastet ist, passen unsere Protokolle selbst im komprimierten Format nicht mehr hindurch. Und Kafka ermöglicht es, sie während des Prozesses einfach zu verlieren. Auf diese Weise vermeiden wir das Festhängen dieser Protokolle. In diesem Fall nutzen wir Kafka direkt. Wenn wir jedoch einen guten Kanal haben und ihn entlasten möchten, verwenden wir rsyslog. Tatsächlich kann man es so einstellen, dass es automatisch das herausfiltert, was nicht hindurch passt. Momentan verwenden wir an einigen Stellen die direkte Zustellung über rsyslog und an anderen Stellen Kafka.
Quelle: habr.com
