Protokolle in Kubernetes (und nicht nur) heute: Erwartungen und Realität

Protokolle in Kubernetes (und nicht nur) heute: Erwartungen und Realität

Wir schreiben das Jahr 2019 und wir haben immer noch keine Standardlösung für die Protokollaggregation in Kubernetes. In diesem Artikel möchten wir anhand von Beispielen aus der Praxis unsere Recherchen, aufgetretenen Probleme und deren Lösungen vorstellen.

Zunächst möchte ich jedoch einen Vorbehalt anbringen, dass verschiedene Kunden beim Sammeln von Protokollen sehr unterschiedliche Dinge verstehen:

  • jemand möchte Sicherheits- und Audit-Protokolle sehen;
  • jemand – zentralisierte Protokollierung der gesamten Infrastruktur;
  • und für einige reicht es aus, nur Anwendungsprotokolle zu sammeln, beispielsweise ohne Balancer.

Im Folgenden erfahren Sie, wie wir verschiedene „Wunschlisten“ implementiert haben und auf welche Schwierigkeiten wir gestoßen sind.

Theorie: über Protokollierungstools

Hintergrundinformationen zu den Komponenten eines Protokollierungssystems

Die Protokollierung hat einen langen Weg zurückgelegt, weshalb Methoden zum Sammeln und Analysieren von Protokollen entwickelt wurden, die wir heute verwenden. Bereits in den 1950er Jahren führte Fortran ein Analogon zu Standard-Eingabe-/Ausgabeströmen ein, das dem Programmierer beim Debuggen seines Programms half. Dies waren die ersten Computerprotokolle, die den damaligen Programmierern das Leben erleichterten. Heute sehen wir in ihnen die erste Komponente des Protokollierungssystems - Quelle oder „Produzent“ von Protokollen.

Die Informatik stand nicht still: Computernetzwerke entstanden, die ersten Cluster... Komplexe Systeme bestehend aus mehreren Computern begannen zu funktionieren. Jetzt waren Systemadministratoren gezwungen, Protokolle von mehreren Maschinen zu sammeln, und in besonderen Fällen konnten sie Betriebssystem-Kernel-Meldungen hinzufügen, falls sie einen Systemfehler untersuchen mussten. Zur Beschreibung zentralisierter Protokollsammelsysteme wurde es Anfang der 2000er Jahre veröffentlicht RFC 3164, das remote_syslog standardisierte. So entstand eine weitere wichtige Komponente: Protokollsammler und deren Lagerung.

Mit der Zunahme des Protokollvolumens und der weit verbreiteten Einführung von Webtechnologien stellte sich die Frage, welche Protokolle den Benutzern bequem angezeigt werden müssen. Einfache Konsolentools (awk/sed/grep) wurden durch fortgeschrittenere ersetzt Log-Viewer - dritte Komponente.

Durch die Zunahme des Protokollvolumens wurde noch etwas deutlich: Es werden Protokolle benötigt, aber nicht alle. Und unterschiedliche Protokolle erfordern unterschiedliche Konservierungsgrade: Einige können an einem Tag verloren gehen, während andere fünf Jahre lang gelagert werden müssen. Daher wurde dem Protokollierungssystem eine Komponente zum Filtern und Weiterleiten von Datenflüssen hinzugefügt – nennen wir es Filter.

Auch die Speicherung hat einen großen Sprung gemacht: von regulären Dateien über relationale Datenbanken bis hin zur dokumentorientierten Speicherung (z. B. Elasticsearch). Daher wurde der Speicher vom Kollektor getrennt.

Letztendlich hat sich das Konzept eines Protokolls zu einer Art abstrakten Ereignisströmen erweitert, die wir für die Geschichte bewahren wollen. Oder besser gesagt, für den Fall, dass Sie eine Untersuchung durchführen oder einen Analysebericht erstellen müssen ...

Dadurch hat sich die Protokollsammlung in relativ kurzer Zeit zu einem wichtigen Subsystem entwickelt, das zu Recht als einer der Unterabschnitte von Big Data bezeichnet werden kann.

Protokolle in Kubernetes (und nicht nur) heute: Erwartungen und Realität
Während einst gewöhnliche Ausdrucke für ein „Protokollierungssystem“ ausreichten, hat sich die Situation heute stark verändert.

Kubernetes und Protokolle

Als Kubernetes in die Infrastruktur kam, ging auch das bereits bestehende Problem des Sammelns von Protokollen nicht daran vorbei. In mancher Hinsicht wurde es sogar noch schmerzhafter: Die Verwaltung der Infrastrukturplattform wurde nicht nur vereinfacht, sondern gleichzeitig auch komplizierter. Viele alte Dienste haben mit der Migration zu Microservices begonnen. Im Kontext von Protokollen spiegelt sich dies in der wachsenden Zahl von Protokollquellen, ihrem besonderen Lebenszyklus und der Notwendigkeit wider, die Beziehungen aller Systemkomponenten durch Protokolle zu verfolgen ...

Mit Blick auf die Zukunft kann ich sagen, dass es derzeit leider keine standardisierte Protokollierungsoption für Kubernetes gibt, die im Vergleich zu allen anderen vorteilhaft wäre. Die beliebtesten Programme in der Community sind wie folgt:

  • jemand rollt den Stapel aus EFK (Elasticsearch, Fluentd, Kibana);
  • Jemand probiert das kürzlich veröffentlichte aus Loki oder Verwendungen Protokollierungsbetreiber;
  • uns (Und vielleicht nicht nur wir?..) Ich bin mit meiner eigenen Entwicklung weitgehend zufrieden - Blockhaus...

In der Regel nutzen wir in K8s-Clustern (für selbst gehostete Lösungen) folgende Bundles:

Ich werde jedoch nicht auf Anweisungen zu ihrer Installation und Konfiguration eingehen. Stattdessen werde ich mich auf ihre Mängel und allgemeinere Schlussfolgerungen zur Situation mit Protokollen im Allgemeinen konzentrieren.

Üben Sie mit Protokollen in K8s

Protokolle in Kubernetes (und nicht nur) heute: Erwartungen und Realität

„Alltagsprotokolle“, wie viele von euch sind da?

Die zentrale Erfassung von Protokollen aus einer relativ großen Infrastruktur erfordert erhebliche Ressourcen, die für die Erfassung, Speicherung und Verarbeitung von Protokollen aufgewendet werden. Während der Durchführung verschiedener Projekte wurden wir mit unterschiedlichen Anforderungen und daraus resultierenden betrieblichen Problemen konfrontiert.

Versuchen wir es mit ClickHouse

Schauen wir uns einen zentralen Speicher für ein Projekt mit einer Anwendung an, die ziemlich aktiv Protokolle generiert: mehr als 5000 Zeilen pro Sekunde. Beginnen wir mit der Arbeit mit seinen Protokollen und fügen sie ClickHouse hinzu.

Sobald maximale Echtzeit erforderlich ist, ist der 4-Core-Server mit ClickHouse bereits auf dem Festplatten-Subsystem überlastet:

Protokolle in Kubernetes (und nicht nur) heute: Erwartungen und Realität

Diese Art des Ladens ist darauf zurückzuführen, dass wir versuchen, so schnell wie möglich in ClickHouse zu schreiben. Und die Datenbank reagiert darauf mit erhöhter Plattenlast, was zu folgenden Fehlern führen kann:

DB::Exception: Too many parts (300). Merges are processing significantly slower than inserts

Tatsache ist, dass MergeTree-Tabellen in ClickHouse (sie enthalten Protokolldaten) haben ihre eigenen Schwierigkeiten bei Schreibvorgängen. Die darin eingefügten Daten erzeugen eine temporäre Partition, die dann mit der Haupttabelle zusammengeführt wird. Dadurch stellt sich heraus, dass die Aufnahme sehr anspruchsvoll für die Festplatte ist und außerdem der Einschränkung unterliegt, auf die wir oben hingewiesen wurden: Es können nicht mehr als 1 Unterpartitionen in einer Sekunde zusammengeführt werden (tatsächlich sind dies 300 Einfügungen). pro Sekunde).

Um dieses Verhalten zu vermeiden, sollte an ClickHouse schreiben in möglichst großen Stücken und nicht öfter als 1 Mal alle 2 Sekunden. Das Schreiben in großen Schüben deutet jedoch darauf hin, dass wir in ClickHouse weniger häufig schreiben sollten. Dies wiederum kann zu einem Pufferüberlauf und dem Verlust von Protokollen führen. Die Lösung besteht darin, den Fluentd-Puffer zu erhöhen, aber dann steigt auch der Speicherverbrauch.

