Am 14. Dezember sprach ich (Artem Sokovets) bei einer Kundgebung in St. Petersburg zusammen mit meinem Kollegen Dmitry Markelov über die aktuelle Infrastruktur für Autotests in SberTech. Eine Nacherzählung unserer Rede finden Sie in diesem Beitrag.

Was ist Selen?
Selen ist ein Webbrowser-Automatisierungstool. Heute ist dieses Tool der Standard für die WEB-Automatisierung.

Es gibt viele Clients für verschiedene Programmiersprachen, die die Selenium Webdriver-API unterstützen. Über die WebDriver-API und das JSON Wire-Protokoll erfolgt eine Interaktion mit dem Treiber des ausgewählten Browsers, der wiederum mit einem bereits realen Browser zusammenarbeitet und die erforderlichen Aktionen ausführt.
Heute ist die stabile Version des Clients Selenium 3.X.

Simon Stewart versprach übrigens, Selenium 4.0 auf der
SeleniumConf Japan- Konferenz vorzustellen.
Selengitter
Im Jahr 2008 kündigte Philippe Hanrigou das Selenium GRID an, um eine Infrastruktur für Autotests mit Unterstützung für verschiedene Browser aufzubauen.

Selenium GRID besteht aus einem Hub und Knoten (Knoten). Der Knoten ist nur ein Java-Prozess. Es kann sich auf demselben Computer wie der Hub befinden, es kann sich auf einem anderen Computer befinden, es kann sich im Docker-Container befinden. Ein Hub ist im Wesentlichen ein Balancer für Autotests, der den Knoten bestimmt, an den die Ausführung eines bestimmten Tests gesendet werden soll. Sie können mobile Emulatoren damit verbinden.
Mit Selenium GRID können Sie Tests auf verschiedenen Betriebssystemen und verschiedenen Browserversionen ausführen. Dies spart auch erheblich Zeit beim Ausführen einer großen Anzahl von Autotests, wenn Autotests natürlich parallel mit dem Maven-Surfire-Plugin oder einem anderen Parallelisierungsmechanismus ausgeführt werden.
Natürlich hat Selenium GRID seine Nachteile. Bei Verwendung der Standardimplementierung müssen folgende Probleme auftreten:
- ständiger Neustart von Hub und Knoten. Wenn der Hub und der Knoten längere Zeit nicht verwendet werden, sind bei einer nachfolgenden Verbindung Situationen möglich, in denen beim Erstellen einer Sitzung auf einem Knoten dieselbe Sitzung im Zeitlimit abfällt. Um die Arbeit wiederherzustellen, ist ein Neustart erforderlich.
- Begrenzung der Anzahl der Knoten. Stark abhängig von Tests und Rastereinstellungen. Ohne mit einem Tamburin zu tanzen, beginnt es mit mehreren Dutzend verbundenen Knoten langsamer zu werden.
- magere Funktionalität;
- die Unmöglichkeit der Aktualisierung ohne vollständigen Stopp des Dienstes.
Erste AutoTest-Infrastruktur bei SberTech
Früher in SberTech gab es die folgende Infrastruktur für UI-Auto-Tests. Der Benutzer startete die Assembly auf Jenkins, der sich mithilfe des Plugins an OpenStack wandte, um die virtuelle Maschine zuzuweisen. VMs wurden mit einem speziellen „Image“ und dem gewünschten Browser ausgewählt, und erst dann wurden Autotests auf dieser VM ausgeführt.
Wenn Sie Tests in Chrome- oder FireFox-Browsern ausführen möchten, sind Docker-Container besonders hervorzuheben. Bei der Arbeit mit dem Internet Explorer mussten Sie jedoch eine „saubere“ VM erstellen, was bis zu 5 Minuten dauerte. Leider ist Internet Explorer ein vorrangiger Browser in unserem Unternehmen.

Das Hauptproblem war, dass dieser Ansatz beim Ausführen von Autotests im IE viel Zeit in Anspruch nahm. Ich musste Tests an Suiten trennen und Baugruppen parallel starten, um zumindest eine gewisse Zeitverkürzung zu erreichen. Wir begannen über Modernisierung nachzudenken.
Neue Infrastrukturanforderungen
Bei verschiedenen Konferenzen zu Automatisierung, Entwicklung und DevOps (Heisenbug, SQA Days, CodeOne, SeleniumConf und andere) haben wir nach und nach eine Liste der Anforderungen für die neue Infrastruktur erstellt:
- Reduzieren Sie die Zeit für die Durchführung von Regressionstests.
- Stellen Sie einen einzigen Einstiegspunkt für Autotests bereit, der das Debuggen für einen Automatisierungsspezialisten erleichtert. Es gibt nicht seltene Fälle, in denen alles lokal funktioniert und sobald die Tests in die Pipeline gelangen - kontinuierliche Stürze.
- Browserübergreifende Kompatibilität und mobile Automatisierung (Appium-Tests).
- Halten Sie sich an die Cloud-Architektur der Bank: Docker-Container sollten in OpenShift verwaltet werden.
- Reduzieren Sie den Speicher- und CPU-Verbrauch.
Ein kurzer Überblick über bestehende Lösungen
Nachdem wir die Aufgaben definiert hatten, analysierten wir die vorhandenen Lösungen auf dem Markt. Die wichtigsten Dinge, die wir untersuchten, waren die Produkte des
Aerokube- Teams (Selenoid und Moon), Alfalab-Lösungen (Alpha Laboratory),
JW-Grid (Avito) und
Zalenium .
Der Hauptnachteil von Selenoid war die mangelnde Unterstützung für OpenShift (ein Wrapper über Kubernetes). Über die Entscheidung von Alfalab gibt es
einen Artikel über Habré . Es stellte sich heraus, dass es sich um dasselbe Selen-Gitter handelte. Die Lösung von Avito ist im
Artikel beschrieben . Wir haben den Bericht auf der Heisenbug-Konferenz gesehen. Es hatte auch Nachteile, die wir nicht mochten. Zalenium ist ein Open-Source-Projekt, auch nicht ohne Probleme.
Die Vor- und Nachteile der von uns berücksichtigten Lösungen sind in der Tabelle zusammengefasst:

Aus diesem Grund haben wir uns für ein Produkt von Aerokube - Selenoid entschieden.
Selenoid gegen Mond
Vier Monate lang haben wir Selenoid verwendet, um das Sberbank-Ökosystem zu automatisieren. Dies ist eine gute Lösung, aber die Bank bewegt sich in Richtung OpenShift, und die Bereitstellung von Selenoid in OpenShift ist keine triviale Aufgabe. Die Subtilität ist, dass Selenoid in Kubernetes den Docker des letzteren steuert und Kubernetes nichts darüber weiß und andere Knoten nicht richtig scherzen kann. Darüber hinaus benötigt Selenoid in Kubernetes einen GGR (Go Grid Router), bei dem der Lastausgleich lahm ist.
Nachdem wir mit Selenoid experimentiert hatten, interessierten wir uns für das kostenpflichtige Moon-Tool, das sich speziell auf die Arbeit mit Kubernetes konzentriert und gegenüber dem kostenlosen Selenoid eine Reihe von Vorteilen bietet. Es wird seit zwei Jahren entwickelt und ermöglicht es Ihnen, die Infrastruktur für die Selenium-Test-Benutzeroberfläche bereitzustellen, ohne Geld für DevOps-Ingenieure auszugeben, die geheime Kenntnisse über die Bereitstellung von Selenoid in Kubernetes haben. Dies ist ein wichtiger Vorteil - versuchen Sie, den Selenoid-Cluster ohne Ausfallzeiten zu aktualisieren und die Kapazität beim Ausführen von Tests zu reduzieren?

