AKS-Plattform: Lasttests¶
Diese Seite beschreibt, wie die Plattform lastgetestet wird und was der Test misst. Der Test hat zwei Ziele: herausfinden, wie viel Traffic ein workload auf dem Nicht-Produktions-Cluster aushält, bevor er kaputtgeht, und nachweisen, dass die Alerting-Kette (Prometheus- Regel, Alertmanager, PagerDuty) auslöst und sich wieder auflöst, wenn sie es soll.
Referenzdesign, dein Deployment kann abweichen
Der Test läuft nur gegen den Nicht-Produktions-Cluster. Die Ziel-Anwendung, die Schwellenwerte und die Routen stammen aus dem Referenz-Setup. Ein längeres Designdokument beschreibt ein breiteres Testprogramm mit einem anderen Tool. Diese Seite behandelt, was umgesetzt ist, und nennt den Rest als geplant.
So läuft ein Testlauf¶
Eine manuell gestartete pipeline erstellt im Cluster einen kurzlebigen Job, erzeugt Traffic in drei Stufen, beobachtet die Auswirkungen und entfernt danach alles wieder. Im Leerlauf trägt der Cluster keinen Lastgenerator.
flowchart TB
start["Pipeline started by hand<br/>(rates, durations, failure trigger)"] --> login["OIDC login to Azure,<br/>cluster credentials"]
login --> infra["Apply namespace, RBAC,<br/>network policy, dashboard"]
infra --> mark1["Annotate Grafana: test start"]
mark1 --> job["Vegeta job in the load-testing namespace"]
job --> s1["Stage 1: warm-up"]
s1 --> s2["Stage 2: sustained"]
s2 --> s3["Stage 3: overload"]
s3 --> report["Vegeta report in the run summary"]
report --> mark2["Annotate Grafana: test end"]
mark2 --> clean["Delete job and config map,<br/>reset the failure rate"]
| Teil | Detail |
|---|---|
| Tool | Vegeta, ein HTTP-Angreifer mit konstanter Rate, als Kubernetes-Job ausgeführt (ein Pod, keine Retries) |
| Wo | Im Cluster in einem eigenen Namespace load-testing, sodass der Traffic durch das Cluster-Netzwerk und DNS läuft |
| Trigger | Ein manueller GitHub-Actions-Workflow mit den unten genannten Parametern |
| Identität | Die föderierte Identität der pipeline mit der Azure-Rolle Azure Kubernetes Service RBAC Admin, auf den Cluster beschränkt |
| Footprint | Ein Service Account mit einer Rolle im Namespace, eine Network Policy für die Generator-Pods und eine Grafana-Dashboard-ConfigMap. Der Job und die ConfigMap seines Skripts werden am Ende gelöscht (if: always()); der Job hat außerdem eine Time-to-live |
| Größe des Generators | Requests 500m CPU und 1 GiB, Limits 2 CPU und 4 GiB. Der Worker-Pool ist auf 8000 Verbindungen begrenzt, was nötig ist, um 3000 Requests pro Sekunde zu halten, wenn die Latenz steigt |
| Pre-flight | Das Skript wartet bis zu 60 Sekunden auf den Health-Endpunkt des Ziels, bevor es angreift |
Stufen und Parameter¶
| Stufe | Standardrate | Standarddauer | Zweck |
|---|---|---|---|
| 1. Warm-up | 60 Requests pro Sekunde | 30 s | Gesunde Baseline |
| 2. Sustained | 400 Requests pro Sekunde | 1 Min. | Last, auf die der Autoscaler reagiert |
| 3. Overload | 3000 Requests pro Sekunde | 5 Min. | Über die Kapazität des workloads hinausgehen |
Alle sechs Werte sind Eingaben des Workflows. Eine siebte Eingabe, trigger_failure (standardmäßig an), automatisiert die Fehlergeschichte: Die pipeline
erhöht die Fehlerrate der Anwendung zu Beginn von Stufe 2 und setzt sie in der letzten Minute von Stufe 3 zurück, sodass ein Fehlerpeak
garantiert ist, ohne Raten von Hand abzustimmen. Ohne sie steuerst du die beiden Änderungen von Hand mit kubectl set env.
Was gemessen wird¶
Die Anwendung exportiert ihre eigenen Prometheus-Metriken (der Generator selbst exportiert keine). Der Lauf wird anhand der Anwendung und des Clusters bewertet.
| Bereich | Signal | Verwendet für |
|---|---|---|
| Request-Rate | Requests pro Sekunde nach Response-Code | Angebotene Last und erreichter Durchsatz |
| Fehler | Anteil der 5xx-Antworten über 5 Minuten | Verfügbarkeit und den Fast-burn-Alert |
| Latenz | p50, p90, p95, p99 aus dem Histogramm der Request-Dauer | Latenzziel |
| Autoscaling | Gewünschte Replicas des HPA gegenüber aktuellen Replicas | Skaliert der workload aus, und wie schnell |
| Kapazität | Pod-CPU gegenüber Requests, pending Pods, Restarts, Out-of-Memory-Kills | Wo der workload saturiert |
| Alerts | Prometheus-Alert-Status mit dem Label für den Lasttest, Alertmanager-Receiver, PagerDuty-Incident | Ob die Kette funktioniert |
| Client-Sicht | Der Vegeta-Report: Summen pro Stufe und gesamt, mit Latenz-Perzentilen und Erfolgsquote | Gegenprüfung von der Client-Seite |
Ein Grafana-Dashboard für den Test wird aus einer ConfigMap bereitgestellt. Ein Hilfsskript kann nach dem Lauf eine Zusammenfassung aus Prometheus ausgeben (Requests gesamt, Spitzenrate, Spitzen-Fehlerrate, Spitzen-p95 und -p99, maximale Replicas, Restarts, Alert-Status).
Bestanden und fehlgeschlagen¶
Der Job besteht oder scheitert nicht an der Erfolgsquote von Vegeta, weil Stufe 3 den workload überlasten soll. Der Workflow schlägt nur fehl, wenn der Job einen Fehler hat (zum Beispiel ist das Ziel nicht erreichbar) oder nicht rechtzeitig fertig wird. Das eigentliche Ergebnis ist, ob die Alerts auslösen und sich auflösen und ob ein Incident erstellt und wieder geschlossen wird.
Alert-Validierung¶
Drei Alert-Regeln für den Lasttest spiegeln die Service Level Objectives der Referenzaufgabe. Sie tragen Labels, die sie als Test-Alerts kennzeichnen:
| Alert | Bedingung | For | Severity |
|---|---|---|---|
AvailabilityErrorBudgetBurnFast |
5xx-Anteil über 5 Minuten über dem 10-Fachen des Error Budgets von 0,1 % | 5 Min. | page |
AvailabilityErrorBudgetBurnSlow |
5xx-Anteil über 30 Minuten über dem 2-Fachen des Budgets | 12 h | ticket |
LatencySLOViolation |
p95-Latenz über 5 Minuten über 300 ms | 15 Min. | page |
Alertmanager leitet Alerts mit source = load-test an einen eigenen Receiver weiter. Im umgesetzten Setup nutzt er denselben
PagerDuty-Routing-Key wie die Produktion und ist an einem Präfix [LOAD-TEST] und an Feldern im Event zu erkennen. Das Designdokument
schlug dafür einen separaten PagerDuty-Service vor.
Was du in einem Lauf beobachten kannst: nur den Fast-burn-Alert, weil Stufe 3 standardmäßig 5 Minuten dauert, so lange wie die
for-Periode der Regel. Lass Stufe 3 20 Minuten oder länger laufen, um den Latenz-Alert auslösen zu sehen. Den Slow-burn-Alert kannst du nicht live beobachten.
Nachweise, die du für eine Alert-Validierung sammelst:
| Prüfung | Wie |
|---|---|
Der Alert erreicht firing |
Prometheus-Alerts-Seite, nach dem Test-Label gefiltert |
| An den Test-Receiver geleitet | Alertmanager-Alerts-Seite |
| Incident erstellt | PagerDuty, Titel mit dem Präfix [LOAD-TEST] |
| Incident löst sich selbst auf | PagerDuty, nachdem die Fehlerrate zurückgesetzt wurde und sich die Metriken erholen |
| Keine Auswirkung auf das Produktions-Alerting | Keine weiteren Incidents auf demselben Service im Zeitfenster |
Der Ziel-workload¶
Das Ziel ist eine kleine öffentliche Demo-Webanwendung (Flask, mit Prometheus-Metriken), deployt im Namespace <team>-dev mit
dem gemeinsamen Application Chart: Autoscaling von 3 bis 10 Replicas bei einem CPU-Ziel von 70 %, ein Disruption Budget, ein gehärteter Security
Context und ein Service Monitor, der das Prometheus-Label job setzt, sodass Regeln, Dashboard und Abfragen alle auf einen Namen zeigen.
Der Work-Endpunkt simuliert 50 bis 200 ms Latenz und schlägt zufällig mit einer konfigurierbaren Rate fehl (Standard 2 %). Er verschlechtert sich
unter Last realistisch, ohne einen künstlichen Stress-Schalter.
Nur die Umgebung dev wird unterstützt. Die Namespaces test und accp laufen mit einem Platzhalter-Webserver-Image, nicht mit der Demo-
Anwendung.
Was die Zahlen bedeuten
Im validierten Lauf flachte der Durchsatz bei etwa 1.800 bis 2.260 Requests pro Sekunde ab (etwa 220 pro Pod bei zehn Pods), der Autoscaler ging von 3 auf 10 Pods, der Fast-burn-Alert löste etwa sechs Minuten nach Beginn des Laufs aus und löste sich kurz nach dem Zurücksetzen der Fehlerrate auf. Diese Zahlen beschreiben die Demo-Anwendung (einen Entwicklungsserver mit einem Prozess), nicht die Kapazität der Plattform. Ein höheres CPU-Limit machte keinen Unterschied. Betrachte den Lauf als wiederholbare Methode, um die Zahl für jeden Service zu finden.
Bekannte Einschränkungen¶
| Einschränkung | Auswirkung |
|---|---|
| Das Latenz-Histogramm misst nur die Verarbeitung der Requests, nicht die Zeit in der Warteschlange, wenn der Server saturiert ist | Der Latenz-Alert löst selbst bei schwerer Überlast selten aus, deshalb ist der Fast-burn-Alert das verlässliche Signal |
| Ein Self-heal von Argo CD machte die Änderung der Fehlerrate innerhalb einer Sekunde rückgängig | Behoben, indem die Argo-CD-Anwendung angewiesen wurde, dieses eine Feld zu ignorieren |
| Rollenzuweisungen für die pipeline und das Graph-API-Problem | Nutze das Target cicd-aks-rbac (Deployment) |
| Ergebnisse der Läufe werden nicht automatisch gespeichert oder verglichen | Sichere die Zusammenfassung des Laufs und das Dashboard selbst |
| Lokale Läufe auf einem Laptop liefern verrauschte absolute Zahlen | Nutze die Zahlen aus dem Cluster |
Geplantes Testprogramm (nur Design)¶
Das Designdokument beschreibt weitere Testarten für den Nicht-Produktions-Cluster. Sie sind in der pipeline nicht umgesetzt.
| Test | Muster | Zweck |
|---|---|---|
| Smoke | 1 bis 5 User, 1 bis 2 Minuten | Test-Setup und Ziel prüfen |
| Baseline | Normale Last für etwa 13 Minuten | Referenzwerte für den Release-Vergleich |
| Load | Erwartete Spitzenlast für etwa 23 Minuten | Alle Plattformziele werden eingehalten |
| Stress | Schrittweise Steigerung bis zur Degradation | Den Bruchpunkt finden, informativ |
| Spike | Plötzlicher Burst, dann zurück | Der Autoscaler reagiert und das System erholt sich |
| Soak | Moderate Last über 2 bis 8 Stunden | Leaks und schleichende Verschlechterung |
| Capacity | Steigerung, bis ein Ziel bricht | Maximal nachhaltiger Durchsatz |
| Autoscaling | Last über dem Autoscaler-Ziel, dann auf null | Timing von Scale-up und Scale-down |
| Alerting | Lastmuster, die bekannte Alerts auslösen | Kette von der Regel zum Incident |
Das Design nannte außerdem k6 mit seinem Kubernetes-Operator als primäres Tool und eine Azure-DevOps-pipeline. Die Umsetzung wechselte zu Vegeta, gesteuert über GitHub Actions, weil die k6-basierten Demo-Läufe wiederholt nicht die erwarteten Ergebnisse lieferten.