Checkliste: Was musste getan werden, bevor Microservices für die Produktion bereitgestellt wurden?

Dieser Artikel enthält einen kurzen Überblick über meine eigenen Erfahrungen und die meiner Kollegen, mit denen ich Tag und Nacht gegen Zwischenfälle gekämpft hatte. Und viele Vorfälle wären niemals aufgetreten, wenn all diese Mikrodienste, die wir so sehr lieben, zumindest etwas sorgfältiger geschrieben worden wären.


Leider glauben einige Programmierer ernsthaft, dass eine Docker-Datei mit jedem Team ein Mikrodienst für sich ist und bereits jetzt bereitgestellt werden kann. Hafenarbeiter laufen - Geld geht ein. Dieser Ansatz führt zu Problemen, die von Leistungseinbußen, Debug-Unfähigkeit, Servicefehlern bis hin zu einem Albtraum namens Dateninkonsistenz reichen.


Wenn Sie der Meinung sind, dass es an der Zeit ist, eine weitere App in Kubernetes / ECS / was auch immer zu starten, habe ich etwas zu beanstanden.


Haftungsausschluss: Dies ist eine Übersetzung meines ursprünglichen Beitrags . Englisch ist nicht meine gemeinsame Sprache. Bitte helfen Sie mir, mich zu verbessern. Wenn Sie einen Fehler sehen, können Sie ihn mir gerne zeigen. Ich bin Ihnen dankbar.

Ich habe mir eine Reihe von Kriterien für die Beurteilung der Bereitschaft von Anträgen für den Start in der Produktion gebildet. Einige Elemente in dieser Checkliste können nicht auf alle Anwendungen angewendet werden, sondern nur auf spezielle. Andere gelten allgemein für alles. Ich bin sicher, Sie können Ihre Optionen in die Kommentare aufnehmen oder einen meiner Artikel bestreiten.


Wenn Ihr Microservice nicht mindestens eines der Kriterien erfüllt, können Sie ihn nicht in meinen idealen Cluster einbauen, der in einem 2000 Meter unter der Erde gelegenen Bunker mit Fußbodenheizung (ja, es wird heiß sein) und einem geschlossenen Selbst errichtet wurde -ausreichendes Internet-System.


Hier geht's! ..


Hinweis: Die Reihenfolge der Artikel spielt keine Rolle. Zumindest für mich.


Eine kurze Beschreibung in der Readme


Es enthält eine kurze Beschreibung seiner selbst ganz am Anfang von Readme.md in seinem Git-Repository.

Gott, es scheint so einfach. Aber wie oft bin ich darauf gestoßen, dass das Repository keine kurze Erklärung enthält, warum es benötigt wird, welche Aufgaben es löst und so weiter. Und ich spreche nicht einmal über etwas Komplizierteres wie Konfigurationsoptionen.


Integration in ein Überwachungssystem


Senden Sie Metriken an DataDog, NewRelic, Prometheus usw.

Analyse des Ressourcenverbrauchs, von Speicherlecks, Stacktraces, Service-Interdependenzen, Fehlerraten usw. Es ist äußerst schwierig zu steuern, was in einer großen verteilten Anwendung geschieht, ohne all dies zu verstehen.


Alertler sind konfiguriert


Hat Warnungen konfiguriert, die alle Standardsituationen sowie bekannte eindeutige Situationen abdecken.

Metriken sind gut, aber niemand wird ihnen manuell folgen. Daher erhalten wir automatisch Anrufe / Pushs / ​​Texte, wenn:


  • Der CPU- / Speicherverbrauch hat dramatisch zugenommen.
  • Der Verkehr hat dramatisch zugenommen / abgenommen.
  • Die Anzahl der pro Sekunde verarbeiteten Transaktionen hat sich in jede Richtung stark geändert.
  • Die Größe eines Bereitstellungsartefakts hat sich merklich geändert (exe, app, jar, ...).
  • Der Prozentsatz der Fehler oder deren Häufigkeit hat den zulässigen Schwellenwert überschritten.
  • Der Dienst hat das Senden von Metriken eingestellt (eine Situation, die häufig übersehen wird).
  • Die Regelmäßigkeit bestimmter erwarteter Ereignisse ist fehlerhaft (Cron-Job funktioniert nicht, nicht alle Ereignisse werden verarbeitet usw.)
  • ...

Runbooks erstellt