Beachten: Ein weiterer problematischer Aspekt unserer Lösung mit ClickHouse hing mit der Tatsache zusammen, dass die Partitionierung in unserem Fall (Blockhaus) über verbundene externe Tabellen implementiert wird Tabelle zusammenführen. Dies führt dazu, dass beim Abtasten großer Zeitintervalle übermäßig viel RAM benötigt wird, da die Metatabelle alle Partitionen durchläuft – auch diejenigen, die offensichtlich nicht die erforderlichen Daten enthalten. Nun kann dieser Ansatz jedoch für aktuelle Versionen von ClickHouse (ca 18.16).

Dadurch wird deutlich, dass nicht jedes Projekt über genügend Ressourcen verfügt, um Protokolle in Echtzeit in ClickHouse zu sammeln (genauer gesagt, ihre Verteilung wäre nicht angemessen). Darüber hinaus müssen Sie verwenden Akkumulator, worauf wir später zurückkommen werden. Der oben beschriebene Fall ist real. Und zu diesem Zeitpunkt waren wir nicht in der Lage, eine zuverlässige und stabile Lösung anzubieten, die für den Kunden geeignet wäre und es uns ermöglichen würde, Protokolle mit minimaler Verzögerung zu sammeln ...

Was ist mit Elasticsearch?

Elasticsearch ist dafür bekannt, hohe Arbeitslasten zu bewältigen. Versuchen wir es im selben Projekt. Nun sieht die Ladung so aus:

Protokolle in Kubernetes (und nicht nur) heute: Erwartungen und Realität

Elasticsearch war in der Lage, den Datenstrom zu verarbeiten, das Schreiben solcher Volumes beansprucht jedoch die CPU stark. Dies wird durch die Bildung eines Clusters entschieden. Technisch gesehen ist das kein Problem, aber es stellt sich heraus, dass wir allein für den Betrieb des Protokollsammelsystems bereits etwa 8 Kerne verwenden und eine zusätzliche hochbelastete Komponente im System haben ...

Fazit: Diese Option kann gerechtfertigt sein, aber nur, wenn das Projekt groß ist und sein Management bereit ist, erhebliche Ressourcen für ein zentrales Protokollierungssystem aufzuwenden.

Dann stellt sich natürlich die Frage:

Welche Protokolle werden wirklich benötigt?

Protokolle in Kubernetes (und nicht nur) heute: Erwartungen und Realität Versuchen wir, den Ansatz selbst zu ändern: Protokolle sollten gleichzeitig informativ und nicht abdeckend sein jeder Ereignis im System.

Nehmen wir an, wir haben einen erfolgreichen Online-Shop. Welche Protokolle sind wichtig? Es ist eine gute Idee, so viele Informationen wie möglich zu sammeln, beispielsweise von einem Zahlungsgateway. Aber nicht alle Protokolle des Image-Slicing-Dienstes im Produktkatalog sind für uns von entscheidender Bedeutung: Nur Fehler und erweiterte Überwachung reichen aus (z. B. der Prozentsatz von 500 Fehlern, die diese Komponente generiert).

Wir sind also zu dem Schluss gekommen, dass Eine zentrale Protokollierung ist nicht immer gerechtfertigt. Sehr oft möchte der Kunde alle Protokolle an einem Ort sammeln, obwohl tatsächlich vom gesamten Protokoll nur bedingt 5 % der für das Unternehmen kritischen Nachrichten erforderlich sind:

  • Manchmal reicht es aus, beispielsweise nur die Größe des Containerprotokolls und des Fehlersammlers (z. B. Sentry) zu konfigurieren.
  • Eine Fehlermeldung und ein großes lokales Protokoll können oft ausreichen, um Vorfälle zu untersuchen.
  • Wir hatten Projekte, die ausschließlich mit Funktionstests und Fehlersammelsystemen auskamen. Der Entwickler benötigte keine Protokolle als solche – er sah alles anhand von Fehlerspuren.

Illustration aus dem Leben

Eine andere Geschichte kann als gutes Beispiel dienen. Wir erhielten eine Anfrage vom Sicherheitsteam eines unserer Kunden, der bereits eine kommerzielle Lösung nutzte, die lange vor der Einführung von Kubernetes entwickelt wurde.

Es war notwendig, das zentralisierte Protokollerfassungssystem mit dem unternehmensweiten Problemerkennungssensor QRadar „anzufreunden“. Dieses System kann Protokolle über das Syslog-Protokoll empfangen und von FTP abrufen. Es war jedoch nicht sofort möglich, es mit dem remote_syslog-Plugin für fluentd zu integrieren (wie sich herausstellte, wir sind nicht alleine). Es stellte sich heraus, dass Probleme beim Einrichten von QRadar auf Seiten des Sicherheitsteams des Kunden lagen.

Infolgedessen wurde ein Teil der geschäftskritischen Protokolle auf FTP QRadar hochgeladen und der andere Teil über Remote-Syslog direkt von den Knoten umgeleitet. Dafür haben wir sogar geschrieben einfaches Diagramm - Vielleicht hilft es jemandem, ein ähnliches Problem zu lösen... Dank des resultierenden Schemas hat der Kunde selbst kritische Protokolle empfangen und analysiert (mit seinen Lieblingstools), und wir konnten die Kosten des Protokollierungssystems senken, indem wir nur das einsparten Im vergangenen Monat.

Ein weiteres Beispiel zeigt deutlich, was man nicht tun sollte. Einer unserer Kunden zur Bearbeitung jeder Ereignisse, die vom Benutzer kommen, mehrzeilig gemacht unstrukturierte Ausgabe Informationen im Protokoll. Wie Sie sich vorstellen können, war das Lesen und Speichern solcher Protokolle äußerst umständlich.

Kriterien für Protokolle

Solche Beispiele lassen den Schluss zu, dass Sie zusätzlich zur Auswahl eines Protokollsammelsystems auch Folgendes tun müssen Entwerfen Sie auch die Protokolle selbst! Was sind hier die Anforderungen?

  • Protokolle müssen in einem maschinenlesbaren Format vorliegen (z. B. JSON).
  • Protokolle sollten kompakt sein und die Möglichkeit bieten, den Protokollierungsgrad zu ändern, um mögliche Probleme zu beheben. Gleichzeitig sollten Sie in Produktionsumgebungen Systeme mit einer Protokollierungsstufe wie ausführen Warnung oder Fehler.
  • Protokolle müssen normalisiert sein, d. h. in einem Protokollobjekt müssen alle Zeilen denselben Feldtyp haben.

Unstrukturierte Protokolle können zu Problemen beim Laden der Protokolle in den Speicher und zu einem vollständigen Stopp ihrer Verarbeitung führen. Zur Veranschaulichung hier ein Beispiel mit Fehler 400, auf den sicher schon viele in fluentd-Logs gestoßen sind:

2019-10-29 13:10:43 +0000 [warn]: dump an error event: error_class=Fluent::Plugin::ElasticsearchErrorHandler::ElasticsearchError error="400 - Rejected by Elasticsearch"

Der Fehler bedeutet, dass Sie ein Feld, dessen Typ instabil ist, mit einer vorgefertigten Zuordnung an den Index senden. Das einfachste Beispiel ist ein Feld im Nginx-Protokoll mit einer Variablen $upstream_status. Es kann entweder eine Zahl oder eine Zeichenfolge enthalten. Zum Beispiel:

