Hinweis perev. : Wir präsentieren eine kleine Auswahl von Post-Mortem-Projekten zu den schwerwiegenden Problemen, mit denen Ingenieure verschiedener Unternehmen beim Betrieb der auf Kubernetes basierenden Infrastruktur konfrontiert waren. In jeder Notiz geht es um das Problem selbst, seine Ursachen und Folgen und natürlich um eine Lösung, mit der ähnliche Situationen in Zukunft vermieden werden können.
Wie Sie wissen, ist es günstiger, aus den Erfahrungen anderer zu lernen. Lassen Sie sich daher mit diesen Geschichten auf mögliche Überraschungen vorbereiten. Übrigens wird auf dieser Site eine große und regelmäßig aktualisierte Auswahl an Links zu solchen „Fehlergeschichten“ veröffentlicht (gemäß Daten aus diesem Git-Repository ). Nr. 1. Wie die Kernel-Panik eine Site zum Absturz brachte
Original: Mondschein .Zwischen dem 18. und 22. Januar kam es auf der Moonlight-Website und der API zu zeitweiligen Fehlfunktionen. Alles begann mit zufälligen API-Fehlern und endete mit einem vollständigen Herunterfahren. Die Probleme wurden behoben und die Anwendung normalisiert.
Allgemeine Informationen
Moonlight verwendet Software, die als Kubernetes bekannt ist. Kubernetes führt Anwendungen auf Servergruppen aus. Diese Server werden als Knoten bezeichnet. Kopien der auf dem Knoten ausgeführten Anwendung werden als Pods bezeichnet. Kubernetes verfügt über einen Scheduler, der dynamisch festlegt, welche Pods auf welchen Knoten funktionieren sollen.
Zeitleiste
Die ersten Fehler am Freitag waren auf Verbindungsprobleme mit der Redis-Datenbank zurückzuführen. Die Moonlight-API verwendet Redis, um Sitzungen für jede authentifizierte Anforderung zu überprüfen. Unser Überwachungstool Kubernetes hat mitgeteilt, dass einige Knoten und Pods nicht reagieren. Zur gleichen Zeit meldete Google Cloud eine
Störung der Netzwerkdienste und wir entschieden, dass diese die Ursache für unsere Probleme sind.
Mit abnehmendem Verkehr am Wochenende schienen die Fehler weitgehend behoben zu sein. Am Dienstagmorgen fiel jedoch die Website von Moonlight, und der externe Datenverkehr erreichte den Cluster überhaupt nicht. Wir haben
auf Twitter eine
andere Person mit ähnlichen Symptomen gefunden und festgestellt, dass beim Hosting von Google ein Netzwerkfehler aufgetreten ist. Wir haben uns an den Google Cloud-Support gewandt, der das Problem umgehend an das technische Support-Team weiterleitete.
Das technische Support-Team von Google hat einige Muster im Verhalten von Knoten in unserem Kubernetes-Cluster festgestellt. Die CPU-Auslastung der einzelnen Knoten erreichte 100%, woraufhin die Kernel-Panik in der virtuellen Maschine auftrat und sie abstürzte.
Gründe
Der Zyklus, der den Fehler verursachte, war wie folgt:
- Der Kubernetes-Scheduler hostete mehrere Pods mit hohem CPU-Verbrauch auf demselben Knoten.
- Die Pods haben alle CPU-Ressourcen auf dem Knoten verbraucht.
- Als nächstes kam die Kernel-Panik, die zu einer Ausfallzeit führte, in der der Knoten nicht auf den Scheduler reagierte.
- Der Scheduler verschob alle heruntergefallenen Pods auf einen neuen Knoten, und der Vorgang wurde wiederholt, was die allgemeine Situation verschärfte.
Anfangs trat der Fehler im Redis-Pod auf, aber am Ende fielen alle mit Verkehr arbeitenden Pods, was zu einem vollständigen Herunterfahren führte. Exponentielle Verzögerungen während der Neuplanung haben zu längeren Ausfallzeiten geführt.
Lösung
Wir konnten die Site wiederherstellen, indem wir allen wichtigen Bereitstellungen
Anti-Affinitätsregeln hinzufügten. Sie verteilen die Pods automatisch auf die Knoten, wodurch Fehlertoleranz und Leistung erhöht werden.
Kubernetes selbst ist als fehlertolerantes Hostsystem konzipiert. Moonlight verwendet drei Knoten auf verschiedenen Servern, um die Stabilität zu gewährleisten. Von jeder Anwendung, die den Datenverkehr bedient, werden drei Kopien ausgeführt. Die Idee ist, eine Kopie auf jedem Knoten zu haben. In diesem Fall führt selbst ein Ausfall von zwei Knoten nicht zu Ausfallzeiten. Kubernetes platzierte jedoch manchmal alle drei Pods mit der Site auf demselben Knoten und verursachte so einen Engpass im System. Zur gleichen Zeit befanden sich andere Anwendungen, die Prozessorleistung benötigten (nämlich serverseitiges Rendern), auf demselben Knoten und nicht auf einem separaten.
Ein ordnungsgemäß konfigurierter und funktionsfähiger Kubernetes-Cluster ist erforderlich, um lange Zeiträume mit hoher CPU-Auslastung zu bewältigen und die Pods so zu platzieren, dass die verfügbaren Ressourcen optimal genutzt werden. Wir arbeiten weiterhin mit dem Google Cloud-Support zusammen, um die Hauptursache der Kernel-Panik auf Servern zu identifizieren und zu beheben.
Fazit
Mit Anti-Affinitätsregeln können Sie Anwendungen, die mit externem Datenverkehr arbeiten, fehlertoleranter machen. Wenn Sie einen ähnlichen Service bei Kubernetes haben, sollten Sie ihn hinzufügen.
Wir arbeiten weiterhin mit den Mitarbeitern von Google zusammen, um die Fehlerursache im Betriebssystemkern auf den Knoten zu finden und zu beseitigen.
Nr. 2. Das "schmutzige" Geheimnis von Kubernetes und Ingress Endpoint
Original: Phil Pearl von Ravelin .Eleganz wird überbewertet
Wir bei Ravelin sind nach Kubernetes (auf GKE) gewandert. Der Prozess war sehr erfolgreich. Unsere Budgets für Pod-Unterbrechungen sind so voll wie immer, Statefuls sind wirklich stattlich
(ein schwer zu übersetzendes Wortspiel: "Unsere Statefulsets sind sehr stattlich" - ungefähr übersetzt) , und das gleitende Ersetzen von Knoten funktioniert wie am Schnürchen.
Der letzte Teil des Puzzles besteht darin, die API-Ebene von alten virtuellen Maschinen in den Kubernetes-Cluster zu verschieben. Dazu müssen wir Ingress so konfigurieren, dass die API von außen zugänglich ist.
Anfangs schien die Aufgabe einfach zu sein. Wir definieren nur den Ingress-Controller, optimieren die Terraform, um eine bestimmte Anzahl von IP-Adressen zu erhalten, und Google kümmert sich um fast alles andere. Und das alles wird wie von Zauberhand funktionieren. Klasse!
Mit der Zeit bemerkten sie jedoch, dass Integrationstests in regelmäßigen Abständen Fehler 502 erhielten. Daraufhin begann unsere Reise. Ich werde Ihnen jedoch Zeit sparen und gleich zu den Schlussfolgerungen kommen.
Anmutiges Herunterfahren
Alle sprechen von einem würdevollen Herunterfahren ("würdevolles", schrittweises Herunterfahren). Aber auf ihn sollte man sich bei Kubernetes wirklich nicht verlassen. Zumindest sollte es nicht das anmutige Herunterfahren sein, das Sie
mit der Milch Ihrer Mutter aufgenommen haben . In der Welt der Kubernetes ist dieses Maß an "Eleganz" unnötig und droht mit ernsthaften Problemen.
Perfekte Welt
In den meisten Fällen wird der Pod folgendermaßen aus dem Dienst oder Load Balancer in Kubernetes entfernt:
- Der Replikationscontroller beschließt, den Pod zu entfernen.
- Der Endpunkt-Pod wird aus dem Dienst oder Load Balancer entfernt. Es kommt kein neuer Verkehr mehr zum Pod.
- Ein Pre-Stop-Hook wird aufgerufen oder der Pod empfängt ein SIGTERM-Signal.
- Pod "anmutig" ist nicht verbunden. Eingehende Verbindungen werden nicht mehr akzeptiert.
- Die "ordnungsgemäße" Trennung ist abgeschlossen, und der Pod wird zerstört, nachdem alle vorhandenen Verbindungen gestoppt oder beendet wurden.
Leider sieht die Realität ganz anders aus.
Reale Welt
Die meisten Dokumentationen weisen darauf hin, dass alles ein wenig anders abläuft, aber sie schreiben nirgendwo explizit darüber. Das Hauptproblem ist, dass Schritt 3 nicht auf Schritt 2 folgt. Sie treten gleichzeitig auf. Bei normalen Diensten werden die Endpunkte so schnell entfernt, dass die Wahrscheinlichkeit von Problemen äußerst gering ist. Bei Ingress ist jedoch alles anders: Sie reagieren in der Regel viel langsamer, sodass das Problem offensichtlich wird. Der Pod kann SIGTERM erhalten, lange bevor Änderungen an Endpunkten in Ingress eintreten.
Infolgedessen ist ein ordnungsgemäßes Herunterfahren überhaupt nicht das, was von einem Pod verlangt wird. Er wird neue Verbindungen erhalten und diese weiter verarbeiten müssen, andernfalls werden die Clients die 500. Fehler erhalten und die ganze wunderbare Geschichte über unkomplizierte Bereitstellungen und Skalierungen wird auseinanderfallen.
Folgendes passiert tatsächlich:
- Der Replikationscontroller beschließt, den Pod zu entfernen.
- Der Endpunkt-Pod wird aus dem Dienst oder Load Balancer entfernt. Bei Ingress kann dies einige Zeit dauern, und es fließt weiterhin neuer Verkehr in den Pod.
- Ein Pre-Stop-Hook wird aufgerufen oder der Pod empfängt ein SIGTERM-Signal.
- Der Pod muss dies weitgehend ignorieren, weiterarbeiten und neue Verbindungen aufrechterhalten. Wenn möglich, sollte er Kunden darauf hinweisen, dass es schön wäre, an einen anderen Ort zu wechseln. Im Falle von HTTP kann beispielsweise
Connection: close
in den Antwort-Headern gesendet werden. - Der Pod wird nur beendet, wenn die Wartezeit „elegant“ abgelaufen ist und er von SIGKILL getötet wird.
- Stellen Sie sicher, dass dieser Zeitraum länger ist als die Zeit, die für die Neuprogrammierung des Load Balancers erforderlich ist.
Wenn es sich um Code von Drittanbietern handelt und Sie das Verhalten nicht ändern können, können Sie am besten einen Pre-Stop-Hook hinzufügen, der nur für eine „elegante“ Zeitspanne ruht, sodass der Pod weiterhin wie nichts funktioniert passiert ist.
Nummer 3. Wie ein einfacher Webhook einen Clusterausfall verursachte
Im Original: Jetstack .Jetstack bietet seinen Kunden mandantenfähige Plattformen auf Kubernetes an. Manchmal gibt es spezielle Anforderungen, die wir mit der Standard-Kubernetes-Konfiguration nicht erfüllen können. Um diese zu implementieren, haben wir kürzlich begonnen, den
Open Policy Agent (über das Projekt haben wir in diesem Test ausführlicher berichtet - ca. übersetzt) als Zugriffskontroller für die Implementierung spezieller Richtlinien zu verwenden.
Dieser Artikel beschreibt den Fehler, der durch die nicht ordnungsgemäß konfigurierte Integration verursacht wird.
Zwischenfall
Wir haben den Assistenten für den Entwicklercluster aktualisiert, in dem verschiedene Teams ihre Anwendungen während des Arbeitstages getestet haben. Es war ein regionaler Cluster in der Zone Europa-West1 der Google Kubernetes Engine (GKE).
Befehle wurden gewarnt, dass ein Update ausgeführt wird und keine Ausfallzeiten zu erwarten sind. Wir haben an diesem Tag bereits ein ähnliches Update für eine andere Vorproduktionsumgebung durchgeführt.
Wir haben das Upgrade mit unserer GKE Terraform-Pipeline gestartet. Das Update des Assistenten wurde erst nach Ablauf des Terraform-Timeouts abgeschlossen, das wir für 20 Minuten festgelegt haben. Dies war der erste Weckruf, bei dem ein Fehler aufgetreten ist, obwohl der Cluster in der GKE-Konsole immer noch als "Upgrade" aufgeführt war.
Ein Neustart der Pipeline führte zu folgendem Fehler
google_container_cluster.cluster: Error waiting for updating GKE master version: All cluster resources were brought up, but the cluster API is reporting that: component "kube-apiserver" from endpoint "gke-..." is unhealthy
Diesmal wurde die Verbindung zum API-Server regelmäßig unterbrochen und die Teams konnten ihre Anwendungen nicht bereitstellen.
Während wir versuchten zu verstehen, was vor sich ging, wurden alle Knoten in einem endlosen Zyklus zerstört und neu erstellt. Dies hat zu einem wahllosen Denial-of-Service für alle unsere Kunden geführt.
Wir ermitteln die Fehlerursache
Mit der Unterstützung von Google konnten wir die Reihenfolge der Ereignisse ermitteln, die zu dem Fehler geführt haben:
- GKE schloss das Upgrade auf einer Instanz des Assistenten ab und begann, den gesamten Datenverkehr zum API-Server darauf zu akzeptieren, wenn die nächsten Assistenten aktualisiert wurden.
- Während des Upgrades der zweiten Instanz des Assistenten konnte der API-Server PostStartHook nicht ausführen, um die Zertifizierungsstelle zu registrieren.
- Während der Ausführung dieses Hooks hat der API-Server versucht, ConfigMap mit dem Namen
extension-apiserver-authentication
in kube-system
zu aktualisieren. Dies war nicht möglich, da das von uns konfigurierte Back-End für den Open Policy Agent (OPA) -Überprüfungswebhook nicht reagierte. - Damit der Assistent eine Integritätsprüfung besteht, muss dieser Vorgang erfolgreich abgeschlossen werden. Da dies nicht geschah, trat der zweite Master in den Notfallzyklus ein und stoppte das Update.
Das Ergebnis waren regelmäßige API-Abstürze, aufgrund derer Kubelets nicht in der Lage waren, den Zustand des Knotens zu melden. Dies führte wiederum dazu, dass der Mechanismus zur automatischen Wiederherstellung von GKE-Knoten
(Node Auto Repair) begann, die Knoten neu zu starten. Diese Funktion wird in der
Dokumentation ausführlich beschrieben:
Ein fehlerhafter Status kann bedeuten: Innerhalb einer bestimmten Zeit (ca. 10 Minuten) gibt der Knoten überhaupt keinen Status aus.
Lösung
Als wir herausfanden, dass die
ValidatingAdmissionWebhook
Ressource einen zeitweiligen Zugriff auf den API-Server verursachte, löschten wir sie und stellten die Funktionsfähigkeit des Clusters wieder her.
Seitdem wurde
ValidatingAdmissionWebhook
for OPA so konfiguriert, dass nur die Namespaces überwacht werden, auf die die Richtlinie anwendbar ist und auf die die Entwicklungsteams Zugriff haben. Wir haben den Webhook auch auf
Ingress
und
Service
, die einzigen, mit denen unsere Richtlinie funktioniert.
Seit wir das OPA zum ersten Mal bereitgestellt haben, wurde die Dokumentation
aktualisiert , um diese Änderung widerzuspiegeln.
Wir haben auch einen Verfügbarkeitstest hinzugefügt, um sicherzustellen, dass der OPA neu gestartet wird, falls er nicht mehr verfügbar ist (und die Dokumentation
entsprechend geändert ).
Wir haben auch überlegt,
den automatischen Wiederherstellungsmechanismus für GKE-Knoten zu deaktivieren, haben uns aber dennoch entschlossen, diese Idee aufzugeben.
Zusammenfassung
Wenn wir die API-Server-Antwortzeitwarnungen aktivieren, können wir nach der Bereitstellung des OPA-Webhooks zunächst die globale Erhöhung für alle
CREATE
und
UPDATE
Anforderungen feststellen.
Dies unterstreicht, wie wichtig es ist, Tests für alle Workloads einzurichten. Rückblickend können wir sagen, dass der Einsatz von OPA so trügerisch einfach war, dass wir uns nicht einmal auf das
Helm-Diagramm einließen (obwohl dies so sein sollte). Die Tabelle enthält eine Reihe von Anpassungen, die über die im Handbuch beschriebenen Grundeinstellungen hinausgehen, einschließlich der Einstellung "livenessProbe" für Container mit einem Zugangscontroller.
Wir waren nicht die Ersten, die auf dieses Problem gestoßen sind: Die
vorgelagerte Frage bleibt offen. Die Funktionalität in dieser Angelegenheit kann eindeutig verbessert werden (und wir werden dies weiter verfolgen).
PS vom Übersetzer
Lesen Sie auch in unserem Blog: