Zum Hauptinhalt springen

Simulation von Uhrzeitabweichungen in K8s ohne Beeinträchtigung anderer Container auf dem Node

· 10 Minuten Lesezeit
Cwen Yin
Maintainer of Chaos Mesh
Inoffizielle Beta-Übersetzung

Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →

Uhrzeitsynchronisation in verteilten Systemen
Uhrzeitsynchronisation in verteilten Systemen

Chaos Mesh, eine benutzerfreundliche, quelloffene Cloud-native Chaos-Engineering-Plattform für Kubernetes (K8s), bietet eine neue Funktion: TimeChaos. Diese simuliert das Phänomen der Uhrzeitabweichung. Normalerweise möchten wir bei der Änderung von Systemuhren in Containern einen minimalen Auswirkungsradius erreichen und vermeiden, dass andere Container auf dem Node beeinflusst werden. In der Praxis stellt sich dies jedoch oft komplexer dar als erwartet. Wie löst Chaos Mesh dieses Problem?

In diesem Beitrag beschreibe ich, wie wir verschiedene Ansätze zur Simulation von Uhrzeitabweichungen untersucht haben und wie TimeChaos in Chaos Mesh ermöglicht, die Zeit in Containern gezielt zu manipulieren.

Simulation von Uhrzeitabweichungen ohne Beeinträchtigung anderer Container auf dem Node

Unter Uhrzeitabweichung versteht man die Differenz zwischen Systemuhren verschiedener Knoten innerhalb eines Netzwerks. Dies kann in verteilten Systemen zu Zuverlässigkeitsproblemen führen und stellt eine Herausforderung für Entwickler komplexer verteilter Systeme dar. In einer verteilten SQL-Datenbank ist beispielsweise die Synchronisation lokaler Uhren entscheidend, um konsistente globale Snapshots zu gewährleisten und ACID-Eigenschaften für Transaktionen sicherzustellen.

Zwar existieren etablierte Lösungen zur Uhrensynchronisation, aber ohne angemessene Tests lässt sich nie sicherstellen, dass die Implementierung robust ist.

Wie lässt sich also die Konsistenz globaler Snapshots in verteilten Systemen testen? Die Antwort liegt nahe: Durch Simulation von Uhrzeitabweichungen können wir prüfen, ob verteilte Systeme unter abnormalen Zeitbedingungen konsistente Snapshots aufrechterhalten. Einige Testtools unterstützen zwar die Simulation von Uhrabweichungen in Containern, beeinflussen dabei jedoch physische Knoten.

TimeChaos ist ein Werkzeug, das Uhrzeitabweichungen in Containern simuliert, um deren Auswirkungen auf Anwendungen zu testen, ohne den gesamten Node zu beeinträchtigen. So lassen sich potenzielle Konsequenzen präzise identifizieren und entsprechende Maßnahmen ergreifen.

Verschiedene Ansätze zur Simulation von Uhrzeitabweichungen

Bei der Betrachtung bestehender Optionen wird deutlich, dass diese nicht für Chaos Mesh im Kubernetes-Umfeld geeignet sind. Zwei gängige Methoden - die direkte Änderung der Node-Uhrzeit und das Jepsen-Framework - verändern die Zeit für alle Prozesse auf dem Node. In Kubernetes-Containern wäre ein solcher Ansatz inakzeptabel, da er andere Container auf demselben Node stören würde. Eine derart grobe Vorgehensweise kommt nicht in Frage.

Wie lösen wir dieses Problem? Naheliegend ist die Suche nach Lösungen auf Kernel-Ebene mittels Berkeley Packet Filter (BPF).

LD_PRELOAD

LD_PRELOAD ist eine Linux-Umgebungsvariable, die festlegt, welche dynamische Linkbibliothek vor der Programmausführung geladen wird.

Diese Variable bietet zwei Vorteile:

  • Eigene Funktionen können ohne Kenntnis des Quellcodes aufgerufen werden.

  • Code kann in andere Programme injiziert werden, um spezifische Ziele zu erreichen.

Bei einigen Sprachen, die Anwendungen verwenden, um die Zeitfunktion in glibc aufzurufen, wie Rust und C, reicht LD_PRELOAD aus, um Uhrzeitabweichungen zu simulieren. Für Golang gestaltet sich die Sache jedoch kniffliger. Da Sprachen wie Golang direkt das virtuelle Dynamic Shared Object (vDSO) parsen – einen Mechanismus zur Beschleunigung von Systemaufrufen. Um die Adresse der Zeitfunktion zu erhalten, können wir nicht einfach LD_PRELOAD verwenden, um die glibc-Schnittstelle abzufangen. Daher ist LD_PRELOAD nicht unsere Lösung.

BPF nutzen, um Rückgabewerte des clock_gettime-Systemaufrufs zu modifizieren

Wir versuchten auch, die Prozessidentifikationsnummer (PID) mit BPF zu filtern. Auf diese Weise könnten wir Uhrzeitabweichungen für einen bestimmten Prozess simulieren und den Rückgabewert des clock_gettime-Systemaufrufs ändern.

Das schien eine gute Idee, aber wir stießen auf ein Problem: In den meisten Fällen beschleunigt vDSO clock_gettime, aber clock_gettime führt keinen Systemaufruf durch. Auch dieser Ansatz funktionierte nicht. Oje.

Glücklicherweise stellten wir fest, dass bei Systemkernelversion 4.18 oder höher und Verwendung der HPET-Uhr clock_gettime() die Zeit durch normale Systemaufrufe statt vDSO erhält. Wir implementierten eine Version der Uhrzeitabweichung mit diesem Ansatz, die für Rust und C gut funktioniert. Bei Golang erhält das Programm die korrekte Zeit, aber wenn während der Injektion der Uhrzeitabweichung ein sleep ausgeführt wird, wird der Sleep-Vorgang sehr wahrscheinlich blockiert. Selbst nach dem Abbrechen der Injektion kann sich das System nicht erholen. Daher mussten wir auch diesen Ansatz verwerfen.

TimeChaos – unser finaler Hack

Aus dem vorherigen Abschnitt wissen wir, dass Programme die Systemzeit normalerweise durch Aufruf von clock_gettime abrufen. In unserem Fall verwendet clock_gettime vDSO, um den Aufrufprozess zu beschleunigen, daher können wir LD_PRELOAD nicht nutzen, um die clock_gettime-Systemaufrufe zu manipulieren.

Wir identifizierten die Ursache; doch wie lautet die Lösung? Beginnen wir bei vDSO. Wenn wir die Adresse, die den Rückgabewert von clock_gettime in vDSO speichert, auf eine von uns definierte Adresse umleiten können, lösen wir das Problem.

Leichter gesagt als getan. Um dieses Ziel zu erreichen, müssen wir folgende Herausforderungen meistern:

  • Die im User-Modus von vDSO verwendete Adresse kennen

  • Die Kernel-Modus-Adresse von vDSO kennen, wenn wir die clock_gettime-Funktion in vDSO über eine Kernel-Modus-Adresse ändern wollen

  • Wissen, wie vDSO-Daten modifiziert werden

Zuerst müssen wir vDSO untersuchen. Die vDSO-Speicheradresse sehen wir in /proc/pid/maps.

$ cat /proc/pid/maps
...
7ffe53143000-7ffe53145000 r-xp 00000000 00:00 0 [vdso]

Die letzte Zeile enthält vDSO-Informationen. Das Privileg dieses Speicherbereichs ist r-xp: lesbar und ausführbar, aber nicht beschreibbar. Das bedeutet, der User-Modus kann diesen Speicher nicht ändern. Wir können ptrace nutzen, um diese Einschränkung zu umgehen.

Als nächstes exportieren wir vDSO mit gdb dump memory und verwenden objdump, um den Inhalt zu prüfen. Hier das Ergebnis:

(gdb) dump memory vdso.so 0x00007ffe53143000 0x00007ffe53145000
$ objdump -T vdso.so
vdso.so: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
ffffffffff700600 w DF .text 0000000000000545 LINUX_2.6 clock_gettime

Wir sehen, dass das gesamte vDSO wie eine .so-Datei aufgebaut ist, also können wir es mit einem Executable and Linkable Format (ELF)-File formatieren. Mit diesen Informationen nimmt ein grundlegender Workflow zur Implementierung von TimeChaos Gestalt an:

TimeChaos-Workflow
TimeChaos-Workflow

Das obige Diagramm zeigt den Prozess von TimeChaos, einer Implementierung der Uhrzeitabweichung in Chaos Mesh.

  1. Ptrace verwenden, um den angegebenen PID-Prozess anzuhängen und den aktuellen Prozess zu stoppen.

  2. Verwenden Sie ptrace, um eine neue Abbildung im virtuellen Adressraum des aufrufenden Prozesses zu erstellen, und nutzen Sie process_vm_writev, um die von uns definierte fake_clock_gettime-Funktion in den Speicherbereich zu schreiben.

  3. Verwenden Sie process_vm_writev, um die spezifizierten Parameter in fake_clock_gettime zu schreiben. Diese Parameter repräsentieren die gewünschte Zeitverschiebung, beispielsweise zwei Stunden zurück oder zwei Tage vor.

  4. Verwenden Sie ptrace, um die clock_gettime-Funktion in vDSO zu modifizieren und sie auf die fake_clock_gettime-Funktion umzuleiten.

  5. Verwenden Sie ptrace, um den PID-Prozess zu trennen.

