Kapitel 4. Gesundheitsprobe
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Das Muster Health Probe zeigt, wie eine Anwendung ihren Gesundheitszustand an Kubernetes übermitteln kann. Um vollständig automatisierbar zu sein, muss eine Cloud Native-Anwendung in hohem Maße beobachtbar sein, indem ihr Zustand abgeleitet werden kann, damit Kubernetes erkennen kann, ob die Anwendung läuft und bereit ist, Anfragen zu bedienen. Diese Beobachtungen beeinflussen das Lebenszyklusmanagement von Pods und die Art und Weise, wie der Datenverkehr zur Anwendung geleitet wird.
Problem
Kubernetes überprüft regelmäßig den Status des Containerprozesses und startet ihn neu, wenn Probleme festgestellt werden. Aus der Praxis wissen wir jedoch, dass die Überprüfung des Prozessstatus nicht ausreicht, um den Zustand einer Anwendung zu bestimmen. In vielen Fällen hängt sich eine Anwendung auf, aber ihr Prozess läuft noch. Zum Beispiel kann eine Java-Anwendung eine OutOfMemoryError
auslösen und der JVM-Prozess läuft noch. Es kann aber auch sein, dass eine Anwendung einfriert, weil sie in eine Endlosschleife, ein Deadlock oder ein Thrashing (Cache, Heap, Prozess) gerät. Um solche Situationen zu erkennen, braucht Kubernetes eine zuverlässige Methode, um den Zustand von Anwendungen zu überprüfen - nicht um zu verstehen, wie eine Anwendung intern funktioniert, sondern um zu prüfen, ob die Anwendung wie erwartet funktioniert und in der Lage ist, die Verbraucher zu bedienen.
Lösung
Die Softwareindustrie hat die Tatsache akzeptiert, dass es nicht möglich ist, fehlerfreien Code zu schreiben. Außerdem steigt die Wahrscheinlichkeit von Fehlern noch mehr, wenn man mit verteilten Anwendungen arbeitet. Infolgedessen hat sich der Schwerpunkt im Umgang mit Fehlern von der Fehlervermeidung auf die Fehlererkennung und -behebung verlagert. Die Erkennung von Fehlern ist keine einfache Aufgabe, die für alle Anwendungen einheitlich durchgeführt werden kann, denn jeder hat eine andere Definition eines Fehlers. Außerdem erfordern verschiedene Arten von Fehlern unterschiedliche Abhilfemaßnahmen. Vorübergehende Ausfälle können sich mit genügend Zeit selbst beheben, während andere Ausfälle einen Neustart der Anwendung erfordern. Schauen wir uns die Prüfungen an, die Kubernetes zur Erkennung und Behebung von Fehlern durchführt.
Process Health Checks
Ein Prozess-Gesundheitscheck ist der einfachste Gesundheitscheck, den das Kubelet ständig für die Containerprozesse durchführt. Wenn die Containerprozesse nicht laufen, wird der Container auf dem Knoten, dem der Pod zugewiesen ist, neu gestartet. Auch ohne andere Gesundheitsprüfungen wird die Anwendung durch diese allgemeine Prüfung etwas robuster. Wenn deine Anwendung in der Lage ist, jede Art von Fehler zu erkennen und sich selbst herunterzufahren, brauchst du nur die Prozesszustandsprüfung. In den meisten Fällen reicht das jedoch nicht aus und es sind auch andere Arten von Gesundheitsprüfungen erforderlich.
Liveness-Proben
Wenn deine Anwendung in eine Sackgasse gerät, gilt sie aus Sicht des Process Health Checks immer noch als gesund. Um diese Art von Problem und andere Arten von Fehlern in der Geschäftslogik deiner Anwendung zu erkennen, verfügt Kubernetes über Liveness Probes - regelmäßigePrüfungen, die vom Kubelet-Agenten durchgeführt werden, der deinen Container fragt, ob er noch gesund ist. Es ist wichtig, dass die Gesundheitsprüfung von außen und nicht in der Anwendung selbst durchgeführt wird, da einige Ausfälle den Anwendungs-Watchdog daran hindern können, seinen Ausfall zu melden. Was die Abhilfemaßnahmen angeht, so ähnelt dieser Gesundheitscheck dem Gesundheitscheck von Prozessen, denn wenn ein Fehler entdeckt wird, wird der Container neu gestartet. Allerdings bietet sie mehr Flexibilität hinsichtlich der Methoden, die zur Überprüfung des Anwendungszustands verwendet werden können:
- HTTP-Sonde
-
Führt eine HTTP-GET-Anfrage an die IP-Adresse des Containers durch und erwartet einen erfolgreichen HTTP-Antwortcode zwischen 200 und 399.
- TCP Socket Probe
-
Setzt eine erfolgreiche TCP-Verbindung voraus.
- Exec-Sonde
-
Führt ein beliebiges Kommando im Benutzer- und Kernel-Namensraum des Containers aus und erwartet einen erfolgreichen Exit-Code (0).
- gRPC-Sonde
-
Nutzt die gRPC-eigene Unterstützung für Health Checks.
Neben der Prüfaktion kann das Verhalten der Gesundheitsprüfung mit den folgenden Parametern beeinflusst werden:
initialDelaySeconds
-
Gibt die Anzahl der Sekunden an, die gewartet werden soll, bis die erste Liveness Probe geprüft wird.
periodSeconds
-
Das Intervall in Sekunden zwischen den Liveness Probe Checks.
timeoutSeconds
-
Die maximale Zeit, die eine Prüfung zurückkehren darf, bevor sie als fehlgeschlagen betrachtet wird.
failureThreshold
-
Legt fest, wie oft ein Probecheck hintereinander fehlschlagen muss, bis der Container als ungesund eingestuft wird und neu gestartet werden muss.
Ein Beispiel für einen HTTP-basierten Liveness Probe ist in Beispiel 4-1 dargestellt.
Beispiel 4-1. Container mit einer Liveness Probe
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
pod-with-liveness-check
spec
:
containers
:
-
image
:
k8spatterns/random-generator:1.0
name
:
random-generator
env
:
-
name
:
DELAY_STARTUP
value
:
"
20
"
ports
:
-
containerPort
:
8080
protocol
:
TCP
livenessProbe
:
httpGet
:
path
:
/actuator/health
port
:
8080
initialDelaySeconds
:
30
Je nach Art deiner Anwendung kannst du die Methode wählen, die für dich am besten geeignet ist. Es liegt an deiner Anwendung zu entscheiden, ob sie sich für gesund hält oder nicht. Bedenke aber, dass ein nicht bestandener Gesundheitscheck dazu führt, dass dein Container neu gestartet wird. Wenn der Neustart deines Containers nicht hilft, hat eine fehlgeschlagene Gesundheitsprüfung keinen Vorteil, da Kubernetes deinen Container neu startet, ohne das Problem zu beheben.
Bereitschaftstests
Liveness Checks helfen dabei, Anwendungen gesund zu halten, indem sie ungesunde Container beenden und durch neue ersetzen. Aber manchmal, wenn ein Container nicht gesund ist, hilft es nicht, ihn neu zu starten. Ein typisches Beispiel ist ein Container, der noch nicht gestartet wurde und noch nicht bereit ist, Anfragen zu bearbeiten. Ein anderes Beispiel ist eine Anwendung, die noch darauf wartet, dass eine Abhängigkeit wie eine Datenbank verfügbar ist. Außerdem kann ein Container überlastet werden, wodurch sich seine Latenzzeit erhöht. Deshalb solltest du ihn für eine Weile von der zusätzlichen Last abschirmen und anzeigen, dass er nicht bereit ist, bis die Last abnimmt.
Für diese Art von Szenario gibt es in Kubernetes Readiness Probes. Die Methoden (HTTP, TCP, Exec, gRPC) und Zeitoptionen für die Durchführung von Readiness Checks sind dieselben wie bei Liveness Checks, aber die Korrekturmaßnahmen sind anders. Eine fehlgeschlagene Bereitschaftsprüfung führt nicht zu einem Neustart des Containers, sondern dazu, dass der Container vom Service-Endpunkt entfernt wird und keinen neuen Datenverkehr mehr erhält. Readiness Probes signalisieren, wann ein Container bereit ist, damit er etwas Zeit hat, sich aufzuwärmen, bevor er mit Anfragen vom Dienst konfrontiert wird. Sie ist auch nützlich, um den Container zu einem späteren Zeitpunkt vom Datenverkehr abzuschirmen, da Readiness Probes regelmäßig durchgeführt werden, ähnlich wie Liveness Checks. Beispiel 4-2 zeigt, wie eine Bereitschaftsprüfung implementiert werden kann, indem das Vorhandensein einer Datei geprüft wird, die die Anwendung erstellt, wenn sie betriebsbereit ist.
Beispiel 4-2. Container mit Bereitschaftssonde
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
pod-with-readiness-check
spec
:
containers
:
-
image
:
k8spatterns/random-generator:1.0
name
:
random-generator
readinessProbe
:
exec
:
command
:
[
"
stat
"
,
"
/var/run/random-generator-ready
"
]
Auch hier liegt es an deiner Implementierung der Zustandsprüfung zu entscheiden, wann deine Anwendung bereit ist, ihre Arbeit zu erledigen und wann sie in Ruhe gelassen werden sollte. Während die Process Health Checks und die Liveness Checks darauf abzielen, den Container nach einem Ausfall neu zu starten, verschafft der Readiness Check deiner Anwendung Zeit und erwartet, dass sie sich von selbst erholt. Bedenke, dass Kubernetes versucht, deinen Container daran zu hindern, neue Anfragen zu erhalten (wenn er z. B. heruntergefahren wird), unabhängig davon, ob die Bereitschaftsprüfung nach dem Empfang eines SIGTERM-Signals noch besteht.
In vielen Fällen führen Liveness- und Readiness-Probes dieselben Prüfungen durch. Das Vorhandensein einer Readiness Probe gibt deinem Container jedoch Zeit zum Starten. Nur wenn die Bereitschaftsprüfung bestanden wird, gilt ein Deployment als erfolgreich, so dass z. B. Pods mit einer älteren Version im Rahmen eines Rolling Updates beendet werden können.
Bei Anwendungen, die sehr lange für die Initialisierung brauchen, ist es wahrscheinlich, dass fehlgeschlagene Liveness Checks dazu führen, dass dein Container neu gestartet wird, bevor der Startvorgang abgeschlossen ist. Um diese unerwünschten Abschaltungen zu verhindern, kannst du Startup Probes verwenden, um anzuzeigen, wann der Startvorgang abgeschlossen ist.
Startup-Sonden
Liveness Probes können auch ausschließlich dazu verwendet werden, lange Startzeiten zu ermöglichen, indem die Prüfintervalle gestreckt, die Anzahl der Wiederholungen erhöht und eine längere Verzögerung für die erste Liveness Probe-Prüfung hinzugefügt wird. Diese Strategie ist jedoch nicht optimal, da diese Timing-Parameter auch für die Phase nach dem Start gelten und verhindern, dass deine Anwendung bei fatalen Fehlern schnell neu gestartet wird.
Wenn Anwendungen Minuten brauchen, um zu starten (z. B. Jakarta EE-Anwendungsserver), bietet Kubernetes Startup-Probes.
Startup-Probes werden mit dem gleichen Format wie Liveness-Probes konfiguriert, erlauben aber unterschiedliche Werte für die Probe-Aktion und die Timing-Parameter. Die Parameter periodSeconds
und failureThreshold
werden im Vergleich zu den entsprechenden Liveness-Probes mit viel größeren Werten konfiguriert, um den längeren Start der Anwendung zu berücksichtigen. Liveness- und Readiness-Probes werden erst aufgerufen, wenn die Startup-Probe Erfolg meldet. Der Container wird neu gestartet, wenn die Startup-Probe innerhalb der konfigurierten Fehlerschwelle nicht erfolgreich ist.
Während für Liveness- und Startup-Sonden dieselbe Aktion verwendet werden kann, wird ein erfolgreicher Start oft durch eine Marker-Datei angezeigt, deren Existenz von der Startup-Sonde überprüft wird.
Beispiel 4-4 ist ein typisches Beispiel für einen Jakarta EE-Anwendungsserver, der sehr lange zum Starten braucht.
Beispiel 4-4. Container mit einer Startup- und Liveness-Probe
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
pod-with-startup-check
spec
:
containers
:
-
image
:
quay.io/wildfly/wildfly
name
:
wildfly
startupProbe
:
exec
:
command
:
[
"
stat
"
,
"
/opt/jboss/wildfly/standalone/tmp/startup-marker
"
]
initialDelaySeconds
:
60
periodSeconds
:
60
failureThreshold
:
15
livenessProbe
:
httpGet
:
path
:
/health
port
:
9990
periodSeconds
:
10
failureThreshold
:
3
JBoss WildFly Jakarta EE Server, der seine Zeit braucht, um zu starten.
Markierungsdatei, die von WildFly nach einem erfolgreichen Start erstellt wird.
Zeitparameter, die festlegen, dass der Container neu gestartet werden soll, wenn er die Startprüfung nach 15 Minuten nicht bestanden hat (60 Sekunden Pause bis zur ersten Prüfung, dann maximal 15 Prüfungen in 60-Sekunden-Intervallen).
Die Timing-Parameter für die Liveness-Probes sind viel kleiner und führen zu einem Neustart, wenn die folgenden Liveness-Probes innerhalb von 20 Sekunden fehlschlagen (drei Wiederholungsversuche mit jeweils 10 Sekunden Pause dazwischen).
Die Liveness-, Readiness- und Startup-Probes sind grundlegende Bausteine für die Automatisierung von Cloud Native Applications. Anwendungsframeworks wie Quarkus SmallRye Health, Spring Boot Actuator, WildFly Swarm Health Check, Apache Karaf Health Check oder die MicroProfile-Spezifikation für Java bieten Implementierungen für das Angebot von Health Probes.
Diskussion
Um vollständig automatisierbar zu sein, müssen Cloud-native Anwendungen in hohem Maße beobachtbar sein, indem sie der Verwaltungsplattform die Möglichkeit geben, den Zustand der Anwendung zu lesen und zu interpretieren und ggf. Korrekturmaßnahmen zu ergreifen. Health Checks spielen eine grundlegende Rolle bei der Automatisierung von Aktivitäten wie Bereitstellung, Selbstheilung, Skalierung und anderen. Es gibt jedoch auch andere Möglichkeiten, wie deine Anwendung mehr Transparenz über ihren Zustand bieten kann.
Die naheliegendste und altbewährte Methode hierfür ist die Protokollierung. Es ist eine gute Praxis für Container, alle wichtigen Ereignisse zu Systemausfällen und Systemfehlern zu protokollieren und diese Protokolle an einer zentralen Stelle zur weiteren Analyse zu sammeln. Logs werden in der Regel nicht dazu verwendet, automatische Aktionen durchzuführen, sondern eher, um Warnungen auszulösen und weitere Untersuchungen durchzuführen. Ein nützlicherer Aspekt der Logs ist die Postmortem-Analyse von Ausfällen und die Erkennung von unbemerkten Fehlern.
Neben der Protokollierung in den Standard-Streams ist es auch eine gute Praxis, den Grund für das Beenden eines Containers in /dev/termination-log zu protokollieren. An diesem Ort kann der Container seinen letzten Willen kundtun, bevor er endgültig verschwindet.1 Abbildung 4-1 zeigt die möglichen Optionen, wie ein Container mit der Laufzeitplattform kommunizieren kann.
Container bieten eine einheitliche Möglichkeit, Anwendungen zu verpacken und auszuführen, indem sie wie undurchsichtige Systeme behandelt werden. Jeder Container, der ein "Cloud Native Citizen" werden will, muss jedoch APIs für die Laufzeitumgebung bereitstellen, um den Zustand des Containers zu überwachen und entsprechend zu handeln. Diese Unterstützung ist eine Grundvoraussetzung für eine einheitliche Automatisierung der Container-Updates und des Lebenszyklus, was wiederum die Widerstandsfähigkeit des Systems und die Nutzererfahrung verbessert. In der Praxis bedeutet das, dass deine containerisierte Anwendung mindestens APIs für die verschiedenen Arten von Health Checks (Liveness und Readiness) bereitstellen muss.
Anwendungen, die sich noch besser verhalten, müssen auch andere Möglichkeiten für die Verwaltungsplattform bieten, den Zustand der containerisierten Anwendung zu beobachten, indem sie mit Tracing- und Metrik-Bibliotheken wie OpenTracing oder Prometheus integriert werden. Behandle deine Anwendung als ein undurchsichtiges System, aber implementiere alle notwendigen APIs, damit die Plattform deine Anwendung bestmöglich beobachten und verwalten kann.
Beim nächsten Muster, Managed Lifecycle, geht es ebenfalls um die Kommunikation zwischen Anwendungen und der Kubernetes-Verwaltungsschicht, allerdings aus der anderen Richtung. Hier geht es darum, wie deine Anwendung über wichtige Pod-Lebenszyklus-Ereignisse informiert wird.
1 Alternativ kannst du das Feld .spec.containers.terminationMessagePolicy
eines Pods auf FallbackToLogsOnError
ändern. In diesem Fall wird die letzte Zeile des Protokolls für die Statusmeldung des Pods verwendet, wenn er beendet wird.
Get Kubernetes Patterns, 2. Auflage now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.