Der Kampf um Qualitätslösungen bei Erlang / Elixir


@jcutrer


Heute werden wir über Ereignisprotokolle, quantitative Metriken und die Überwachung all dessen sprechen, um die Reaktionsrate des Teams auf Vorfälle zu erhöhen und die Ausfallzeiten des Zielsystems zu verringern.


Erlang / OTP als Rahmen und Ideologie für den Aufbau verteilter Systeme bietet regulierte Ansätze für die Entwicklung, Tools und Implementierung von Standardkomponenten. Nehmen wir an, wir haben das Potenzial von OTP genutzt und sind vom Prototyp bis zur Produktion gegangen. Unser Erlang-Projekt fühlt sich auf Kampfservern großartig an, die Codebasis entwickelt sich ständig weiter, neue Anforderungen und Funktionen erscheinen, neue Leute kommen ins Team und alles scheint in Ordnung zu sein. Aber manchmal geht etwas schief und technische Probleme, multipliziert mit dem menschlichen Faktor, können zu einem Unfall führen.


Da es unmöglich ist, für absolut alle möglichen Fälle von Fehlern und Problemen Strohhalme zu legen, oder dies wirtschaftlich nicht machbar ist, ist es erforderlich, die Ausfallzeit des Systems bei Fehlern durch Management- und Softwarelösungen zu reduzieren.


In Informationssystemen besteht immer die Wahrscheinlichkeit, dass Fehler verschiedener Art auftreten:


  • Hardwarefehler und Stromausfälle
  • Netzwerkfehler: Konfigurationsfehler, Firmware-Kurven
  • Logische Fehler: von Algorithmuscodierungsfehlern bis zu Architekturproblemen, die an den Grenzen von Subsystemen und Systemen auftreten.
  • Sicherheitsprobleme und damit verbundene Angriffe und Hacks, einschließlich interner Betrugsfälle.

Wir unterscheiden sofort die Verantwortung: Die Überwachung der Infrastruktur, die beispielsweise von zabbix organisiert wird, wird für den Betrieb von Computergeräten und Datenübertragungsnetzen verantwortlich sein. Es wurde viel über die Installation und Konfiguration einer solchen Überwachung geschrieben, wir werden sie nicht wiederholen.


Aus Sicht des Entwicklers liegt das Problem der Zugänglichkeit und Qualität in der Ebene der Früherkennung von Fehlern und Leistungsproblemen und der schnellen Reaktion darauf. Dies erfordert Ansätze und Mittel zur Bewertung. Versuchen wir also, quantitative Metriken abzuleiten und zu analysieren, welche Qualität wir in verschiedenen Phasen der Entwicklung und des Betriebs des Projekts erheblich verbessern können.


Montagesysteme


Ich möchte Sie noch einmal an die Bedeutung des Engineering-Ansatzes und der Tests in der Softwareentwicklung erinnern. Erlang / OTP bietet zwei Test-Frameworks gleichzeitig: Eunit und Common Test.


Als Metriken für eine erste Beurteilung des Status der Codebasis und ihrer Dynamik können Sie die Anzahl erfolgreicher und problematischer Tests, ihre Ausführungszeit und den Prozentsatz der Codeabdeckung mit Tests verwenden. Beide Frameworks ermöglichen das Speichern von Testergebnissen im Junit-Format.
Fügen Sie beispielsweise für rebar3 und ct die folgenden Zeilen zu rebar.config hinzu:


{cover_enabled, true}. {cover_export_enabled, true}. {ct_opts,[ {ct_hooks, [{cth_surefire, [{path, "report.xml"}]}]} ]}. 

Mit der Anzahl der erfolgreichen und erfolglosen Tests können Sie ein Trenddiagramm erstellen:

Wenn Sie sich das ansehen, können Sie die Dynamik des Teams und die Regression der Tests bewerten. In Jenkins kann dieses Diagramm beispielsweise mit dem Test Results Analyzer Plugin abgerufen werden.


Wenn die Tests rot werden oder längere Zeit ausgeführt werden, kann die Freigabe anhand der Metriken bereits in der Phase der Montage und des automatischen Tests abgeschlossen werden.


Anwendungsmetriken


Zusätzlich zu den Betriebssystemmetriken sollte die Überwachung Anwendungsmetriken umfassen, z. B. die Anzahl der Ansichten pro Sekunde, die Anzahl der Zahlungen und andere kritische Indikatoren.


In meinen Projekten verwende ich eine Vorlage wie ${application}.${metrics_type}.${name} , um die Metriken zu benennen. Mit dieser Benennung können Sie Listen mit Metriken des Formulars abrufen


 messaging.systime_subs.messages.delivered = 1654 messaging.systime_subs.messages.proxied = 0 messaging.systime_subs.messages.published = 1655 messaging.systime_subs.messages.skipped = 3 

Je mehr Metriken vorhanden sind, desto einfacher ist es möglicherweise zu verstehen, was in einem komplexen System geschieht.


Erlang VM-Metriken


Besondere Aufmerksamkeit sollte der Überwachung der Erlang VM gewidmet werden. Die Ideologie, es abstürzen zu lassen, ist wunderschön, und die ordnungsgemäße Verwendung von OTP wird sicherlich dazu beitragen, die heruntergefallenen Teile der Anwendung in Erlang VM zu entfernen. Vergessen Sie jedoch nicht Erlang VM selbst, da es schwierig ist, es zu löschen, aber es ist möglich. Alle Optionen basieren auf der Erschöpfung von Ressourcen. Wir listen die wichtigsten auf:


  • Atomtabellenüberlauf.
    Atome sind Bezeichner, deren Hauptzweck darin besteht, die Lesbarkeit des Codes zu verbessern. Einmal erstellte Atome bleiben für immer im Speicher der Erlang VM-Instanz, da sie vom Garbage Collector nicht gelöscht werden. Warum passiert das? Der Garbage Collector arbeitet in jedem Prozess separat mit Daten aus diesem Prozess, während Atome über die Datenstrukturen vieler Prozesse verteilt werden können.
    Standardmäßig können 1.048.576 Atome erstellt werden. In Artikeln über das Töten von Erlang VM finden Sie normalerweise so etwas.


     [list_to_atom(integer_to_list(I)) || I <- lists:seq(erlang:system_info(atom_count), erlang:system_info(atom_limit))] 

    zur Veranschaulichung dieses Effekts. Es scheint, dass ein künstliches Problem in realen Systemen nicht erreichbar ist, aber es gibt Fälle ... Zum Beispiel wird im externen API-Handler beim Parsen von Anforderungen binary_to_atom/2 anstelle von binary_to_existing_atom/2 oder list_to_atom/1 anstelle von list_to_existing_atom/1 .
    Die folgenden Parameter sollten verwendet werden, um den Zustand der Atome zu überwachen:


    1. erlang:memory(atom_used) - Speichermenge, die für Atome verwendet wird
    2. erlang:system_info(atom_count) - die Anzahl der im System erstellten Atome. Zusammen mit erlang:system_info(atom_limit) kann die erlang:system_info(atom_limit) berechnet werden.

  • Prozesslecks.
    Ich möchte sofort sagen, dass, wenn process_limit (+ P erreicht ist, das Argument erl) erlang vm nicht fällt, aber es in einen Notfallzustand übergeht, zum Beispiel wird es höchstwahrscheinlich unmöglich sein, eine Verbindung dazu herzustellen. Wenn bei der Zuweisung zu durchgesickerten Prozessen nicht genügend Speicher zur Verfügung steht, stürzt erlang vm ab.


    1. erlang:system_info(process_count) - die Anzahl der momentan aktiven Prozesse. Zusammen mit erlang:system_info(process_limit) kann die Prozessauslastung berechnet werden.
    2. erlang:memory(processes) - zugewiesener Speicher für Prozesse
    3. erlang:memory(processes_used) - verwendeter Speicher für Prozesse.

  • Mailbox-Prozessüberlauf.
    Ein typisches Beispiel für ein solches Problem ist, dass der Absenderprozess Nachrichten an den Empfängerprozess sendet, ohne auf eine Bestätigung zu warten, während der receive im Empfängerprozess alle diese Nachrichten aufgrund eines fehlenden oder falschen Musters ignoriert. Infolgedessen werden Nachrichten in der Mailbox gesammelt. Obwohl erlang über einen Mechanismus zum Verlangsamen des Absenders verfügt, falls der Handler die Verarbeitung nicht ausführen kann, stürzt vm nach Erschöpfung des verfügbaren Speichers ab.
    Um zu verstehen, ob es Probleme mit dem Postfachüberlauf gibt, hilft etop.


     $ erl -name etop@host -hidden -s etop -s erlang halt -output text -node dest@host -setcookie some_cookie -tracing off -sort msg_q -interval 1 -lines 25 


    Als Metrik für die kontinuierliche Überwachung können Sie die Anzahl der Problemprozesse verwenden. Um sie zu identifizieren, können Sie die folgende Funktion verwenden:


     top_msq_q()-> [{P, RN, L, IC, ST} || P <- processes(), { _, L } <- [ process_info(P, message_queue_len) ], L >= 1000, [{_, RN}, {_, IC}, {_, ST}] <- [process_info(P, [registered_name, initial_call, current_stacktrace]) ] ]. 

    Diese Liste kann auch protokolliert werden. Wenn Sie eine Benachrichtigung von der Überwachung erhalten, wird die Analyse des Problems vereinfacht.


  • Undichte Binärdateien.
    Der Speicher für große Binärdateien (mehr als 64 Byte) wird im allgemeinen Heap zugewiesen. Der zugewiesene Block verfügt über einen Referenzzähler, der die Anzahl der Prozesse angibt, die Zugriff darauf haben. Nach dem Zurücksetzen des Zählers erfolgt die Reinigung. Das einfachste System, aber wie sie sagen, gibt es Nuancen. Im Prinzip besteht die Möglichkeit, dass ein Prozess so viel Müll auf dem Heap generiert, dass das System nicht über genügend Speicher verfügt, um die Bereinigung durchzuführen.
    erlang:memory(binary) dient als Metrik für die Überwachung und zeigt den für Binärdateien zugewiesenen Speicher an.



