Lasttests mit Puppeteer durchführen
Table of contents
Vorwort
Es empfiehlt sich zunächst unseren Bericht zum Thema eduMEET Lasttest mit Puppeteer durchzulesen, bevor Sie diese Anleitung lesen. Darin befindet sich viel Information darüber, wie wir den Lasttest durchgeführt haben, warum wir es so gemacht haben und auf welche Probleme bzw. Hindernisse wir gestoßen sind, welche Limitationen diese Art des Tests mit sich bringt und natürlich auch zu welchen Ergebnissen wir gekommen sind.
Vorraussetzungen zum Verstehen und Umsetzen dieser Anleitung sind Verständnis von Linuxbetriebssystemen und damit der Shell, etwas Expertise im Bereich der Programmiersprache Javascript und SSH. Auf Hardwareseite bedarf es natürlich einer Instanz gegen die getestet wird und eine oder mehrere Linux-VM(s), die die Last generieren. Es handelt sich aber im Grunde "nur" um javascript Dateien, welche natuerlich auch unter Windows oder MacOS ausgefuehrt werden koennen. Im folgenden beschrieben wird das Prozedere allerdings aus Sicht einer Linux-VM.
Einleitung & Motivation
Diese kurze Anleitung soll dem geneigten Leser einen Einstiegspunkt in den systematischen und programmatischen Prozess eines Lasttests geben. Sie richtet sich an Administratoren und Betreuer von Videokonferenzplattformen, die sich einige Fragen zur Gewährleistung eines laufenden Betriebs stellen. Solche Fragen können sein "Wie viele Teilnehmer hält mein System stand?" oder auch "Für wie viel Zustrom ist meine derzeitige Installation ausgelegt?" und dem entsprechend dann auch "Muss ich zeitnah aufrüsten (oder kann ich sogar abrüsten?)?".
Um Antworten auf diese Fragen zu bekommen, folgt nun eine kurze Anleitung, wie man mit Hilfe von Puppeteer einen solchen Lasttest realisieren kann. Wir exerzieren das Ganze anhand der Open Source VC-Plattform eduMEET durch, welches System Sie schlussendlich stresstesten wollen, bleibt dabei Ihnen überlassen.
Puppeteer
Puppeteer ist laut der offiziellen Webseite pptr.dev eine Javascript Bibliothek, welche eine High-Level API zur Kontrolle von Chrome oder Firefox bereitstellt. Standardmäßig läuft Puppeteer headless, das heisst ohne graphisches User Interface. Puppeteer ist also keine reine Stresstest-Suite, sondern vielmehr ein Schweizer Taschenmesser der GUI-losen Browsersteuerung. Welche Zielsetzung man mit dem Tool verfolgt, ist zunächst einmal offen.
Wir machen uns daher zu Nutze, dass man mit Hilfe eines Skripts Browser starten, manipulieren, Aktionen ausführen und wieder beenden lassen kann.
In unserem Fall begegnen wir dem Stresstest mit Puppeteer programmatisch mit einem Skript, welches verschiedene Nutzerinteraktionen simuliert. In Kürze sind das die folgenden Dinge:
- Nutzer betreten Videokonferenzraum,
- senden dauerhaft Video/Audio und
- erzeugen somit Last für die zu testende Instanz
Welches Videokonferenzsystem wir dabei testen ist unerheblich, Puppeteer ist hier VC-Systemagnostisch. Solange es WebRTC-fähig ist, kann es mit einem Browser gegengetestet werden.
Disclaimer: in Abhängigkeit davon, wie man Ihre Instanz verwendet - gerade in Bezug auf Authorisierung und Authentifikation - kann es sehr komplex/aufwändig werden einen Lasttest mit Puppeteer zu realisieren. Bei vorgeschalteter 2-Faktor-Authentifizierung beispielsweise kann es sehr aufwändig sein, ein Puppeteer-Skript so zu schreiben, dass ein Lasttest gelingt.
Szenario
Zum Thema Szenario haben wir in unserem Bericht einige Hinweise verfasst, die uns während der Tests aufgefallen sind, sodass Ihr Lasttest sinnvoll und konstruktiv durchgeführt werden kann und Sie nicht in die selben Probleme laufen.
Der Grundgedanke ist, man hat eine lasterzeugende VM (dabei kommen auch andere Instanzen als AWS in Frage) auf der man Puppeteer laufen lässt und testet damit die lastverarbeitende VM.
Unser gewähltes Szenario (welches Sie natürlich gerne abwandeln können) lautet dabei wie folgt:
Befülle ein Meetingraum so lange mit Video und Audio sendenden Teilnehmern, bis die VM in die Knie geht und Ausfallerscheinungen auftreten.
Es soll also getestet werden, wie viele Teilnehmer innerhalb eines Raumes Video und Audio senden können ohne dass andere Teilnehmer Beeinträchtigungen in der Video- und Audioqualität wahrnehmen.
Vorgehen
Wie eingangs schon erwähnt ist die Vorraussetzung für dieses Unterfangen ein (einigermaßen) aktuelles Betriebssystem, welches mit Javascript und SSH umgehen kann. Glücklicherweise können das mittlerweile fast alle Betriebssysteme. Trotzdem kann es zu kleineren Abweichungen in der Benutzung kommen. Der Einfachheit halber und der Popularität von Debian im Serverumfeld geschuldet, gehen wir hier die Schritte mit dieser Linuxdistribution durch.
Zunächst muss Node in einer aktuellen Version und die Puppeteer Javascript-Bibliothek per Terminal installiert werden:
npm i puppeteer
npm i puppeteer-core # Alternatively, install as a library, without downloading Chrome.
Ab hier passiert im Grunde alles im Terminal bzw. dem präferierten Code-Editor.
Der Ablauf für unser Unterfangen ist dabei heruntergebrochen immer der selbe:
- Importiere die Bibliothek
- Starte eine(n) Browser(instanz)
- Öffne einen neuen Tab
- Gehe auf die Seite, die man testen möchte
- Gehe die einzelnen Schritte durch, die ein Benutzer auch durchgehen müsste
Das oben stehende Bild zeigt die grundsätzliche Struktur einer Javascriptdatei, welche noch einige nützliche beispielhafte Funktionen und deren Benutzung beinhaltet. Für genauere Informationen ist die sehr gute Puppeteer Dokumentation zu empfehlen. Ausserdem kann unter Puppeteer API die einzelnen Funktionen und Variablen angeschaut werden.
Im Falle eduMEET sieht das ganze so aus:
eduMEET Mutlijoin.js
Zeile 5-9: Konfigurationsparameter die beliebig dem Szenario angepasst werden können.
Zeile 11-32: Funktion, welche pro gestartete Instanz durchlaufen wird und die jeweilige Benutzerinteraktion simuliert.
Zeile 14-21: Startet den Browser mit einigen Argumenten, wichtig für den WebRTC Lasttest, da sie bspw. die Kamerastreams emulieren.
Zeile 37-48: Main-Funktion, und damit Start des Skripts. Es regelt die Schleifenlogik, welche die einzelnen Durchläufe der synthetischen Teilnehmer der VC realisiert.
So wie das Skript oben geschrieben wurde, treten alle 5 Sekunden ein neuer Teilnehmer mit dem Video und Audio der im Ordner der Skriptdatei befindlichen Datei 1.mpjeg (ff.) bei. Insgesamt 10 Teilnehmer treten hinzu. In der Konsole wird dabei immer ausgegeben, der wievielte Teilnehmer gerade hinzugefügt wurde. Das Skript läuft so lange, bis man es per CTRL+C beendet.
Ein Link zum Github Repository mit besagter Datei finden Sie am Ende dieser Seite.
Hinweise:
Die Videodateien müssen im Ordner der Javascriptdatei im .mjpeg oder .y4m Format liegen. Leider akzeptiert der Browser bzw. der Flag nur .y4m und .mjpeg Dateien. Die Dateien können natürlich auch wo anders liegen, aber dann müssen sie korrekt im Code referenziert werden (Zeile 12 und 18). Welche Dateien Sie hierbei nehmen, bleibt Ihnen überlassen. Hier wurden 5 sekündige KI-generierte Videoschnippsel verwendet, welche redende Personen in einer Videokonferenz darstellen. Ob es einen Unterschied macht, welche Art von Video man hierfür nimmt, ist eine offene Frage für zukünftige Arbeiten.
Zu Debugzwecken, beispielsweise um rauszufinden "wo man sich gerade auf der Seite befindet", kann als Argument headless: false gesetzt werden (Zeile 14), dann wird ein Browser mit GUI gestartet. Das kann man aber auch umgehen indem einfach immer ein Screenshot gemacht wird, der im Ordner gespeichert wird.
Die Benutzerinteraktionen werden typischerweise über Klicks realisiert; die korrespondierende Funktion ist hierbei die click()-Funktion. Es gibt mehrere Möglichkeiten diese zu verwenden, die wohl einfachste ist aber die Id des jeweiligen Buttons oder Links auf der Seite per "Entwicklerwerkzeuge → Untersuchen" zu finden und dann darauf die click()-Funktion anzuwenden. In diesem Fall wird also vom Objekt page per locator der Button 'button.css-1uuijbe' ausgewählt und geklickt. Sollten die CSS Ids schwerer zu finden oder gar randomisiert sein, wird es unbequem...
Es empfiehlt sich das RAMP_UP_INTERVALL nicht zu kurz zu setzen, um keine verfälschenden Lastspitzen zu erzeugen, die die VM vorzeitig in die Knie zwingen und einen geregelten Testbetrieb nicht mehr ermöglichen. Es kann aber auch ein Szenario für einen Test sein, wenn man Lastspitzen explizit erzeugen will, um beispielsweise zeitkritische Events zu simulieren, bei denen viele User nahezu gleichzeitig auf einen Dienst zugreifen wollen.
Start des Skripts
Das Skript wird per node Befehl über die Konsole gestartet:
node name_des_skripts.js
Beendet wird es mit CTRL+C.
Logging
Wenn man nicht gerade nur an der Info "geht oder geht nicht?" interessiert ist, bietet es sich an die Systemdaten während des Lasttests mitzuloggen. Idealerweise aller Systeme die beteiligt sind aber das bleibt Ihrer Einschätzung über Aufwand/Nutzen überlassen.
Sinnvoll wäre es dabei, sich die folgenden Metriken loggen zu lassen (inklusive Beispielen für CLI Programmen in Klammern):
Alle der genannten Programme können die Daten entweder bequem per Parameter in eine Datei schreiben oder alternativ piped man den Output direkt in eine Datei per > logging.csv
Eine Visualisierung der .csv Datei kann dann beispielsweise mit LibreOffice Calc vorgenommen werden und sieht beispielhaft so aus:
Zusammenfassung
Puppeteer bietet einen flexiblen und programmatischen Ansatz für Lasttests von WebRTC-basierten Videokonferenzplattformen. Mit vergleichsweise wenig Code lassen sich realistische Nutzerszenarien simulieren - von einfachen Join/Leave-Patterns bis hin zu komplexen Multi-User-Setups mit Audio und Video.
Die Hauptstärken dieser Methode liegen in der Plattformunabhängigkeit und der einfachen Anpassbarkeit an verschiedene Testszenarien. Solange die zu testende Plattform WebRTC-fähig ist und über einen Browser zugänglich, kann sie mit Puppeteer getestet werden. Die Javascript-basierte Implementierung ermöglicht zudem schnelle Iterationen und Anpassungen des Testskripts.
Offene Punkte
Es gibt diverse Browserflags, die in der Theorie die Performance erhöhen sollten:
--enable-webrtc-hw-decoding
Sollte ermöglichen, dass der Browser die Grafikkarte verwendet, um den einkommenden Videostream zu dekodieren anstatt den CPU damit zu belasten. Leider hat dieser Flag in unseren Tests keine Wirkung gezeigt und die CPU Last nicht verringert. Gleiches gilt für den Flag
--enable-webrtc-hw-encoding
welcher die Last an die GPU auslagert, den Videostream zu enkodieren.
Zum Überprüfen, ob der Flag Wirkung zeigt, kann die Seite chrome://gpu aufgerufen werden:
Als Inspiration für einen performanteren Weg gibt es speziell für das Projekt eduMEET ein eigenes Repository, welches einen ressourcenschonenderen Weg über ein Python-Skript geht. Die Last wird dabei nur durch das Erzeugen von In-/Outputstreams mit Signalling generiert. Dadurch fällt der Overhead eines ganzen neuen Browsers bzw. Tabs und fast quadratischer Notwendigkeit des Dekodierens weg.
Weitere offene Fragen sind: Wie koordiniert man mehrer VM's um den Lasttest zu skalieren? Wie verhindert man, dass die lastgenerierende VM selbst das Bottleneck wird? Spielt es für die lastempfangende (oder auch lastgenerierende) VM eine Rolle wie groß die eingebundene Videodatei ist oder wie hoch die Bitrate des Videos ist?