Skip to content

Repository files navigation

Kubernetes Chaos Simulator – randomfail

randomfail ist ein absichtlich instabiler FastAPI‑Service zur Simulation realer Ausfallszenarien in Kubernetes‑Clustern. Der Dienst erzeugt automatisch oder manuell verschiedene Fehlerzustände und ermöglicht so das gezielte Testen von:

  • Liveness‑ und Readiness‑Probes
  • Self‑Healing‑Mechanismen
  • HPA‑Reaktionen unter Last
  • PVC‑Limits und Storage‑Fehlern
  • Graceful‑Shutdown‑Verhalten

Der integrierte Chaos‑Zyklus wechselt in frei konfigurierbaren Intervallen automatisch zwischen verschiedenen Fehlerszenarien. Zusätzlich lassen sich alle Szenarien jederzeit manuell über HTTP‑Endpoints auslösen — ideal für reproduzierbare Tests, Demos oder Chaos‑Engineering‑Workshops.


Simulierte Fehlerzustände

OOM_KILL – Speichererschöpfung

Der Service füllt schrittweise und ohne Obergrenze den Arbeitsspeicher (1 MB pro Sekunde). Sobald das Memory-Limit des Containers erreicht ist, beendet der Linux-Kernel den Prozess mit einem OOMKill-Signal.

Kubernetes-Reaktion:

  • Pod-Status wechselt zu OOMKilled
  • Kubelet startet den Container automatisch neu
  • Bei wiederholten OOMKills: Status CrashLoopBackOff
  • Sichtbar in: kubectl describe pod, Prometheus-Metrik container_oom_events_total

CPU_BURN – CPU-Erschöpfung

Mehrere separate Prozesse führen Endlosberechnungen durch und sättigen die zugewiesenen CPU-Kerne für eine konfigurierbare Dauer. Prozesse statt Threads, weil der GIL Threads auf einen Kern beschränkt; der Event-Loop bleibt dadurch frei und die Probes erreichbar.

Kubernetes-Reaktion:

  • Container wird auf das CPU-Limit gedrosselt (CPU Throttling)
  • Sichtbar in Metrik container_cpu_cfs_throttled_seconds_total
  • Horizontal Pod Autoscaler (HPA) kann weitere Replikas hochskalieren
  • Probes bleiben erreichbar; Probe-Timeouts gezielt über SLOW_RESPONSE mit SLOW_AFFECTS_PROBES=true testen

SLOW_DEATH – Liveness Probe Failure

Der Service setzt seinen internen Health-Status auf unhealthy. Der /healthz-Endpoint antwortet ab diesem Moment mit HTTP 500.

Kubernetes-Reaktion:

  • Liveness Probe schlägt nach failureThreshold Versuchen fehl
  • Kubelet führt einen Container-Neustart durch (kein Pod-Neustart)
  • Während der Neustart-Wartezeit bleibt der Pod im Status Running, nimmt aber keinen Traffic mehr an
  • Sichtbar in: kubectl describe pod → Events: Liveness probe failed

CRASH – Harter Prozessabsturz

Der Prozess beendet sich sofort mit os._exit(1) ohne jegliches Cleanup. Simuliert einen Segfault oder eine unkontrollierte Ausnahme.

Kubernetes-Reaktion:

  • Pod-Status wechselt zu Error (Exit Code 1)
  • Kubelet startet den Container sofort neu
  • Bei wiederholten Abstürzen: exponentielles Backoff → CrashLoopBackOff
  • Sichtbar in: kubectl get pods, kubectl logs --previous

DISK_FILL – PVC-Erschöpfung

Der Service schreibt eine große Binärdatei auf das eingebundene PersistentVolume, bis der verfügbare Speicherplatz erschöpft ist.

Kubernetes-Reaktion:

  • Schreiboperationen der Anwendung schlagen mit No space left on device fehl
  • Je nach Konfiguration: Pod wird durch den Kubelet evakuiert (Evicted)
  • Das PVC bleibt nach dem Pod-Neustart gefüllt – Cleanup nötig
  • Sichtbar in: kubectl describe pod → Events: Evicted, df -h im Container

SLOW_RESPONSE – Antwort-Verzögerung

Jeder HTTP-Request wird künstlich um eine konfigurierbare Anzahl von Sekunden verzögert. Die Probe-Endpunkte /healthz und /readyz sind standardmäßig ausgenommen, der Pod bleibt also am Leben und liefert nur langsame Antworten.