Wenn Sie an den Implementierungsdetails interessiert sind, sehen Sie sich das Chaos Mesh GitHub-Repository an.

Simulieren von Clock-Skew in einer verteilten SQL-Datenbank

Praxistests liefern die überzeugendsten Ergebnisse. Hier wenden wir TimeChaos auf TiDB an, eine Open-Source-NewSQL-Datenbank für verteilte SQL-Systeme, die Hybrid Transactional/Analytical Processing (HTAP)-Workloads unterstützt. Wir möchten prüfen, ob das Chaos-Engineering tatsächlich effektiv funktioniert.

TiDB nutzt einen zentralen Timestamp Oracle (TSO)-Dienst, um global konsistente Versionsnummern zu generieren und monotone Transaktionsversionen sicherzustellen. Der TSO-Dienst wird durch die Placement Driver (PD)-Komponente verwaltet. Daher wählen wir einen zufälligen PD-Knoten aus und injizieren regelmäßig TimeChaos mit jeweils 10 Millisekunden Rückwärts-Skew. Sehen wir uns an, ob TiDB dieser Herausforderung gewachsen ist.

Für aussagekräftige Tests verwenden wir bank als Workload, der Finanztransaktionen in Banksystemen simuliert. Dieser Workload wird häufig zur Validierung von Datenbanktransaktionen eingesetzt.

Unsere Testkonfiguration:

apiVersion: chaos-mesh.org/v1alpha1
kind: TimeChaos
metadata:
name: time-skew-example
namespace: tidb-demo
spec:
mode: one
selector:
labelSelectors:
"app.kubernetes.io/component": "pd"
timeOffset:
sec: -600
clockIds:
- CLOCK_REALTIME
duration: "10s"
scheduler:
cron: "@every 1m"

Während dieses Tests injiziert Chaos Mesh alle 1 Millisekunde für 10 Sekunden TimeChaos in einen ausgewählten PD-Pod. Innerhalb dieses Zeitraums weicht die von PD abgerufene Zeit um 600 Sekunden von der tatsächlichen Zeit ab. Details finden Sie im Chaos Mesh Wiki.

Erstellen wir ein TimeChaos-Experiment mit dem Befehl kubectl apply:

kubectl apply -f pd-time.yaml

Die PD-Protokolle können wir mit folgendem Befehl abrufen:

kubectl logs -n tidb-demo tidb-app-pd-0 | grep "system time jump backward"

Protokollauszug:

[2020/03/24 09:06:23.164 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585041383060109693]
[2020/03/24 09:16:32.260 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585041992160476622]
[2020/03/24 09:20:32.059 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585042231960027622]
[2020/03/24 09:23:32.059 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585042411960079655]
[2020/03/24 09:25:32.059 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585042531963640321]
[2020/03/24 09:28:32.060 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585042711960148191]
[2020/03/24 09:33:32.063 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585043011960517655]
[2020/03/24 09:34:32.060 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585043071959942937]
[2020/03/24 09:35:32.059 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585043131978582964]
[2020/03/24 09:36:32.059 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585043191960687755]
[2020/03/24 09:38:32.060 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585043311959970737]
[2020/03/24 09:41:32.060 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585043491959970502]
[2020/03/24 09:45:32.061 +00:00] [ERROR] [systime_mon.go:32] ["system time jump backward"] [last=1585043731961304629]
...

Aus den Protokollen wird ersichtlich, dass PD regelmäßig erkennt, dass die Systemzeit zurückgesetzt wird. Dies bedeutet:

  • TimeChaos simuliert Clock-Skew erfolgreich.

  • PD kann mit solchen Clock-Skew-Situationen umgehen.

Ein vielversprechendes Ergebnis! Doch betrifft TimeChaos auch andere Dienste neben PD? Prüfen wir dies im Chaos Dashboard:

Chaos-Dashboard
Chaos-Dashboard

Die Überwachung zeigt deutlich: TimeChaos wurde alle 1 Millisekunde injiziert und der gesamte Vorgang dauerte 10 Sekunden. Entscheidend ist, dass TiDB nicht von diesen Injektionen beeinträchtigt wurde. Das Bank-Programm lief fehlerfrei und ohne Leistungseinbußen.

Testen Sie Chaos Mesh

Als cloud-native Chaos-Engineering-Plattform bietet Chaos Mesh umfassende Methoden zur Fehlerinjektion in Kubernetes-Systemen, die Fehler in Pods, Netzwerken, Dateisystemen und sogar im Kernel abdecken.

Lust auf praktische Erfahrungen im Bereich Chaos Engineering? Willkommen bei Chaos Mesh. Dieses schnelle 10-Minuten-Tutorial hilft Ihnen, schnell in Chaos Engineering einzusteigen und Ihr erstes Chaos-Experiment mit Chaos Mesh durchzuführen.