Für den Dienst wurde ein Dokument erstellt, das bekannte oder erwartete abnormale Situationen beschreibt.

  • Wie kann sichergestellt werden, dass der Fehler intern ist und nicht von Dritten abhängt?
  • ob es darauf ankommt: wo, an wen und was zu schreiben ist;
  • wie man es sicher neu startet;
  • wie man aus einem Backup wiederherstellt und wo sind sie, die Backups;
  • Welche speziellen Dashboards / Suchanfragen wurden erstellt, um diesen Dienst zu überwachen?
  • Hat der Dienst ein eigenes Admin-Panel und wie kommt man dorthin?
  • Gibt es eine API / CLI und wie kann man damit bekannte Probleme beheben?
  • und so weiter.

Die Liste kann in verschiedenen Organisationen sehr unterschiedlich sein, aber zumindest die grundlegenden Dinge müssen vorhanden sein.


Alle Protokolle werden in STDOUT / STDERR geschrieben


Der Dienst erstellt im Produktionsmodus keine Protokolldateien, sendet sie nicht an externe Dienste, enthält keine redundanten Abstraktionen wie Protokollrotation usw.

Wenn eine Anwendung Dateien mit Protokollen erstellt, sind diese Protokolle unbrauchbar. Sie werden nicht in 5 parallel laufende Container springen, in der Hoffnung, den notwendigen Fehler von "tail -f" abzufangen (ja, Sie werden weinen und weglaufen ...). Ein Neustart eines Containers führt zum vollständigen Verlust seiner Protokolle.


Wenn eine Anwendung Protokolle in ein System eines Drittanbieters schreibt, z. B. in Logstash, führt dies zu nutzloser Redundanz. Der benachbarte Dienst kann dies nicht, weil er auf einem anderen Framework basiert? Du wirst einen Zoo bekommen.


Die Anwendung schreibt einen Teil ihrer Protokolle in die Dateien und einen anderen Teil in das STDOUT, weil der Entwickler die INFO in der Konsole sehen möchte, aber DEBUG in den Dateien? Dies ist im Allgemeinen die schlechteste Option. Niemand braucht diese Komplexität und behält den zusätzlichen Code und die Konfigurationen bei, die man zuerst lernen muss.


Protokolle bedeuten json


Jede Zeile des Protokolls ist im Json-Format geschrieben und enthält einen vereinbarten Satz von Feldern.

Jeder schreibt immer noch Protokolle im Klartext. Dies ist eine echte Katastrophe. Ich würde mich freuen, nie etwas über Grok Patterns zu erfahren. Manchmal träume ich von ihnen und friere, versuche mich nicht zu bewegen, um ihre Aufmerksamkeit nicht zu erregen. Versuchen Sie einfach einmal, Java-Ausnahmen mit Logstash zu analysieren, und spüren Sie diesen Schmerz.


Json ist ein Segen, es ist ein Feuer, das vom Himmel gegeben wird. Fügen Sie einfach dort hinzu:


  • Zeitstempel mit Millisekunden gemäß RFC 3339 ;
  • Level: Info, Warnung, Fehler, Debug
  • user_id;
  • app_name
  • und andere Felder.

Laden Sie sie auf ein geeignetes System hoch (z. B. ordnungsgemäß konfigurierte ElasticSearch) und genießen Sie es. Verbinden Sie die Protokolle vieler Microservices und spüren Sie erneut, wozu monolithische Anwendungen gut waren.


(Sie können auch eine Anforderungs-ID hinzufügen und eine Ablaufverfolgung erhalten ...)


Protokolle mit Ausführlichkeitsstufen


Die Anwendung muss eine Umgebungsvariable, z. B. LOG_LEVEL, mit mindestens zwei Optionen unterstützen: ERRORS und DEBUG.

Es ist wünschenswert, dass alle Dienste im selben Ökosystem dieselbe Umgebungsvariable unterstützen. Keine Konfigurationsoption, nicht die Befehlszeilenoption (obwohl dies natürlich umbrochen werden könnte), sondern standardmäßig direkt aus der Umgebungsvariablen. Sie sollten in der Lage sein, so viele Protokolle wie möglich zu erhalten, wenn etwas schief geht, und so wenige Protokolle wie möglich, wenn alles in Ordnung ist.


Gesperrte Versionen von Abhängigkeiten


Abhängigkeiten für Paketmanager sind behoben, einschließlich Nebenversionen (z. B. cool_framework = 2.5.3). Festgelegte Sperrdateien sind auch ein guter Weg, dies zu tun.

Dies war natürlich schon an vielen Stellen erwähnt worden. Jemand sperrt Abhängigkeiten von seinen Hauptversionen und hofft, dass es in Nebenversionen nur Fehlerbehebungen und Sicherheitsbehebungen gibt. Es ist ein Fehler.


Dockerisiert


Das Repository enthält eine produktionsbereite Docker-Datei und die Datei docker-compose.yml

Docker ist für viele Unternehmen seit langem ein Standard. Es gibt Ausnahmen, aber selbst wenn Sie keinen Docker in der Produktion haben, sollte jeder Ingenieur in der Lage sein, "Docker-Compose-Up" einfach auszuführen und an nichts anderes zu denken, um den Entwickler-Build für die lokale Überprüfung zu erhalten. Und der Systemadministrator sollte ein Artefakt haben, das bereits von Entwicklern mit den richtigen Versionen von Bibliotheken, Dienstprogrammen usw. überprüft wurde, wobei die Anwendung zumindest irgendwie funktioniert , um es an die Produktion anzupassen.


Konfiguration über Umgebung


Alle wichtigen Konfigurationsoptionen werden aus der Umgebung gelesen und die Umgebung hat eine höhere Priorität als die Konfigurationsdateien (jedoch niedriger als die Befehlszeilenargumente).

Niemand wird jemals Ihre Konfigurationsdateien lesen und deren Format studieren wollen. Akzeptiere es einfach.


Weitere Details hier: https://12factor.net/config


Bereitschafts- und Lebendigkeitssonden


Enthält die entsprechenden Endpunkte oder CLI-Befehle, um die Bereitschaft zu testen, Anforderungen beim Start und während des Lebenszyklus zu bearbeiten

Wenn eine Anwendung HTTP-Anforderungen bedient, sollte sie standardmäßig über zwei Schnittstellen verfügen (CLI-Überprüfungen sind ebenfalls möglich):


  1. Um zu überprüfen, ob die Anwendung aktiv ist und nicht hängen bleibt, wird die Lebendigkeitssonde verwendet. Wenn die Anwendung nicht reagiert, kann sie von Orchestratoren wie Kubernetes automatisch gestoppt werden. Ehrlich gesagt kann das Beenden einer blockierten Anwendung einen Dominoeffekt verursachen und Ihren gesamten Service dauerhaft beeinträchtigen. Dies ist jedoch kein Entwicklerproblem. Erstellen Sie einfach diesen Endpunkt und schalten Sie Ihr Telefon in den Flugmodus.


  2. Um zu überprüfen, ob die Anwendung nicht nur gestartet wurde, sondern bereit ist, Anforderungen anzunehmen, kann ein Bereitschaftstest durchgeführt werden. Wenn eine Anwendung eine Verbindung mit einer Datenbank, einem Warteschlangensystem usw. hergestellt hat, muss sie mit einem Status von 200 bis 400 (für Kubernetes) antworten.



Ressourcenlimits


Enthält Beschränkungen für Speicher, CPU, Speicherplatz und andere verfügbare Ressourcen im vereinbarten Format.

Die konkrete Umsetzung dieses Punktes wird in verschiedenen Organisationen und für verschiedene Orchestratoren sehr unterschiedlich sein. Sie müssen jedoch speziell für alle verfügbaren Umgebungen (prod, dev, test, ...) konfiguriert und außerhalb des Git-Repos mit dem Anwendungscode gespeichert werden .


Automatisierte Erstellung und Lieferung


Das in Ihrer Organisation oder Ihrem Projekt verwendete CI / CD-System ist konfiguriert und kann die Anwendung gemäß dem akzeptierten Workflow an die gewünschte Umgebung liefern.

Nichts wird jemals manuell an die Produktion geliefert.


Unabhängig davon, wie schwierig es ist, Builds und die Bereitstellung Ihrer Anwendung zu automatisieren, muss dies erfolgen, bevor dieses Projekt in Produktion geht. Dieser Artikel umfasst das Erstellen und Ausführen von Ansible / Chef-Kochbüchern / Salt / ... sowie das Erstellen von Anwendungen für mobile Geräte. Gabeln des Betriebssystems; Bilder von virtuellen Maschinen, was auch immer.
Schwer zu automatisieren? Das kann man dann nicht in die Welt bringen. Niemand wird dies nach Ihrer Abreise wieder manuell erstellen können.