Kubernetes-Reaktion:

  • Standard: Probes bleiben grün, Clients und Ingress/Mesh sehen hohe Latenz bzw. eigene Timeouts
  • Mit SLOW_AFFECTS_PROBES=true: Liveness- und Readiness-Probe überschreiten timeoutSeconds → Probe gilt als fehlgeschlagen
  • Nach failureThreshold Timeouts: Container-Neustart (Liveness) bzw. Traffic-Ausschluss (Readiness)
  • Simuliert langsame Datenbankabfragen, überlastete Downstream-Services oder GC-Pausen
  • Sichtbar in: kubectl describe pod → Events: Liveness probe failed: context deadline exceeded

FD_EXHAUSTION – File-Descriptor-Erschöpfung

Der Service öffnet kontinuierlich Datei-Handles ohne sie zu schließen, bis das Limit erreicht ist. Vorher wird das Soft-Limit RLIMIT_NOFILE auf FD_EXHAUSTION_LIMIT (Default 1024) gesenkt, da containerd nofile oft auf 1.048.576 setzt und das Szenario sonst sehr lange dauert. Beim Reset wird das ursprüngliche Limit wiederhergestellt.

Kubernetes-Reaktion:

  • Alle weiteren Systemoperationen schlagen mit too many open files fehl
  • HTTP-Verbindungen können nicht mehr angenommen werden → Pod antwortet nicht mehr
  • Liveness Probe schlägt fehl → Container-Neustart
  • Simuliert typische FD-Leaks in Produktionsdiensten (nicht geschlossene DB-Connections, Socket-Leaks)
  • Sichtbar in: kubectl exec → ls /proc/<pid>/fd | wc -l

SIGTERM_DELAY – Verzögerter Graceful Shutdown

Beim Empfang des SIGTERM-Signals (ausgelöst durch kubectl delete pod, Rolling Update oder Scale-Down) wartet der Service für eine konfigurierbare Zeit, bevor er sich beendet.

Kubernetes-Reaktion:

  • Kubernetes wartet maximal terminationGracePeriodSeconds auf den Prozess
  • Nach Ablauf der Frist: SIGKILL (harter Abbruch)
  • Testet, ob terminationGracePeriodSeconds ausreichend dimensioniert ist
  • Während der Wartezeit werden Health-Probes auf unhealthy gesetzt, um Traffic-Routing zu stoppen
  • Sichtbar in: kubectl describe pod → Terminating Status

READINESS_FLAP – Intermittierende Readiness

Der Pod wechselt in konfigurierbarem Takt zwischen Ready und NotReady, ohne sich zu stabilisieren. Simuliert instabile Datenbankverbindungen, Race Conditions beim Startup oder kurze Überlastzustände. Die Liveness-Probe bleibt dabei unberührt – der Pod wird nicht neu gestartet.

Kubernetes-Reaktion:

  • Pod wird wiederholt aus den Service-Endpoints entfernt und wieder hinzugefügt
  • Clients erhalten intermittierend HTTP 503, obwohl der Pod läuft
  • Löst Alerting-Regeln aus (KubePodNotReady, KubeDeploymentReplicasMismatch)
  • HPA kann nicht stabil skalieren
  • Sichtbar in: kubectl get endpoints -w, kubectl describe pod → Events: Readiness probe failed

STABLE – Kein Chaos

Der Service läuft ohne Fehler. Dient als Ruhephase zwischen den Chaos-Zyklen.


HTTP-Endpunkte

Kubernetes Probes

Endpunkt Probe-Typ Beschreibung
GET /healthz Liveness + Startup Gibt HTTP 200 zurück wenn der Service gesund ist, sonst HTTP 500
GET /readyz Readiness Gibt HTTP 200 zurück wenn der Service bereit ist, sonst HTTP 503

Anwendung

Endpunkt Beschreibung
GET / HTML-Seite mit aktueller Uhrzeit und aktivem Szenario. Jede dritte Anfrage gibt HTTP 500 zurück.
GET /status JSON-Statusübersicht: aktives Szenario, Speicherverbrauch, FD-Anzahl, Konfiguration

Chaos-Kontrolle

Endpunkt Szenario Beschreibung
POST /chaos/reset – Setzt alle Chaos-Zustände zurück
POST /chaos/pause – Pausiert den automatischen Chaos-Loop; das aktive Szenario läuft weiter bis /chaos/reset
POST /chaos/resume – Setzt den automatischen Chaos-Loop fort
POST /chaos/oom OOM_KILL Fügt sofort 100 MB Speicherdruck hinzu
POST /chaos/cpu CPU_BURN Startet CPU-Burn-Prozesse für konfigurierte Dauer
POST /chaos/crash CRASH Beendet den Prozess sofort mit Exit Code 1
POST /chaos/unhealthy SLOW_DEATH Schaltet den Health-Status um (Toggle)
POST /chaos/disk DISK_FILL Startet das Befüllen des PVC
POST /chaos/slow SLOW_RESPONSE Aktiviert künstliche Request-Verzögerung
POST /chaos/fd FD_EXHAUSTION Startet das Erschöpfen der File-Descriptors
POST /chaos/flap READINESS_FLAP Startet das intermittierende Readiness-Toggling