Mond war nicht die einzige Option. Zum Beispiel könnten Sie das oben erwähnte Zalenium nehmen, aber tatsächlich ist es das gleiche Selen-Gitter. Es enthält eine vollständige Liste der Sitzungen im Hub. Wenn der Hub abstürzt, werden die Tests beendet. Vor diesem Hintergrund gewinnt Moon aufgrund der Tatsache, dass es keinen internen Zustand hat, so dass der Fall einer seiner Repliken im Allgemeinen nicht wahrnehmbar ist. Moon hat alles „anmutig“ - es kann furchtlos neu gestartet werden, ohne auf das Ende der Sitzung zu warten.
Zalenium hat andere Einschränkungen. Beispielsweise wird Quota nicht unterstützt. Sie können nicht zwei Kopien davon für einen Load Balancer ablegen, da er nicht weiß, wie er seinen Status auf zwei oder mehr "Köpfe" verteilen soll. Und im Allgemeinen ist es schwierig, mit seinem Cluster zu beginnen. Zalenium verwendet PersistentVolume zum Speichern von Daten: Protokollen und aufgezeichneten Videotests. Dies betrifft jedoch hauptsächlich Festplatten in den Clouds und nicht das fehlertolerantere S3.
Automatische Testinfrastruktur
Die aktuelle Infrastruktur mit Moon und OpenShift sieht wie folgt aus:

Der Benutzer kann Tests sowohl lokal als auch über einen CI-Server ausführen (in unserem Fall Jenkins, aber möglicherweise auch andere). In beiden Fällen verwenden wir RemoteWebDriver, um auf OpenShift zuzugreifen, in dem der Dienst mit mehreren Moon-Replikaten bereitgestellt wird. Außerdem wird die Anforderung, in der der von uns benötigte Browser angegeben ist, in Moon verarbeitet, wodurch die Kubernetes-API die Erstellung eines Herds mit diesem Browser initiiert. Dann leitet Moon Anfragen direkt an den Container weiter, in dem die Tests bestanden werden.
Am Ende des Laufs endet die Sitzung, unter werden gelöscht, Ressourcen werden freigegeben.
Starten Sie den Internet Explorer
Natürlich gab es einige Schwierigkeiten. Wie bereits erwähnt, ist der Zielbrowser für uns der Internet Explorer. Die meisten unserer Anwendungen verwenden ActiveX-Komponenten. Da wir OpenShift verwenden, laufen unsere Docker-Container unter RedHat Enterprise Linux. Daher stellt sich die Frage: Wie starte ich Internet Explorer im Docker-Container, wenn der Host-Computer unter Linux läuft?
Die Mitarbeiter des Moon-Entwicklungsteams teilten ihre Entscheidung, Internet Explorer und Microsoft Edge zu starten.
Der Nachteil dieser Lösung besteht darin, dass der Docker-Container im privilegierten Modus ausgeführt werden sollte. Das Initialisieren des Containers mit Internet Explorer nach dem Start des Tests dauert 10 Sekunden. Dies ist 30-mal schneller als bei Verwendung der vorherigen Infrastruktur.
Fehlerbehebung
Abschließend möchten wir Ihnen Lösungen für einige der Probleme vorstellen, die bei der Bereitstellung und Konfiguration des Clusters aufgetreten sind.
Das erste Problem ist die Verteilung von Service-Images. Wenn der Mond die Erstellung des Browsers initiiert, werden zusätzlich zu dem Container mit dem Browser zusätzliche Service-Container gestartet - ein Logger, ein Verteidiger, ein Videorecorder.

All dies wird in einem Pod gestartet. Wenn die Images dieser Container nicht auf den Knoten zwischengespeichert werden, werden sie vom Docker-Hub bereitgestellt. Zu diesem Zeitpunkt fiel alles auf uns, weil das interne Netzwerk genutzt wurde. Daher haben die Jungs von Aerokube diese Einstellung schnell in die Kartenkonfiguration übernommen. Wenn Sie auch ein internes Netzwerk verwenden, empfehlen wir Ihnen, diese Images in Ihre Registrierung zu löschen und den Pfad zu ihnen in der Moon-Config-Konfigurationszuordnung anzugeben. In der Datei service.json müssen Sie den Abschnitt images hinzufügen:
"images": { "videoRecorder": "ufs-selenoid-cluster/moon-video-recorder:latest", "defender": "ufs-selenoid-cluster/defender:latest", "logger": "ufs-selenoid-cluster/logger:latest" }
Das folgende Problem wurde bereits zu Beginn der Tests festgestellt. Die gesamte Infrastruktur wurde dynamisch erstellt, der Test stürzte jedoch nach 30 Sekunden mit dem folgenden Fehler ab:
Driver info: org.openqa.selenium.remote.RemoteWebDriver Org.openqa.selenium.WebDriverException: <html><body><h1>504 Gateway Time-out</h1> The server didn't respond in time.
Warum ist das passiert? Tatsache ist, dass sich der Test über RemoteWebDriver zunächst auf die Routing-Schicht OpenShift bezieht, die für die Interaktion mit der externen Umgebung verantwortlich ist. Die Rolle dieser Ebene ist Haproxy, das Anforderungen an die von uns benötigten Container umleitet. In der Praxis wandte sich der Test dieser Ebene zu und wurde zu unserem Container umgeleitet, der einen Browser erstellen sollte. Aber er konnte es nicht schaffen, da die Ressourcen knapp wurden. Daher wurde der Test in die Warteschlange gestellt, und nach 30 Sekunden löschte der Proxyserver ihn nach Zeitüberschreitung, da dies standardmäßig dieses Zeitintervall war.