Die Fälle, die zum Fall von VM führen, sind also aussortiert. Außerdem ist es hilfreich, nicht weniger wichtige Parameter zu überwachen, die sich direkt oder indirekt auf die korrekte Funktion Ihrer Anwendungen auswirken:


  • Der von den ETS: erlang:memory(ets) .
  • Speicher für kompilierte Module: erlang:memory(code) .
    Wenn Ihre Lösungen keine dynamische Codekompilierung verwenden, kann diese Option ausgeschlossen werden.
    Ich möchte auch erlydtl erwähnen. Wenn Sie Vorlagen dynamisch kompilieren, wird beim Kompilieren ein Strahl erstellt, der in den VM-Speicher geladen wird. Es kann auch Speicherlecks verursachen.
  • Systemspeicher: erlang:memory(system) . Zeigt den Speicherbedarf zur Erlang-Laufzeit an.
  • Insgesamt verbrauchter Speicher: erlang:memory(total) . Dies ist die Speichermenge, die von Prozessen und zur Laufzeit verbraucht wird.
  • Informationen zu Ermäßigungen: erlang:statistics(reductions) .
  • Anzahl der Prozesse und Ports, die zur Ausführung bereit sind: erlang:statistics(run_queue) .
  • Mit der Verfügbarkeit der Instanz vm: erlang:statistics(runtime) können Sie nachvollziehen, ob ein Neustart ohne Protokollanalyse durchgeführt wurde.
  • Netzwerkaktivität: erlang:statistics(io) .

Übermitteln von Metriken an zabbix


Wir erstellen eine Datei mit Anwendungsmetriken und erlang vm-Metriken, die wir alle N Sekunden aktualisieren. Für jeden erlang-Knoten muss die Metrikdatei die Metriken der darauf ausgeführten Anwendungen und die Metriken der erlang vm-Instanz enthalten. Das Ergebnis sollte ungefähr so ​​aussehen:


 messaging.systime_subs.messages.delivered = 1654 messaging.systime_subs.messages.proxied = 0 messaging.systime_subs.messages.published = 1655 messaging.systime_subs.messages.skipped = 3 …. erlang.io.input = 2205723664 erlang.io.output = 1665529234 erlang.memory.binary = 1911136 erlang.memory.ets = 1642416 erlang.memory.processes = 23596432 erlang.memory.processes_used = 23598864 erlang.memory.system = 50883752 erlang.memory.total = 74446048 erlang.processes.count = 402 erlang.processes.run_queue = 0 erlang.reductions = 148412771 .... 

Mit zabbix_sender senden wir diese Datei an zabbix, wo bereits eine grafische Darstellung und die Möglichkeit zum Erstellen von Automatisierungs- und Benachrichtigungsauslösern verfügbar sind.


Jetzt, da wir Metriken im Überwachungssystem haben und Automatisierungsauslöser und Benachrichtigungsereignisse auf ihrer Grundlage erstellt haben, haben wir die Möglichkeit, Unfälle zu vermeiden, indem wir im Voraus auf alle gefährlichen Abweichungen von einem voll funktionsfähigen Zustand reagieren.


Zentrales Sammeln von Protokollen


Wenn ein Projekt 1-2 Server enthält, können Sie wahrscheinlich immer noch ohne eine zentrale Protokollsammlung leben. Sobald jedoch ein verteiltes System mit vielen Servern, Clustern und Umgebungen angezeigt wird, muss das Problem der Erfassung und bequemen Anzeige von Protokollen gelöst werden.


Um Protokolle in meine Projekte zu schreiben, verwende ich Lager. Auf dem Weg vom Prototyp zur Produktion durchlaufen Projekte häufig die folgenden Phasen des Sammelns von Protokollen:


  • Einfachste Protokollierung mit Ausgabe in eine lokale Datei oder sogar in stdout (lager_file_backend)
  • Zentralisierte Erfassung von Protokollen mithilfe von beispielsweise syslogd und automatischem Senden von Protokollen an den Collector. Für ein solches Schema ist lager_syslog geeignet.
    Der Hauptnachteil des Schemas besteht darin, dass Sie zum Protokollsammlungsserver gehen, die Datei mit den erforderlichen Protokollen suchen und die Ereignisse auf der Suche nach denjenigen filtern müssen, die für das Debuggen erforderlich sind.
  • Zentralisierte Erfassung von Protokollen mit Speicher im Repository mit der Möglichkeit, nach Datensätzen zu filtern und zu suchen.

Über die Minuspunkte, Pluspunkte und quantitativen Metriken, die mit letzteren angewendet werden können, und wir werden im Prisma einer bestimmten Implementierung lager_clickhouse - lager_clickhouse , die ich in den meisten entwickelten Projekten verwende. Ein paar Worte zu lager_clickhouse . Dies ist das größere Backend zum Speichern von Ereignissen in Clickhouse. Im Moment ist dies ein internes Projekt, aber es gibt Pläne, es zu öffnen. Bei der Entwicklung von lager_clickhouse musste ich einige Funktionen von clickhouse umgehen, z. B. die Ereignispufferung verwenden, um häufige Anfragen in clickhouse zu vermeiden. Der Aufwand hat sich bei stabilem Betrieb und guter Leistung gelohnt.


Der Hauptnachteil des Ansatzes zum Speichern im Repository ist das zusätzliche Wesentliche - Clickhouse und die Notwendigkeit, Code zum Speichern von Ereignissen darin und die Benutzeroberfläche zum Analysieren und Suchen nach Ereignissen zu entwickeln. Bei einigen Projekten kann es auch wichtig sein, TCP zum Senden von Protokollen zu verwenden.


Aber die Profis überwiegen meines Erachtens alle möglichen Nachteile.


  • Einfache und schnelle Ereignissuche:


    • Filtern nach Datum, ohne auf einem zentralen Server mit einer Reihe von Ereignissen nach einer oder mehreren Dateien suchen zu müssen.
    • Filtern nach Umgebung. Protokolle von verschiedenen Subsystemen und häufig von verschiedenen Clustern werden in ein Repository geschrieben. Im Moment erfolgt die Trennung nach Beschriftungen, die auf jedem Knoten des Clusters festgelegt sind.
    • Nach Hostnamen filtern
    • Filtern nach dem Namen des Moduls, das das Ereignis gesendet hat
    • Ereignisfilterung
    • Textsuche

    Eine Beispielansicht der Protokollanzeigeoberfläche ist im Screenshot dargestellt:


  • Automatisierungsfähigkeit.
    Mit der Einführung des Protokollspeichers wurde es möglich, in Echtzeit Informationen über die Anzahl der Fehler, das Auftreten kritischer Fehler und die Systemaktivität zu erhalten. Durch die Einführung bestimmter Grenzwerte können wir Notfallereignisse generieren, wenn das System den Funktionsstatus verlässt. Die Handler führen Automatisierungsaktionen aus, um diesen Status zu beseitigen, und senden Benachrichtigungen an die für die Funktionalität verantwortlichen Teammitglieder:


    • Wenn ein kritischer Fehler auftritt.
    • Bei Massenfehlern (die Zeitableitung steigt schneller als ein bestimmter Grenzwert an).
    • Eine separate Metrik ist die Rate der Ereignisgenerierung, d. H. Wie viele neue Ereignisse im Ereignisprotokoll angezeigt werden. Sie können fast immer die ungefähre Anzahl der von einem Projekt pro Zeiteinheit generierten Protokolle kennen. Wenn es mehrmals überschritten wird, läuft höchstwahrscheinlich etwas schief.


Die Weiterentwicklung des Themas Automatisierung der Behandlung von Notfallereignissen war die Verwendung von Lua-Skripten. Jeder Entwickler oder Administrator kann ein Skript zum Verarbeiten von Protokollen und Metriken schreiben. Skripte bieten Flexibilität und ermöglichen es Ihnen, personalisierte Automatisierungsskripte und Benachrichtigungen zu erstellen.


Zusammenfassung


Um die im System ablaufenden Prozesse zu verstehen und Vorfälle zu untersuchen, sind quantitative Indikatoren und Ereignisprotokolle sowie praktische Tools zu deren Analyse unerlässlich. Je mehr Informationen wir über das System sammeln, desto einfacher ist es, sein Verhalten zu analysieren und Probleme bereits im Stadium ihres Auftretens zu beheben. Für den Fall, dass unsere Maßnahmen nicht funktionierten, haben wir immer Zeitpläne und detaillierte Protokolle des Vorfalls.


Und wie arbeiten Sie mit Lösungen auf Erlang / Elixir und auf welche interessanten Fälle sind Sie in der Produktion gestoßen?

Source: https://habr.com/ru/post/de437720/


All Articles