Konfiguration

Alle Parameter werden über Umgebungsvariablen gesetzt (Helm-Values in randomfail-chart/values.yaml):

Variable Default Beschreibung
CHAOS_ENABLED true false: automatischer Chaos-Loop startet pausiert (per /chaos/resume aktivierbar)
CHAOS_INTERVAL 300 Sekunden zwischen automatischen Chaos-Zyklen
CHAOS_STARTUP_DELAY 10 Sekunden Wartezeit nach dem Start vor dem ersten Zyklus
MEMORY_CHUNK_SIZE 1000000 Bytes pro Speicher-Chunk im OOM-Szenario (1 MB)
DISK_FILL_SIZE_MB 110 Größe der Junk-Datei für das DISK_FILL-Szenario in MB
CPU_BURN_WORKERS 2 Anzahl paralleler Prozesse im CPU_BURN-Szenario (je ein Kern)
CPU_BURN_DURATION 120 Sekunden Dauer des CPU-Burns (empfohlen: max. CHAOS_INTERVAL / 2)
SLOW_RESPONSE_DELAY 5 Sekunden künstliche Verzögerung pro Request im SLOW_RESPONSE-Szenario
SLOW_AFFECTS_PROBES false true: SLOW_RESPONSE verzögert auch /healthz und /readyz
SIGTERM_DELAY 30 Sekunden Wartezeit nach SIGTERM vor dem Prozess-Exit
READINESS_FLAP_INTERVAL 5 Sekunden zwischen Readiness-Toggles im READINESS_FLAP-Szenario
FD_EXHAUSTION_LIMIT 1024 Soft-Limit für offene FDs im FD_EXHAUSTION-Szenario, 0 = Limit nicht ändern

Lokaler Betrieb

Voraussetzungen

  • Python 3.12+
  • uv

Setup und Start

uv lock --upgrade
uv sync
uv run ruff check .
uv run pyright .
uv pip compile pyproject.toml -o requirements.txt

uv run uvicorn main:app --host 0.0.0.0 --port 8080

Erreichbar unter: http://localhost:8080

Docker

docker build -t randomfail .
docker run -p 8080:8080 \
  -e CHAOS_INTERVAL=60 \
  -e SLOW_RESPONSE_DELAY=3 \
  randomfail

Deployment in Kubernetes (Helm)

helm upgrade randomfail ./randomfail-chart -n randomfail --create-namespace

Das Chart enthält:

  • Deployment mit konfigurierten Liveness-, Readiness- und Startup-Probes
  • PersistentVolumeClaim für das DISK_FILL-Szenario
  • Service (ClusterIP)
  • Optionales Istio-Gateway mit VirtualService und TLS via cert-manager

Chaos-Szenarien manuell auslösen

# Status abfragen
curl -k https://randomfail.gmk.lan/status

# OOM-Druck erzeugen
curl -k -X POST https://randomfail.gmk.lan/chaos/oom

# Harten Absturz auslösen
curl -k -X POST https://randomfail.gmk.lan/chaos/crash

# CPU-Last starten
curl -k -X POST https://randomfail.gmk.lan/chaos/cpu

# PVC füllen
curl -k -X POST https://randomfail.gmk.lan/chaos/disk

# Health-Status auf unhealthy setzen (Liveness Probe Test)
curl -k -X POST https://randomfail.gmk.lan/chaos/unhealthy

# Langsame Antworten aktivieren (Probe Timeout Test)
curl -k -X POST https://randomfail.gmk.lan/chaos/slow

# File-Descriptor-Erschöpfung starten
curl -k -X POST https://randomfail.gmk.lan/chaos/fd

# Readiness Flapping starten (intermittierender NotReady-Status)
curl -k -X POST https://randomfail.gmk.lan/chaos/flap

# Automatischen Chaos-Loop pausieren, damit manuelle Szenarien nicht überschrieben werden
curl -k -X POST https://randomfail.gmk.lan/chaos/pause

# Automatischen Chaos-Loop fortsetzen
curl -k -X POST https://randomfail.gmk.lan/chaos/resume

# Alle Chaos-Zustände zurücksetzen
curl -k -X POST https://randomfail.gmk.lan/chaos/reset

About

Ein absichtlich instabiler FastAPI-Service zur Simulation realer Ausfallszenarien in Kubernetes-Clustern.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages