Kurze Ausfälle beim Deployment: Anfragen abschließen lassen

Die neue Version funktioniert, doch beim Austausch der Prozesse erhalten Clients Fehler. So stimmen Sie Bereitschaft, Routing und geordnetes Herunterfahren aufeinander ab.

Schwarze imgix-Serverracks mit Kabeln unter der Decke eines Rechenzentrums
Fotografie: imgix / Unsplash

Ein Deployment endet erfolgreich, doch währenddessen schlagen einige Clientanfragen fehl. Prüfen Sie bei der Fehlersuche auch, wie die alte Version herunterfährt. Ein Prozess kann enden, bevor er eine Antwort abschließt oder eine Routingänderung entlang des gesamten Anfragewegs wirksam wird. Geordnetes Herunterfahren, oft als Graceful Shutdown bezeichnet, gibt laufender Arbeit Zeit zum Abschluss. Die konkreten Beispiele verwenden Kubernetes und einen HTTP-Server in Node.js unter Linux.

Prüfen Sie, wann die neue Version Anfragen erhält

In Kubernetes prüft eine Readiness Probe, ob ein Container Anfragen annehmen kann. Konfigurieren Sie sie so, dass die neue Version erst nach der dafür nötigen Initialisierung besteht. Eine Startup Probe kann bei einem langsamen Start die anderen Prüfungen verzögern. Ein laufender Prozess allein liefert noch keine ausreichende Grundlage, um Clients dorthin zu schicken.

Prüfen Sie bei RollingUpdate maxUnavailable für die erlaubte Zahl nicht verfügbarer Pods und maxSurge für zusätzliche Pods. Der gleichzeitige Betrieb beider Versionen benötigt Kapazität. Diese Einstellungen gewährleisten für sich genommen jedoch keinen Abschluss bereits angenommener Anfragen. Erfassen Sie in der Deployment-Zeitleiste sowohl die Bereitschaft der neuen Version als auch das Herunterfahren der alten.

Trennen Sie die Routingänderung vom Prozessende

Kubernetes EndpointSlices unterscheiden die Zustände ready, serving und terminating. Das normale Routing überspringt in der Regel Endpoints, die beendet werden; wenn alle Endpoints beendet werden, gibt es eine Ausnahme. Verwenden Sie zusätzlich einen Ingress oder externen Load Balancer? Prüfen Sie dessen Verhalten beim Herunterfahren eines Backends. Eine einzelne Zustandsänderung belegt nicht, dass alle Schichten bereits keine neuen Anfragen mehr senden.

Lassen Sie laufende Antworten beim Herunterfahren abschließen, bevor Sie die dafür nötigen Ressourcen schließen. Erfassen Sie den Beginn des Herunterfahrens, die Zahl aktiver Anfragen und ihren Abschluss. Nimmt der Prozess weiterhin neue Arbeit an, prüfen Sie, welche Schicht sie sendet. Eine beliebig verlängerte Wartezeit erklärt diesen Unterschied nicht.

Geben Sie der Anwendung einen eigenen Ablauf zum Herunterfahren

Wenn Sie in Node.js unter Linux einen SIGTERM-Listener installieren, entfällt das standardmäßige Beenden bei diesem Signal. Die Anwendung muss deshalb auch den Abschluss des Herunterfahrens sicherstellen. Bei einem HTTP/1-Server in Node.js ab Version 19 nimmt server.close() keine neuen Verbindungen mehr an und schließt inaktive Verbindungen. Prüfen Sie den Abschluss über den Callback; der Methodenaufruf allein reicht dafür nicht.

Die Methode server.closeAllConnections() schließt auch aktive HTTP-Verbindungen gewaltsam. Auf WebSocket umgestellte Verbindungen beendet sie selbst nicht. Legen Sie für lange Antworten, WebSocket-Verbindungen und Hintergrundarbeit passende Abläufe mit einer festen Frist fest. Halten Sie den Datenbankpool offen, solange die abschließende Arbeit ihn noch benötigt. Unterscheiden Sie im Log zwischen Abschluss und Abbruch nach Ablauf der Frist.

Testen Sie das gesamte Zeitbudget unter Last

In Kubernetes läuft preStop vor dem Senden des Beendigungssignals und verbraucht dasselbe Zeitbudget terminationGracePeriodSeconds wie das Herunterfahren des Containers. Die Standardfrist beträgt 30 Sekunden; nach ihrem Ablauf kann SIGKILL folgen. Prüfen Sie auch ein eventuell eigenes Stoppsignal. Berücksichtigen Sie Hook, Abschluss der Anfragen und Schließen der Ressourcen gemeinsam.

Erzeugen Sie in einer getrennten Umgebung durchgehend Anfragen über denselben Weg wie Ihre Clients und führen Sie währenddessen ein Deployment aus. Testen Sie auch eine längere Antwort und einen langsameren Start. Messen Sie Fehler, unterbrochene Verbindungen und die Dauer des Herunterfahrens. Prüfen Sie anschließend denselben Ablauf beim Rückwechsel zur vorherigen Version. Vergleichen Sie den Erfolg des Deployment-Befehls mit dem Ergebnis der Clientanfragen.

  • Prüfen Sie die Bereitschaft der neuen Version vor der ersten Anfrage.
  • Prüfen Sie das Routing beim Herunterfahren des Backends.
  • Verfolgen Sie den Abschluss der Anfragen und das Schließen der Ressourcen.
  • Testen Sie Deployment und Rückwechsel unter durchgehender Last.

Was Sie mitnehmen

Vergleichen Sie bei kurzen Ausfällen während eines Deployments drei Zeitpunkte: die Bereitschaft der neuen Version, die Routingänderung und das Ende der alten Version. Testen Sie den Ablauf aus Sicht der Clients und legen Sie die Frist zum Herunterfahren anhand des gemessenen Verhaltens fest.

Dokumentation und weiterführende Links

Mgr. Martin Hlavaj, MBA

Softwareentwickler

Alle Artikel

Erfahren Sie frühzeitig von Ausfällen.

Fügen Sie Ihre Website oder API zu UpBot hinzu und bestimmen Sie, wer den Alarm erhält.

Kostenlos überwachen