Wie kann man das lösen? Alles stellte sich als recht einfach heraus - Sie mussten lediglich die Annotation haproxy.router.openshift.io/timeout für den Router unseres Containers neu definieren.
$oc annotate route moon --overwrite haproxy.router.openshift.io/timeout=10m
Der nächste Fall ist die Arbeit mit S3-kompatiblem Speicher. Moon kann mit dem Browser aufzeichnen, was im Container passiert. Auf einem Knoten steigen Service-Container zusammen mit dem Browser an, von denen einer ein Videorecorder ist. Es zeichnet alles auf, was im Container passiert, und sendet nach dem Ende der Sitzung Daten an den S3-kompatiblen Speicher. Um Daten an einen solchen Speicher zu senden, müssen Sie in den Einstellungen die URL, die Weichenkennwörter und den Namen des Warenkorbs angeben.
Es scheint, dass alles einfach ist. Wir haben die Daten eingegeben und mit dem Ausführen der Tests begonnen, aber es befanden sich keine Dateien im Repository. Nach der Analyse der Protokolle stellten wir fest, dass der Client, der mit S3 interagierte, auf das Fehlen von Zertifikaten schwor, da wir im URL-Feld die Adresse für S3 mit https angegeben haben. Die Lösung besteht darin, einen ungeschützten http-Modus anzugeben oder Ihre Zertifikate zum Container hinzuzufügen. Die letztere Option ist schwieriger, wenn Sie nicht wissen, was sich im Container befindet und wie alles funktioniert.
Und schließlich ...
Jeder Browser-Container kann unabhängig konfiguriert werden - alle verfügbaren Parameter finden Sie in der Moon-Dokumentation. Achten wir auf benutzerdefinierte Einstellungen wie privileged und nodeSelector.
Sie werden dafür benötigt. Ein Container mit Internet Explorer sollte, wie oben erwähnt, nur im privilegierten Modus ausgeführt werden. Der Betrieb im erforderlichen Modus wird durch das privilegierte Flag zusammen mit der Erteilung von Rechten zum Starten solcher Container für das Dienstkonto bereitgestellt.
Um auf separaten Knoten ausgeführt zu werden, müssen Sie nodeSelector registrieren:
"internet explorer": { "default": "latest", "versions": { "latest": { "image": "docker-registry.default.svc:5000/ufs-selenoid-cluster/windows:7", "port": "4444", "path": "/wd/hub", "nodeSelector": { "kubernetes.io/hostname": "nirvana5.ca.sbrf.ru" }, "volumes": ["/var/lib/docker/selenoid:/image"], "privileged": true } } }
Letzter Tipp. Verfolgen Sie die Anzahl der laufenden Sitzungen. Wir zeigen alle Starts in Grafana an:

Wohin gehen wir?
Wir sind nicht mit allem in der aktuellen Infrastruktur zufrieden, und die Lösung kann noch nicht als vollständig bezeichnet werden. In naher Zukunft planen wir, den IE in Docker zu stabilisieren, eine „reichhaltige“ Benutzeroberfläche in Moon zu erhalten und Appium auch für mobile Autotests zu testen.