Anmutiges Herunterfahren


Die Anwendung versteht SIGTERM und andere Signale und fährt sich nach der Verarbeitung der aktuellen Aufgabe ordnungsgemäß herunter.

Dies ist ein äußerst wichtiger Punkt. Docker-Prozesse werden verwaist und arbeiten monatelang im Hintergrund, wo niemand sie sieht. Nicht-Transaktionsvorgänge werden mitten in der Ausführung beendet, was zu einer Inkonsistenz der Daten zwischen Diensten und zwischen Datenspeichern führt. Dies führt zu Fehlern, die nicht vorhersehbar sind und die sehr, sehr teuer sein können.


Wenn Sie einige Abhängigkeiten von Drittanbietern nicht kontrollieren können und nicht garantieren können, dass Ihr Code SIGTERM korrekt verarbeitet, verwenden Sie etwas wie dummes Init .


Weitere Informationen hier:



Die Datenbankverbindung wird regelmäßig überprüft


Die Anwendung pingt ständig die Datenbank an und reagiert automatisch auf die Ausnahme "Verbindung verloren", die durch einen Ping oder andere Abfragen aufgetreten ist, und versucht, sie selbst wiederherzustellen oder die Arbeit korrekt abzuschließen.

Ich habe viele Fälle gesehen (es ist nicht nur eine Redewendung), in denen Dienste, die zur Verarbeitung von Warteschlangen oder Ereignissen erstellt wurden, die als Dämonen fungieren, die Verbindung durch Zeitüberschreitung verloren und unendlich viele Fehler in Protokolle geschrieben haben, Nachrichten an ihre ursprüngliche Warteschlange zurückgesendet und an diese gesendet wurden Dead Letter Queue oder einfach nicht ihren Job machen.


Horizontal skaliert


Wenn die Last zunimmt, reicht es aus, mehr Instanzen der Anwendung auszuführen, um sicherzustellen, dass alle Anforderungen oder Aufgaben verarbeitet werden.

Nicht alle Anwendungen können horizontal skaliert werden. Ein gutes Beispiel ist Kafka Consumers. Dies ist nicht unbedingt schlecht, aber wenn eine bestimmte Anwendung nicht zweimal gestartet werden kann, müssen alle interessierten Personen dies im Voraus wissen. Diese Informationen sollten ein Dorn im Auge sein, in der Readme-Datei fett formatiert und hinzugefügt werden, wo immer dies möglich ist. Einige Anwendungen können naturgemäß unter keinen Umständen parallel gestartet werden, was zu ernsthaften Schwierigkeiten bei der Wartung führt.


Es ist viel besser, wenn die Anwendung selbst diese Situationen steuert oder ein Wrapper dafür geschrieben wird, der die "Konkurrenten" effektiv überwacht und einfach verhindert, dass der Prozess ihre Arbeit startet oder startet, bis ein anderer Prozess seinen eigenen abschließt oder eine externe Konfiguration dies zulässt N Prozesse arbeiten gleichzeitig.


Warteschlangen für tote Briefe und Widerstand gegen "schlechte" Nachrichten


Wenn der Dienst Warteschlangen abhört oder auf Ereignisse reagiert, führt das Ändern des Formats oder Inhalts von Nachrichten nicht zu einem Absturz. Nicht erfolgreiche Versuche, die Aufgabe zu verarbeiten, werden N-mal wiederholt. Danach wird die Nachricht an die Dead Letter Queue gesendet.

Ich habe oft gesehen, wie Verbraucher endlos neu gestartet wurden und die Warteschlangen so groß wurden, dass ihre anschließende Verarbeitung viele Tage dauerte. Jeder Listener der Warteschlange muss bereit sein, das Format zu ändern, zufällige Fehler in der Nachricht selbst (z. B. Datentypen in JSON) oder während die Nachricht vom untergeordneten Code verarbeitet wird. Ich war sogar mit einer Situation konfrontiert, in der die Standardbibliothek für RabbitMQ in einem äußerst beliebten Framework Wiederholungsversuche, Versuchszähler usw. überhaupt nicht unterstützte. und es gab keine einfache Möglichkeit, seine Logik zu erweitern.


Und noch viel schlimmer, wenn die Nachricht im Fehlerfall einfach zerstört wird.


Begrenzte Anzahl verarbeiteter Nachrichten und Aufgaben durch eine Instanz


Es unterstützt eine Umgebungsvariable, die bei Bedarf festgelegt werden kann, um die maximale Anzahl verarbeiteter Aufgaben zu begrenzen. Danach wird der Dienst ordnungsgemäß heruntergefahren.

Ständig steigender Speicherverbrauch und "OOM Killed" am Ende sind die Norm des Lebens für moderne kubernetische Köpfe. Die Implementierung einer primitiven Prüfung, die Ihnen nur die Notwendigkeit erspart, all diese Speicherlecks zu untersuchen, würde das Leben erleichtern. Ich habe oft gesehen, dass Menschen viel Zeit und Mühe (und Geld) aufwenden, um diese Lecks zu stoppen, aber es gibt keine Garantie dafür, dass das nächste Engagement Ihres Arbeitskollegen die Situation nicht verschlimmert. Wenn die Anwendung eine Woche überleben kann, ist dies ein ausgezeichneter Indikator. Lassen Sie es dann einfach aufhören und wird neu gestartet. Dies ist besser als SIGKILL (zu SIGTERM, siehe oben) oder die Ausnahme "Nicht genügend Speicher". Für ein paar Jahrzehnte reicht diese Problemumgehung für die meisten Fälle aus.


Nicht durch eine Drittanbieter-Integration mit IP-Adress-Whitelistening gesperrt


Wenn eine Anwendung Anforderungen an einen Drittanbieter-Dienst stellt, der nur Anforderungen von begrenzten IP-Adressen zulässt, stellt der Dienst diese Anforderungen indirekt (z. B. über einen Reverse-Proxy).

Dies ist ein seltener Fall, aber äußerst unangenehm. Es ist sehr unpraktisch, wenn eine kleine Anwendung die Möglichkeit blockiert, das Clusternetzwerk zu ändern oder in eine andere Region der gesamten Infrastruktur zu wechseln. Wenn Sie mit etwas kommunizieren müssen, das nicht mit oAuth oder VPN funktioniert, richten Sie im Voraus einen Reverse-Proxy ein. Implementieren Sie in Ihren Programmen nicht das dynamische Hinzufügen / Löschen externer Integrationen für diese Zwecke, die Hostnamen von Drittanbietern usw. verwenden, da Sie sich dabei an die einzige verfügbare Laufzeitumgebung halten. Es ist besser, mit der Automatisierung dieser Prozesse zu beginnen, um beispielsweise Nginx-Konfigurationen oder andere zu verwalten, und in Ihrer Anwendung darauf zu verweisen.


Offensichtlicher HTTP-Benutzeragent


Der Dienst ersetzt den User-Agent-Header durch einen benutzerdefinierten für alle Anforderungen an APIs. Dieser Header enthält genügend Informationen über den Dienst selbst und seine Version.

Wenn 100 verschiedene Anwendungen miteinander kommunizieren, können Sie verrückt werden und in den Protokollen etwas wie "Go-http-client / 1.1" und die dynamische IP-Adresse des Kubernetes-Containers sehen. Identifizieren Sie Ihre Anwendung und ihre Version immer explizit.


Verstößt nicht gegen eine Lizenz


Enthält keine Abhängigkeiten, die die Anwendung einschränken, und es handelt sich nicht um eine Kopie des Codes einer anderen Person usw.

Dies ist ein selbstverständlicher Fall, aber ich habe Dinge gesehen, bei denen selbst ein Anwalt, der die NDA geschrieben hat, jetzt Schluckauf hat.


Verwendet keine nicht unterstützten Abhängigkeiten.


Wenn Sie den Dienst zum ersten Mal starten, enthält er keine Abhängigkeiten, die bereits veraltet sind.

Wenn die Bibliothek, die Sie in das Projekt aufgenommen haben, von niemandem mehr unterstützt wird, suchen Sie nach einem anderen Weg, um das Ziel zu erreichen, oder beginnen Sie, die Bibliothek selbst zu unterstützen.


Fazit


In meiner Liste gibt es einige sehr spezifische Überprüfungen für bestimmte Technologien oder Situationen, oder ich habe einfach vergessen, etwas hinzuzufügen. Ich bin sicher, Sie wissen auch, was darin enthalten sein muss.

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


All Articles