Testen in Yandex. So erstellen Sie aus Tausenden von Browsern ein fehlertolerantes Raster

    Jeder Spezialist, der mit dem Testen von Webanwendungen befasst ist, weiß, dass die meisten Routinemaßnahmen für Dienste das Selenium- Framework ausführen können . In Yandex werden täglich Millionen von Autotests ausgeführt, wobei Selenium für die Arbeit mit Browsern verwendet wird. Daher benötigen wir Tausende verschiedener Browser, die gleichzeitig rund um die Uhr verfügbar sind. Und hier beginnt der Spaß. Selen mit einer großen Anzahl von Browsern hat viele Probleme mit der Skalierung und Fehlertoleranz. Nach mehreren Versuchen haben wir eine elegante und einfach zu wartende Lösung erhalten, die wir mit Ihnen teilen möchten. Mit unserem Gridrouter- Projekt können Sie ein fehlertolerantes Selenium-Gitter in einer beliebigen Anzahl von Browsern organisieren. Der Code ist in Open Source verfügbar und auf Github verfügbar



    . Im Folgenden werde ich Ihnen die Nachteile von Selenium erläutern, auf die wir geachtet haben, wie wir zu unserer Lösung gekommen sind, und erläutern, wie Sie sie konfigurieren.

    Das Problem


    Selen hat sich seit seiner Einführung mehr als einmal dramatisch verändert. Die derzeitige Architektur, das Selenium Grid, funktioniert so.

    Der Cluster besteht aus zwei Anwendungen: einem Hub (Hub) und Knoten (Knoten). Ein Hub ist eine API, die Benutzeranforderungen akzeptiert und an Knoten sendet. Noda ist ein Abfrage-Executor, der Browser startet und darin Testschritte ausführt. Theoretisch kann eine unendliche Anzahl von Knoten mit einem Hub verbunden werden, von denen jeder einen der unterstützten Browser starten kann. Aber was in der Praxis?

    • Es gibt eine Schwachstelle. Der Hub ist der einzige Browser-Zugangspunkt. Wenn der Hub-Prozess aus irgendeinem Grund nicht mehr reagiert, kann nicht mehr auf alle Browser zugegriffen werden. Es ist klar, dass der Dienst auch dann nicht mehr funktioniert, wenn das Rechenzentrum, in dem sich der Hub befindet, einen Netzwerk- oder Stromausfall aufweist.
    • Das Selengitter lässt sich nicht gut skalieren. Unsere langjährige Erfahrung im Betrieb von Selen auf verschiedenen Geräten zeigt, dass ein Hub unter Last mit nicht mehr als mehreren Dutzend verbundenen Knoten arbeiten kann. Wenn Sie weiterhin Knoten hinzufügen, reagiert der Hub bei Spitzenlast möglicherweise nicht mehr über das Netzwerk oder verarbeitet Anforderungen zu langsam.
    • Keine Quote. Sie können keine Benutzer erstellen und angeben, welche Browserversionen welcher Benutzer verwenden kann.

    Lösung


    Um nicht unter dem Sturz eines Hubs zu leiden, können Sie einige erhöhen. Gewöhnliche Bibliotheken für die Arbeit mit Selen sind jedoch so konzipiert, dass sie nur mit einem Hub arbeiten. Sie müssen ihnen daher die Arbeit mit einem verteilten System beibringen.

    Client Balancing


    Zunächst haben wir das Problem gelöst, mit mehreren Hubs mithilfe einer kleinen Bibliothek zu arbeiten, die im Testcode verwendet wurde und auf der Clientseite einen Ausgleich durchführte.

    So funktioniert es:

    1. Informationen zu Hosts mit Hubs und darauf verfügbaren Browserversionen werden in der Konfigurationsdatei gespeichert.
    2. Der Benutzer steckt die Bibliothek in seine Tests ein und fordert einen Browser an.
    3. Ein Host wird zufällig aus der Liste ausgewählt und es wird versucht, einen Browser zu erhalten.
    4. Wenn der Versuch erfolgreich ist, wird der Browser dem Benutzer übergeben und die Tests beginnen.
    5. Wenn der Browser nicht abgerufen werden konnte, wird erneut der nächste Host zufällig ausgewählt usw. Da für verschiedene Hubs möglicherweise eine unterschiedliche Anzahl verfügbarer Browser verfügbar ist, können Hubs in der Konfigurationsdatei unterschiedliche Gewichte zugewiesen werden, und die zufällige Auswahl wird unter Berücksichtigung dieser Gewichte vorgenommen. Dieser Ansatz ermöglicht eine gleichmäßige Lastverteilung.
    6. Der Benutzer erhält nur dann eine Fehlermeldung, wenn der Browser auf keinem der Hubs empfangen werden konnte.

    Die Implementierung eines solchen Algorithmus ist einfach, erfordert jedoch die Integration in jede Bibliothek, um mit Selen arbeiten zu können. Angenommen, in Ihren Tests wird der Browser mit diesem Code angezeigt:

    WebDriver driver = new RemoteWebDriver(SELENIUM_SERVER_URL, capabilities);

    Hier ist RemoteWebDriver die Standardklasse für die Arbeit mit Selenium in Java. Um in unserer Infrastruktur zu arbeiten, müssen Sie sie mit der Auswahl eines Hubs in unseren eigenen Code einbinden:

    WebDriver driver = SeleniumHubFinder.find(capabilities);

    Der Testcode hat vor Selenium keine URL mehr, sondern ist in der Bibliothekskonfiguration enthalten. Dies bedeutet auch, dass der Testcode jetzt an SeleniumHubFinder gebunden ist und ohne ihn nicht gestartet werden kann. Wenn Sie Tests nicht nur in Java, sondern auch in anderen Sprachen durchführen, müssen Sie außerdem für alle einen Client-Balancer schreiben. Dies kann teuer sein. Es ist viel einfacher, den Ausgleichscode auf den Server zu übertragen und seine Adresse im Testcode anzugeben.

    Serverausgleich


    Bei der Gestaltung des Servers haben wir folgende natürliche Anforderungen festgelegt:

    1. Der Server muss die Selenium-API ( JsonWire- Protokoll ) implementieren , damit die Tests damit arbeiten können, wie bei einem regulären Selenium-Hub.
    2. Sie können beliebig viele Serverziele in beliebigen Rechenzentren festlegen und diese mit einem Eisen- oder Software-Balancer (SLB) ausgleichen.
    3. Die Serverköpfe sind völlig unabhängig voneinander und speichern den gemeinsam genutzten Status nicht.
    4. Der sofort einsatzbereite Server bietet Kontingente, dh den unabhängigen Betrieb mehrerer Benutzer.



    Die architektonisch erhaltene Lösung sieht folgendermaßen aus:

    • Ein Load Balancer (SLB) streut Anforderungen von Benutzern an eines von N Zielen, wobei ein Server einen Standardport überwacht (4444).
    • Jedes der Ziele wird in Form von Konfigurationsinformationen zu allen verfügbaren Selenium-Hubs gespeichert.
    • Wenn eine Anforderung beim Browser eingeht, verwendet der Server den im vorherigen Abschnitt beschriebenen Ausgleichsalgorithmus und empfängt den Browser.
    • Jeder laufende Browser in Standard-Selenium erhält eine eigene eindeutige Kennung, die als Sitzungs-ID bezeichnet wird . Dieser Wert wird vom Client auf Anfrage an den Hub übertragen. Nach Erhalt des Browsers ersetzt der Server die aktuelle Sitzungs-ID durch eine neue und enthält zusätzlich Informationen zu dem Hub, auf dem diese Sitzung empfangen wurde. Die empfangene Sitzung mit der erweiterten ID wird dem Client übergeben.
    • Bei den folgenden Anforderungen ruft der Server die Hostadresse mit dem Hub von der Sitzungs-ID ab und sendet Proxy-Anforderungen an diesen Host. Da sich alle für den Server erforderlichen Informationen in der Anforderung selbst befinden, muss der Status der Ziele nicht synchronisiert werden - jeder von ihnen kann unabhängig voneinander arbeiten.

    Gridrouter


    Wir haben den Server Gridrouter angerufen und beschlossen, seinen Code mit allen zu teilen. Der Server wird mit dem Spring Framework in Java geschrieben . Der Quellcode des Projekts kann hier eingesehen werden . Wir haben auch Debian-Pakete vorbereitet , die den Server installieren.

    Derzeit ist Gridrouter als Kampfserver installiert, der von verschiedenen Yandex-Teams verwendet wird. Die Gesamtzahl der verfügbaren Browser in diesem Raster beträgt mehr als dreitausend. Bei Spitzenlasten bedienen wir ungefähr die gleiche Anzahl von Benutzersitzungen.

    So konfigurieren Sie den Gridrouter


    Um den Gridouter zu konfigurieren, müssen Sie für jeden Benutzer eine Liste von Benutzern und Kontingenten angeben . Wir haben uns nicht zum Ziel gesetzt, eine supersichere Authentifizierung mit Hash-Funktionen und Salt durchzuführen. Daher verwenden wir die übliche grundlegende HTTP-Authentifizierung und speichern Anmeldungen und Kennwörter im Klartext in der Textdatei /etc/grid-router/users.properties des Formulars:

    user:password, user
    user2:password2, user
    

    Jede Zeile enthält einen Benutzernamen und ein Kennwort über einen Doppelpunkt sowie die derzeit gleiche Rolle des Benutzers . Bei den Quoten ist auch hier alles sehr einfach. Jedes Kontingent ist eine separate XML-Datei / etc / grid-router / quota /.xmlwo - Benutzername. Die Datei sieht folgendermaßen aus:


    Es ist ersichtlich, dass die Namen und Versionen der verfügbaren Browser bestimmt werden, die genau mit den auf den Hubs angegebenen übereinstimmen müssen. Für jede Version des Browsers werden eine oder mehrere Regionen definiert, dh verschiedene Rechenzentren, in denen jeweils der Host, der Port und die Anzahl der verfügbaren Browser aufgezeichnet sind (dies ist das Gewicht). Der Name der Region kann beliebig sein. Informationen zu den Regionen werden benötigt, wenn eines der Rechenzentren nicht mehr verfügbar ist. In diesem Fall versucht der Gridrouter nach einem erfolglosen Versuch, einen Browser in einer bestimmten Region abzurufen, einen Browser aus einer anderen Region abzurufen. Ein solcher Algorithmus erhöht die Wahrscheinlichkeit eines schnellen Browsers erheblich.

    So führen Sie Tests durch


    Obwohl wir hauptsächlich in Java schreiben, haben wir unseren Server mit Selenium-Tests in anderen Programmiersprachen getestet. In Hub-Hub-Tests wird normalerweise Folgendes angezeigt:

    http://example.com:4444/wd/hub
    

    Da wir die grundlegende HTTP-Authentifizierung verwenden, sollten bei der Arbeit mit Gridrouter die folgenden Links verwendet werden:

    http://username:password@example.com:4444/wd/hub
    

    Wenn Sie Probleme mit der Konfiguration haben, kontaktieren Sie uns bitte und starten Sie das Problem auf Github .

    Empfehlungen zum Einrichten von Hubs und Knoten


    Wir haben Experimente mit verschiedenen Konfigurationen von Hubs und Knoten durchgeführt und sind zu dem Schluss gekommen, dass der folgende Ansatz unter dem Gesichtspunkt der Benutzerfreundlichkeit, Zuverlässigkeit und Skalierbarkeit am praktischsten ist. Normalerweise installieren sie einen Hub, mit dem viele Knoten verbunden sind, da der Einstiegspunkt einer sein muss.



    Wenn Sie einen Gridrouter verwenden, können Sie so viele Hubs platzieren, wie Sie möchten. Am einfachsten ist es also, einen Hub und mehrere mit localhost verbundene Knoten auf einem Computer zu konfigurieren : 4444. Dies ist besonders praktisch, wenn alles auf virtuellen Maschinen bereitgestellt wird. Beispielsweise haben wir festgestellt, dass für eine virtuelle Maschine mit zwei VCPUs und 4 GB Speicher die Kombination aus einem Hub und fünf Knoten optimal ist. Wir installieren nur eine Browserversion auf einer virtuellen Maschine, da es in diesem Fall einfach ist, den Speicherverbrauch zu messen und die Anzahl der virtuellen Maschinen in die Anzahl der verfügbaren Browser zu übersetzen.

    Jetzt auch beliebt: