Checkliste: Was ist zu tun, bevor Mikrodienste in prod ausgeführt werden?

    Dieser Artikel enthält einen kurzen Ausschnitt aus meinen eigenen Erfahrungen und denen meiner Kollegen, mit denen ich Tag und Nacht Vorfälle harken musste. Und viele Vorfälle wären niemals vorgekommen, wenn alle bevorzugten Mikrodienste aller mindestens etwas sorgfältiger geschrieben worden wären.


    Unglücklicherweise glauben einige Programmierer auf niedriger Ebene ernsthaft, dass eine Docker-Datei mit einem beliebigen Team in sich selbst ein Microservice ist und bereits jetzt eingesetzt werden kann. Hafenarbeiter drehen sich, Laveshka ist trüb. Dieser Ansatz wird zu Problemen, beginnend mit einem Leistungsabfall, der Unfähigkeit, Fehler zu debuggen und Servicefehler auszuführen, und endet mit einem Alptraum, der als Data Inconsistency bezeichnet wird.


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


    Russische Version ist ebenfalls verfügbar .


    Ich habe mir bestimmte Kriterien für die Beurteilung der Bereitschaft von Anträgen für den Start in der Produktion zusammengestellt. Einige Elemente in dieser Checkliste können nicht auf alle Anwendungen angewendet werden, sondern nur auf spezielle. Andere gelten im Allgemeinen für alles. Ich bin sicher, Sie können Ihre Optionen in den Kommentaren hinzufügen oder einen dieser Punkte anfechten.


    Wenn Ihr Mikrodienst mindestens eines der Kriterien nicht erfüllt, lasse ich es nicht in meinen idealen Cluster ein, der in einem 2000 m unterirdischen Bunker mit Fußbodenheizung und einem geschlossenen autarken Internet-Einspeisesystem errichtet wurde.


    Lass uns gehen ....


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


    Kurze Beschreibung in der Readme


    Es enthält eine kurze Beschreibung von sich selbst am Anfang von Readme.md in seinem Repository.

    Gott, es scheint so einfach zu sein. Aber wie oft bin ich aufgefallen, dass das Repository nicht die geringste Erklärung dafür enthält, warum es benötigt wird, welche Aufgaben es löst und so weiter. Es ist nicht nötig, etwas komplizierteres zu besprechen, beispielsweise Konfigurationsoptionen.


    Integration mit dem Überwachungssystem


    Senden Sie Metriken an DataDog, NewRelic, Prometheus usw.

    Analyse des Ressourcenverbrauchs, Speicherlecks, Stacktraces, Abhängigkeiten der Dienste, Fehlerrate - Es ist äußerst schwierig zu steuern, was in einer großen verteilten Anwendung geschieht, ohne all das zu verstehen (und nicht nur).


    Warnungen sind konfiguriert


    Für den Dienst enthalten Warnungen (Warnungen), die alle Standardsituationen sowie bekannte eindeutige Situationen abdecken.

    Metriken sind gut, aber niemand wird ihnen folgen. Deshalb erhalten wir automatisch Anrufe / Pushes / SMS, wenn:


    • Der Verbrauch von CPU / Speicher ist dramatisch gestiegen.
    • Der Verkehr ist in die Höhe geschossen.
    • Die Anzahl der pro Sekunde verarbeiteten Transaktionen hat sich in jede Richtung dramatisch verändert.
    • Die Größe des Artefakts nach der Montage hat sich dramatisch verändert (exe, app, jar, ...).
    • Der Prozentsatz der Fehler oder deren Häufigkeit hat den zulässigen Schwellenwert überschritten.
    • Der Dienst sendet keine Messdaten mehr (häufig eine übersehene Situation).
    • Die Regelmäßigkeit bestimmter erwarteter Ereignisse ist unterbrochen (Cron-Job funktioniert nicht, nicht alle Ereignisse werden behandelt usw.)
    • ...

    Runbooks erstellt


    Für den Dienst wurde ein Dokument erstellt, in dem bekannte oder erwartete ungewöhnliche Situationen beschrieben werden.

    • wie Sie sicherstellen können, dass der Fehler intern ist und nicht von Dritten abhängt;
    • wenn es darauf ankommt, wo, wem und was schreiben soll;
    • wie man es sicher neu startet;
    • Wie kann man aus einem Backup wiederherstellen und wo sind Backups?
    • Welche speziellen Dashboards / Abfragen werden erstellt, um diesen Dienst zu überwachen?
    • Hat der Dienst ein eigenes Admin-Panel und wie komme ich dorthin?
    • Gibt es eine API / CLI und wie werden bekannte Probleme behoben?
    • usw.

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


    Alle Protokolle werden in STDOUT / STDERR geschrieben


    Der Dienst erstellt keine Dateien mit Protokollen im Betriebsmodus in der Produktion, sendet sie nicht an externe Dienste, enthält keine redundanten Abstraktionen für die Protokollrotation usw.

    Wenn eine Anwendung Dateien mit Protokollen erstellt, sind diese Protokolle unbrauchbar. Sie werden keine 5 parallel laufenden Container eingeben, in der Hoffnung, den erforderlichen Fehler zu finden (und Sie werden weinen ...). Ein Neustart des Containers führt zum vollständigen Verlust dieser Protokolle.


    Wenn eine Anwendung Protokolle in ein Drittsystem schreibt, z. B. in Logstash, führt dies zu unnötiger Redundanz. Der benachbarte Dienst weiß nicht, wie das geht, weil Hat es einen anderen Rahmen? Du bekommst einen Zoo.


    Die Anwendung schreibt einen Teil der Protokolle in die Dateien und einen Teil in den Standardauszug, da der Entwickler die INFO in der Konsole und DEBUG in den Dateien sehen kann. Dies ist im Allgemeinen die schlechteste Option. Niemand braucht die Komplexität und den zusätzlichen Code und die Konfiguration, die Sie kennen und pflegen müssen.


    Protokolle - das ist Json


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

    Bisher schreibt fast jeder Protokolle in Klartext. Das ist eine echte Katastrophe. Ich würde mich freuen, nie über Grok Patterns Bescheid zu wissen . Manchmal träume ich von ihnen und friere ein, versuche, mich nicht zu bewegen, um ihre Aufmerksamkeit nicht zu erregen. Versuchen Sie einfach einmal, Java-Ausnahmen in den Protokollen zu analysieren.


    Json ist ein Segen, es ist ein vom Himmel gegebenes Feuer. Einfach dort hinzufügen:


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

    Laden Sie auf ein beliebiges geeignetes System herunter (z. B. ordnungsgemäß konfiguriertes ElasticSearch) und genießen Sie es. Verbinden Sie die Protokolle mehrerer Mikrodienste und spüren Sie erneut, was gute monolithische Anwendungen waren.


    (Sie können auch eine Request-Id hinzufügen und die Rückverfolgung abrufen ...)


    Protokolle mit Ausführlichkeitsstufen


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

    Es ist wünschenswert, dass alle Dienste in demselben Ökosystem dieselbe Umgebungsvariable beibehalten. Keine Konfigurationsoption, keine Option in der Befehlszeile (obwohl diese natürlich umschlossen ist), sondern standardmäßig sofort aus der Umgebung. 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.


    Korrigierte Versionen von Abhängigkeiten


    Abhängigkeiten für Paketmanager sind festgelegt, einschließlich Nebenversionen (z. B. cool_framework = 2.5.3).

    Davon wurden natürlich viele erwähnt. Einige Korrekturabhängigkeiten für Hauptversionen, in der Hoffnung, dass es in Nebenversionen nur Fehlerkorrekturen und Sicherheitsupdates gibt. Das ist nicht richtig.
    Jede Änderung in jeder Abhängigkeit muss durch ein separates Commit angezeigt werden . Damit kann es bei Problemen abgebrochen werden. Ist es schwer mit den Händen zu kontrollieren? Es gibt nützliche Roboter wie diese , die die Updates nachverfolgen und Pull-Requests für jeden von ihnen erstellen.


    Verwirrt


    Das Repository enthält die serienreife Dockerfile und docker-compose.yml

    Docker ist für viele Unternehmen längst zum Standard geworden. Es gibt Ausnahmen, aber selbst wenn Sie keinen Docker in der Produktion haben, sollte jeder Ingenieur in der Lage sein, den Docker-Compose einfach zu erledigen und nicht an etwas anderes zu denken, um den Dev-Build für die lokale Überprüfung zu erhalten. Der Systemadministrator sollte bereits eine Baugruppe mit den erforderlichen Versionen von Bibliotheken, Dienstprogrammen usw. von Entwicklern geprüft haben, in der die Anwendung zumindest irgendwie funktioniert , um sie für die Produktion anzupassen.


    Konfiguration durch die Umgebung


    Alle wichtigen Konfigurationsoptionen werden aus der Umgebung gelesen und die Umgebung hat Vorrang vor Konfigurationsdateien (jedoch niedriger als die Befehlszeilenargumente beim Start).

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


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


    Bereitschafts- und Lebendigkeitsprüfungen


    Enthält die entsprechenden Endpunkt- oder CLI-Befehle, um die Bereitschaft zum Bereitstellen von Anforderungen beim Start und für die Betriebszeit zu testen.

    Wenn eine Anwendung HTTP-Anforderungen bedient, sollte sie standardmäßig zwei Schnittstellen haben:


    1. Um zu überprüfen, ob die Anwendung live ist und nicht hängen bleibt, verwenden Sie den Liveness-Test. Wenn die Anwendung nicht antwortet, kann sie automatisch von Orchestrierungsprogrammen wie Kubernetes gestoppt werden. " Dies ist jedoch nicht genau ." Wenn Sie eine gesperrte Anwendung töten, kann dies zu einem Dominoeffekt führen und Ihren Dienst dauerhaft einstellen. Dies ist jedoch kein Entwicklerproblem, sondern nur diesen Endpunkt.


    2. Um zu überprüfen, ob die Anwendung nicht nur gestartet, sondern bereit ist, Anforderungen zu akzeptieren, wird ein Readiness-Test durchgeführt. 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.



    Ressourcenbeschränkungen


    Enthält Beschränkungen für Arbeitsspeicher, CPU, Speicherplatz und andere verfügbare Ressourcen in einem konsistenten Format.

    Die konkrete Umsetzung dieses Elements wird in verschiedenen Organisationen und für verschiedene Orchestratoren sehr unterschiedlich sein. Diese Grenzwerte müssen jedoch für alle Dienste in einem einheitlichen Format festgelegt werden, für verschiedene Umgebungen (prod, dev, test, ...) unterschiedlich sein und sich außerhalb des Repositorys mit dem Anwendungscode befinden .


    Montage und Lieferung erfolgen automatisiert


    Das in Ihrer Organisation oder Ihrem Projekt verwendete CI / CD-System ist konfiguriert und kann die Anwendung entsprechend dem akzeptierten Workflow in der gewünschten Umgebung bereitstellen.

    Nichts wird jemals manuell an die Produktion geliefert.


    Egal wie schwierig es ist, die Montage und Lieferung Ihres Projekts zu automatisieren, dies muss geschehen, bevor dieses Projekt in Betrieb geht. Dieser Artikel umfasst das Erstellen und Ausführen von Ansible / Chefkochbüchern / Salt / ..., das Erstellen mobiler Anwendungen, das Erstellen von Betriebssystemgabeln, das Erstellen von Images für virtuelle Maschinen und was auch immer.
    Kann nicht automatisieren? Sie können es also nicht in die Welt führen. Nach dir wird es niemand sammeln.


    Anmutiges Herunterfahren - korrektes Herunterfahren


    Die Anwendung kann SIGTERM und andere Signale verarbeiten und nach der Bearbeitung der aktuellen Task systematisch ihre Arbeit unterbrechen.

    Dies ist ein äußerst wichtiger Punkt. Docker-Prozesse werden verwaist und arbeiten seit Monaten im Hintergrund, wo niemand sie sieht. Nicht-transaktionale Vorgänge werden mitten in der Ausführung abgebrochen, was zu Inkonsistenzen bei Daten zwischen Diensten und Datenbanken führt. Dies führt zu Fehlern, die nicht vorhersehbar sind und sehr, sehr teuer sein können.


    Wenn Sie keine Abhängigkeiten haben und nicht garantieren können, dass Ihr Code SIGTERM korrekt verarbeitet, verwenden Sie etwas wie dumb-init .


    Weitere Informationen hier:



    Datenbankverbindung regelmäßig überprüft


    Die Anwendung pingt ständig die Datenbank und reagiert automatisch auf die Ausnahme "Verbindung verloren" für alle Anforderungen. Sie versucht, sie selbst wiederherzustellen oder beendet ihre Arbeit ordnungsgemäß.

    Ich habe viele Fälle gesehen (es handelt sich nicht nur um eine Wendung), als Dienste, die für die Verarbeitung von Warteschlangen oder Ereignissen erstellt wurden, die Verbindung bis zum Timeout verloren haben und in den Protokollen endlos fehlerhaft waren, Nachrichten in der Warteschlange zurückschickten, sie in die Dead Letter-Warteschlange zurückschickten oder einfach ihre Arbeit nicht erledigten.


    Horizontal skaliert


    Wenn die Last wächst, 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 sind die Kafka-Verbraucher . Dies ist nicht unbedingt schlecht, aber wenn eine bestimmte Anwendung nicht zweimal gestartet werden kann, müssen alle Beteiligten dies im Voraus wissen. Diese Informationen sollten ein Schandfleck sein, hängen in der Readme-Datei und wo immer möglich. Einige Anwendungen können im Allgemeinen nicht unter allen Umständen parallel ausgeführt werden, was die Unterstützung erheblich erschwert.


    Es ist viel besser, wenn die Anwendung diese Situationen selbst steuert oder ein Wrapper für sie geschrieben wird, der die „Konkurrenten“ effektiv überwacht und einfach verhindert, dass der Prozess die Arbeit startet oder startet, bis ein anderer Prozess seine eigene abgeschlossen hat oder eine externe Konfiguration zulässt, dass N Prozesse gleichzeitig arbeiten.


    Warteschlangen mit toten Buchstaben und Widerstand gegen schlechte Nachrichten


    Wenn ein Dienst Warteschlangen abhört oder auf Ereignisse reagiert, führt das Ändern des Formats oder des Inhalts von Nachrichten nicht dazu, dass Nachrichten fallen. Nicht erfolgreiche Versuche, die Aufgabe zu bearbeiten, werden N-mal wiederholt, wonach die Nachricht an die Dead Letter Queue gesendet wird.

    Oft habe ich gesehen, wie endlos Verbraucher neu gestartet wurden, und die Warteschlangen waren so stark angeschwollen, dass die anschließende Verarbeitung mehrere Tage dauerte. Jeder Listener der Warteschlange sollte bereit sein, das Format zu ändern, auf zufällige Fehler in der Nachricht selbst (z. B. Eingabe von Daten in json) oder bei der Verarbeitung durch einen untergeordneten Code. Ich bin sogar auf eine Situation gestoßen, in der die Standardbibliothek für die Arbeit mit RabbitMQ für ein äußerst beliebtes Framework keine Wiederholungen, Zähler für Versuche usw. unterstützte.


    Schlimmer noch, die Nachricht wird im Fehlerfall einfach zerstört.


    Beschränken Sie die Anzahl der verarbeiteten Nachrichten und Aufgaben auf einen Prozess


    Es unterstützt eine Umgebungsvariable, die die maximale Anzahl der verarbeiteten Aufgaben begrenzen kann. Danach wird der Dienst ordnungsgemäß heruntergefahren.

    Alles fließt, alles verändert sich, besonders die Erinnerung. Der ständig wachsende Zeitplan für den Speicherverbrauch und OOM Killed ist die Lebensnorm des modernen kubernischen Geistes. 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 die Leute viel Zeit und Mühe (und Geld) aufwenden, um diesen Umsatz zu stoppen, aber es gibt keine Garantie, dass die nächste Verpflichtung Ihres Kollegen die Sache nicht verschlimmert. Wenn die Anwendung eine Woche überleben kann, ist dies ein hervorragender Indikator. Lass es sich dann einfach selbst beenden und neu starten. Dies ist besser als SIGKILL (über SIGTERM, siehe oben) oder die Ausnahme "Nicht genügend Speicher". Für einige Jahrzehnte reicht dieser Gag für Sie.


    Verwendet keine IP-Adressfilterintegration von Drittanbietern


    Wenn eine Anwendung Anforderungen an einen Dienst eines Drittanbieters stellt, der Anrufe von begrenzten IP-Adressen zulässt, führt der Dienst diese Anrufe indirekt über einen Reverse-Proxy aus.

    Dies ist ein seltener Fall, aber äußerst unangenehm. Es ist sehr unpraktisch, wenn ein kleiner Dienst die Möglichkeit blockiert, den Cluster zu ändern oder in eine andere Region der gesamten Infrastruktur zu wechseln. Wenn Sie mit jemandem kommunizieren möchten, der nicht weiß, wie oAuth oder VPN verwendet werden soll, konfigurieren Sie den Reverse-Proxy im Voraus . Implementieren Sie in Ihrem Programm nicht das dynamische Hinzufügen / Löschen ähnlicher externer Integrationen, da Sie sich damit an die einzig verfügbare Laufzeitumgebung nageln. Es ist besser, diese Prozesse sofort zu automatisieren, um Nginx-Konfigurationen zu verwalten und in Ihrer Anwendung darauf zu verweisen.


    Offensichtlicher HTTP-Benutzeragent


    Der Dienst ersetzt den User-Agent-Header durch einen benutzerdefinierten Header für alle Anforderungen an eine API. Dieser Header enthält genügend Informationen zum Service selbst und seiner 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 die Lizenz


    Enthält keine Abhängigkeiten, die die Anwendung unangemessen einschränken, keine Kopie des Codes einer anderen Person usw.

    Dies ist ein selbstverständlicher Fall, aber es ist möglich zu sehen, dass sogar 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 bereits veralteten Abhängigkeiten.

    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 entwickeln Sie die Bibliothek selbst.


    Fazit


    In meiner Liste gibt es einige spezifischere Prüfungen für bestimmte Technologien oder Situationen, und ich habe nur vergessen, etwas hinzuzufügen. Ich bin mir sicher, dass Sie auch etwas finden werden, an das Sie sich erinnern können.


    Jetzt auch beliebt: