Best Practices für Kubernetes-Container: Gesundheitsprüfungen

Best Practices für Kubernetes-Container: Gesundheitsprüfungen

TL; DR

  • Um eine hohe Beobachtbarkeit von Containern und Microservices zu erreichen, reichen Protokolle und primäre Metriken nicht aus.
  • Für eine schnellere Wiederherstellung und erhöhte Ausfallsicherheit sollten Anwendungen das High Observability Principle (HOP) anwenden.
  • Auf Anwendungsebene erfordert NOP: ordnungsgemäße Protokollierung, genaue Überwachung, Integritätsprüfungen und Leistungs-/Übergangsverfolgung.
  • Verwenden Sie Schecks als Element von NOR Bereitschaftsprobe и Lebendigkeit Probe Kubernetes.

Was ist eine Gesundheitscheck-Vorlage?

Beim Entwerfen einer geschäftskritischen und hochverfügbaren Anwendung ist es sehr wichtig, über einen Aspekt wie Fehlertoleranz nachzudenken. Eine Anwendung gilt als fehlertolerant, wenn sie sich nach einem Ausfall schnell erholt. Eine typische Cloud-Anwendung verwendet eine Microservices-Architektur, bei der jede Komponente in einem separaten Container platziert wird. Und um sicherzustellen, dass die Anwendung auf k8s beim Entwerfen eines Clusters hochverfügbar ist, müssen Sie bestimmten Mustern folgen. Darunter befindet sich auch die Health-Check-Vorlage. Es definiert, wie die Anwendung k8s mitteilt, dass sie fehlerfrei ist. Dabei handelt es sich nicht nur um Informationen darüber, ob der Pod läuft, sondern auch darüber, wie er Anfragen empfängt und darauf reagiert. Je mehr Kubernetes über den Zustand des Pods weiß, desto intelligentere Entscheidungen trifft es über die Weiterleitung des Datenverkehrs und den Lastausgleich. Somit ermöglicht das Prinzip der hohen Beobachtbarkeit der Anwendung, zeitnah auf Anfragen zu reagieren.

High-Observability-Prinzip (HOP)

Das Prinzip der hohen Beobachtbarkeit ist eines davon Prinzipien für den Entwurf von Containeranwendungen. In einer Microservices-Architektur ist es den Diensten egal, wie ihre Anfrage verarbeitet wird (und das zu Recht), sondern es kommt darauf an, wie sie Antworten von den empfangenden Diensten erhalten. Um beispielsweise einen Benutzer zu authentifizieren, sendet ein Container eine HTTP-Anfrage an einen anderen und erwartet eine Antwort in einem bestimmten Format – das ist alles. PythonJS kann die Anfrage auch verarbeiten und Python Flask kann antworten. Container sind wie Blackboxen, deren Inhalt untereinander verborgen ist. Das NOP-Prinzip erfordert jedoch, dass jeder Dienst mehrere API-Endpunkte offenlegt, die anzeigen, wie gesund er ist, sowie seine Bereitschaft und seinen Fehlertoleranzstatus. Kubernetes fordert diese Indikatoren an, um die nächsten Schritte für Routing und Load Balancing zu durchdenken.

Eine gut gestaltete Cloud-Anwendung protokolliert ihre wichtigsten Ereignisse mithilfe der Standard-E/A-Streams STDERR und STDOUT. Als nächstes kommt ein Hilfsdienst, zum Beispiel Filebeat, Logstash oder Fluentd, der Protokolle an ein zentrales Überwachungssystem (zum Beispiel Prometheus) und ein Protokollsammelsystem (ELK-Software-Suite) liefert. Das folgende Diagramm zeigt, wie eine Cloud-Anwendung gemäß dem Health Test Pattern und dem High Observability Principle funktioniert.

Best Practices für Kubernetes-Container: Gesundheitsprüfungen

Wie wende ich das Health Check Pattern in Kubernetes an?

Standardmäßig überwacht k8s den Status der Pods mithilfe eines der Controller (Deployments, Replikationssets, DaemonSets, StatefulSets usw. usw.). Nachdem der Controller festgestellt hat, dass der Pod aus irgendeinem Grund heruntergefallen ist, versucht er, ihn neu zu starten oder auf einen anderen Knoten zu verschieben. Ein Pod kann jedoch melden, dass er betriebsbereit ist, aber er selbst funktioniert nicht. Geben wir ein Beispiel: Ihre Anwendung verwendet Apache als Webserver, Sie haben die Komponente auf mehreren Pods des Clusters installiert. Da die Bibliothek falsch konfiguriert wurde, antworten alle Anfragen an die Anwendung mit dem Code 500 (interner Serverfehler). Bei der Überprüfung der Lieferung führt die Überprüfung des Status der Pods zu einem erfolgreichen Ergebnis, aber die Kunden denken anders. Wir werden diese unerwünschte Situation wie folgt beschreiben:

Best Practices für Kubernetes-Container: Gesundheitsprüfungen

In unserem Beispiel ist dies bei k8s der Fall Funktionsprüfung. Bei dieser Art der Überprüfung überprüft das Kubelet kontinuierlich den Status des Prozesses im Container. Sobald er versteht, dass der Prozess gestoppt wurde, wird er ihn neu starten. Wenn der Fehler durch einen einfachen Neustart der Anwendung behoben werden kann und das Programm so konzipiert ist, dass es bei jedem Fehler herunterfährt, ist eine Prozessgesundheitsprüfung alles, was Sie brauchen, um dem NOP und dem Gesundheitstestmuster zu folgen. Schade nur, dass nicht alle Fehler durch einen Neustart behoben werden. In diesem Fall bietet k8s zwei tiefergehende Möglichkeiten, Probleme mit dem Pod zu identifizieren: Lebendigkeit Probe и Bereitschaftsprobe.

LivenessProbe

Es ist Zeit Lebendigkeit Probe Kubelet führt drei Arten von Prüfungen durch: Es stellt nicht nur fest, ob der Pod ausgeführt wird, sondern auch, ob er bereit ist, Anfragen zu empfangen und angemessen darauf zu reagieren:

  • Richten Sie eine HTTP-Anfrage an den Pod ein. Die Antwort muss einen HTTP-Antwortcode im Bereich von 200 bis 399 enthalten. Die Codes 5xx und 4xx signalisieren also, dass der Pod Probleme hat, obwohl der Prozess läuft.
  • Um Pods mit Nicht-HTTP-Diensten (z. B. dem Postfix-Mailserver) zu testen, müssen Sie eine TCP-Verbindung herstellen.
  • Einen beliebigen Befehl für einen Pod ausführen (intern). Die Prüfung gilt als erfolgreich, wenn der Befehlsabschlusscode 0 ist.

Ein Beispiel dafür, wie das funktioniert. Die nächste Pod-Definition enthält eine NodeJS-Anwendung, die bei HTTP-Anfragen einen Fehler 500 auslöst. Um sicherzustellen, dass der Container neu gestartet wird, wenn ein solcher Fehler empfangen wird, verwenden wir den Parameter livenessProbe:

apiVersion: v1
kind: Pod
metadata:
 name: node500
spec:
 containers:
   - image: magalix/node500
     name: node500
     ports:
       - containerPort: 3000
         protocol: TCP
     livenessProbe:
       httpGet:
         path: /
         port: 3000
       initialDelaySeconds: 5

Dies unterscheidet sich nicht von jeder anderen Pod-Definition, wir fügen jedoch ein Objekt hinzu .spec.containers.livenessProbe. Parameter httpGet akzeptiert den Pfad, an den die HTTP-GET-Anfrage gesendet wird (in unserem Beispiel ist dies /, aber in Kampfszenarien kann es so etwas geben /api/v1/status). Eine andere livenessProbe akzeptiert einen Parameter initialDelaySeconds, der den Verifizierungsvorgang anweist, eine bestimmte Anzahl von Sekunden zu warten. Die Verzögerung ist erforderlich, da der Container Zeit zum Starten benötigt und nach einem Neustart für einige Zeit nicht verfügbar ist.

Um diese Einstellung auf einen Cluster anzuwenden, verwenden Sie:

kubectl apply -f pod.yaml

Nach einigen Sekunden können Sie den Inhalt des Pods mit dem folgenden Befehl überprüfen:

kubectl describe pods node500

Am Ende der Ausgabe finden Sie das ist was.

Wie Sie sehen können, hat livenessProbe eine HTTP-GET-Anfrage initiiert, der Container hat einen Fehler 500 generiert (wofür er programmiert war) und das Kubelet hat ihn neu gestartet.

Wenn Sie sich fragen, wie die NideJS-Anwendung programmiert wurde, finden Sie hier die verwendeten app.js- und Docker-Dateien:

app.js.

var http = require('http');

var server = http.createServer(function(req, res) {
    res.writeHead(500, { "Content-type": "text/plain" });
    res.end("We have run into an errorn");
});

server.listen(3000, function() {
    console.log('Server is running at 3000')
})

Dockerfile

FROM node
COPY app.js /
EXPOSE 3000
ENTRYPOINT [ "node","/app.js" ]

Es ist wichtig zu beachten: livenessProbe startet den Container nur dann neu, wenn er fehlschlägt. Wenn ein Neustart den Fehler, der die Ausführung des Containers verhindert, nicht behebt, kann das Kubelet keine Maßnahmen zur Behebung des Problems ergreifen.

Bereitschaftsprobe

readinessProbe funktioniert ähnlich wie livenessProbes (GET-Anfragen, TCP-Kommunikation und Befehlsausführung), mit Ausnahme von Fehlerbehebungsaktionen. Der Container, in dem der Fehler erkannt wird, wird nicht neu gestartet, sondern vom eingehenden Datenverkehr isoliert. Stellen Sie sich vor, dass einer der Container viele Berechnungen durchführt oder stark ausgelastet ist, wodurch sich die Antwortzeiten verlängern. Im Fall von livenessProbe wird die Antwortverfügbarkeitsprüfung ausgelöst (über den Prüfparameter timeoutSeconds), woraufhin das Kubelet den Container neu startet. Beim Start beginnt der Container mit der Ausführung ressourcenintensiver Aufgaben und wird erneut gestartet. Dies kann für Anwendungen, die Reaktionsgeschwindigkeit erfordern, von entscheidender Bedeutung sein. Wenn beispielsweise ein Auto unterwegs auf eine Antwort vom Server wartet, verzögert sich die Antwort – und das Auto gerät in einen Unfall.

Schreiben wir eine redinessProbe-Definition, die die Antwortzeit der GET-Anfrage auf nicht mehr als zwei Sekunden festlegt und die Anwendung nach 5 Sekunden auf die GET-Anfrage antwortet. Die pod.yaml-Datei sollte so aussehen:

apiVersion: v1
kind: Pod
metadata:
 name: nodedelayed
spec:
 containers:
   - image: afakharany/node_delayed
     name: nodedelayed
     ports:
       - containerPort: 3000
         protocol: TCP
     readinessProbe:
       httpGet:
         path: /
         port: 3000
       timeoutSeconds: 2

Lassen Sie uns einen Pod mit kubectl bereitstellen:

kubectl apply -f pod.yaml

Warten wir ein paar Sekunden und sehen uns dann an, wie die readinessProbe funktioniert:

kubectl describe pods nodedelayed

Am Ende der Ausgabe können Sie sehen, dass einige der Ereignisse ähnlich sind Dieses hier.

Wie Sie sehen, hat kubectl den Pod nicht neu gestartet, als die Prüfzeit 2 Sekunden überschritt. Stattdessen stornierte er die Anfrage. Eingehende Kommunikation wird an andere, funktionierende Pods umgeleitet.

Beachten Sie, dass kubectl nach dem Auslagern des Pods erneut Anfragen an ihn weiterleitet: Antworten auf GET-Anfragen werden nicht mehr verzögert.

Zum Vergleich finden Sie unten die geänderte Datei app.js:

var http = require('http');

var server = http.createServer(function(req, res) {
   const sleep = (milliseconds) => {
       return new Promise(resolve => setTimeout(resolve, milliseconds))
   }
   sleep(5000).then(() => {
       res.writeHead(200, { "Content-type": "text/plain" });
       res.end("Hellon");
   })
});

server.listen(3000, function() {
   console.log('Server is running at 3000')
})

TL; DR
Vor dem Aufkommen von Cloud-Anwendungen waren Protokolle das wichtigste Mittel zur Überwachung und Überprüfung des Anwendungszustands. Es bestand jedoch keine Möglichkeit, Korrekturmaßnahmen zu ergreifen. Protokolle sind auch heute noch nützlich; sie müssen gesammelt und an ein Protokollerfassungssystem gesendet werden, um Notfallsituationen zu analysieren und Entscheidungen zu treffen. [All dies ließe sich ohne Cloud-Anwendungen beispielsweise mit Monit bewerkstelligen, aber mit k8s wurde es viel einfacher :) – Anmerkung der Redaktion. ]

Korrekturen müssen heute nahezu in Echtzeit vorgenommen werden, sodass Anwendungen keine Blackbox mehr sein müssen. Nein, sie sollten Endpunkte anzeigen, die es Überwachungssystemen ermöglichen, wertvolle Daten über den Status von Prozessen abzufragen und zu sammeln, damit sie bei Bedarf sofort reagieren können. Dies wird als Performance Test Design Pattern bezeichnet und folgt dem High Observability Principle (HOP).

Kubernetes bietet standardmäßig zwei Arten von Gesundheitsprüfungen: readinessProbe und livenessProbe. Beide verwenden die gleichen Prüfarten (HTTP-GET-Anfragen, TCP-Kommunikation und Befehlsausführung). Sie unterscheiden sich darin, welche Entscheidungen sie als Reaktion auf Probleme in den Gruppen treffen. livenessProbe startet den Container neu in der Hoffnung, dass der Fehler nicht erneut auftritt, und readinessProbe isoliert den Pod vom eingehenden Datenverkehr, bis die Ursache des Problems behoben ist.

Ein ordnungsgemäßes Anwendungsdesign sollte beide Arten der Überprüfung umfassen und sicherstellen, dass genügend Daten erfasst werden, insbesondere wenn eine Ausnahme ausgelöst wird. Es sollte auch die notwendigen API-Endpunkte anzeigen, die das Überwachungssystem (Prometheus) mit wichtigen Gesundheitsmetriken versorgen.

Source: habr.com

Kommentar hinzufügen