{ "ip": "1.2.3.4", "http_user": "-", "request_id": "17ee8a579e833b5ab9843a0aca10b941", "time": "29/Oct/2019:16:18:57 +0300", "method": "GET", "uri": "/staffs/265.png", "protocol": "HTTP/1.1", "status": "200", "body_size": "906", "referrer": "https://example.com/staff", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", "request_time": "0.001", "cache_status": "-", "upstream_response_time": "0.001, 0.007", "upstream_addr": "127.0.0.1:9000", "upstream_status": "200", "upstream_response_length": "906", "location": "staff"}
{ "ip": "1.2.3.4", "http_user": "-", "request_id": "47fe42807f2a7d8d5467511d7d553a1b", "time": "29/Oct/2019:16:18:57 +0300", "method": "GET", "uri": "/staff", "protocol": "HTTP/1.1", "status": "200", "body_size": "2984", "referrer": "-", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36", "request_time": "0.010", "cache_status": "-", "upstream_response_time": "0.001, 0.007", "upstream_addr": "10.100.0.10:9000, 10.100.0.11:9000", "upstream_status": "404, 200", "upstream_response_length": "0, 2984", "location": "staff"}

Aus den Protokollen geht hervor, dass Server 10.100.0.10 mit einem 404-Fehler geantwortet hat und die Anfrage an einen anderen Inhaltsspeicher gesendet wurde. Infolgedessen sah der Wert in den Protokollen wie folgt aus:

"upstream_response_time": "0.001, 0.007"

Diese Situation kommt so häufig vor, dass sie sogar eine gesonderte Betrachtung verdient Verweise in der Dokumentation.

Wie sieht es mit der Zuverlässigkeit aus?

Es gibt Zeiten, in denen ausnahmslos alle Protokolle lebenswichtig sind. Und damit haben die oben vorgeschlagenen/diskutierten typischen Protokollerfassungsschemata für K8s Probleme.

Beispielsweise kann fluentd keine Protokolle aus kurzlebigen Containern sammeln. In einem unserer Projekte lebte der Datenbank-Migrationscontainer weniger als 4 Sekunden und wurde dann gelöscht – laut entsprechender Anmerkung:

"helm.sh/hook-delete-policy": hook-succeeded

Aus diesem Grund wurde das Migrationsausführungsprotokoll nicht in den Speicher aufgenommen. Die Politik kann in diesem Fall helfen. before-hook-creation.

Ein weiteres Beispiel ist die Docker-Protokollrotation. Nehmen wir an, es gibt eine Anwendung, die aktiv in Protokolle schreibt. Unter normalen Bedingungen schaffen wir es, alle Protokolle zu verarbeiten, aber sobald ein Problem auftritt – zum Beispiel wie oben mit einem falschen Format beschrieben – stoppt die Verarbeitung und Docker rotiert die Datei. Die Folge ist, dass geschäftskritische Protokolle verloren gehen können.

Genau das ist es Es ist wichtig, Protokollströme zu trennen, indem das Senden der wertvollsten Nachrichten direkt in die Anwendung eingebettet wird, um deren Sicherheit zu gewährleisten. Darüber hinaus wäre es nicht überflüssig, welche zu erstellen „Akkumulator“ von Protokollen, die eine kurze Nichtverfügbarkeit des Speichers überstehen und gleichzeitig wichtige Nachrichten speichern kann.

Schließlich dürfen wir das nicht vergessen Es ist wichtig, jedes Subsystem ordnungsgemäß zu überwachen. Andernfalls kann es leicht zu einer Situation kommen, in der sich fluentd in einem Zustand befindet CrashLoopBackOff und sendet nichts, was den Verlust wichtiger Informationen verspricht.

Befund

In diesem Artikel betrachten wir keine SaaS-Lösungen wie Datadog. Viele der hier beschriebenen Probleme wurden bereits auf die eine oder andere Weise von kommerziellen Unternehmen gelöst, die sich auf das Sammeln von Protokollen spezialisiert haben, aber aus verschiedenen Gründen kann nicht jeder SaaS nutzen (Die wichtigsten sind Kosten und Einhaltung von 152-FZ).

Die zentralisierte Protokollsammlung scheint zunächst eine einfache Aufgabe zu sein, ist es aber keineswegs. Es ist wichtig, sich daran zu erinnern:

  • Nur kritische Komponenten müssen detailliert protokolliert werden, während für andere Systeme Überwachung und Fehlersammlung konfiguriert werden können.
  • Protokolle in der Produktion sollten minimal gehalten werden, um keine unnötige Belastung zu verursachen.
  • Protokolle müssen maschinenlesbar und normalisiert sein und ein striktes Format haben.
  • Wirklich kritische Protokolle sollten in einem separaten Stream gesendet werden, der von den Hauptprotokollen getrennt sein sollte.
  • Es lohnt sich, über einen Holzspeicher nachzudenken, der Sie vor hohen Belastungen schützt und die Belastung des Speichers gleichmäßiger macht.

Protokolle in Kubernetes (und nicht nur) heute: Erwartungen und Realität
Wenn diese einfachen Regeln überall angewendet würden, würden die oben beschriebenen Schaltkreise funktionieren – auch wenn ihnen wichtige Komponenten (die Batterie) fehlen. Wenn Sie sich nicht an solche Grundsätze halten, führt die Aufgabe Sie und die Infrastruktur leicht zu einer weiteren hochbelasteten (und gleichzeitig ineffektiven) Komponente des Systems.

PS

Lesen Sie auch auf unserem Blog:

Source: habr.com

Kommentar hinzufügen