Le 14 décembre, lors d'un rassemblement à Saint-Pétersbourg, moi (Artem Sokovets), avec mon collègue Dmitry Markelov, avons parlé de l'infrastructure actuelle pour les autotests dans SberTech. Un récit de notre discours est dans ce post.

Qu'est-ce que le sélénium
Selenium est un outil d'automatisation de navigateur Web. Aujourd'hui, cet outil est la norme pour l'automatisation WEB.

Il existe de nombreux clients pour différents langages de programmation qui prennent en charge l'API Selenium Webdriver. Grâce à l'API WebDriver, via le protocole JSON Wire, une interaction se produit avec le pilote du navigateur sélectionné, qui, à son tour, fonctionne avec un navigateur déjà réel, effectuant les actions dont nous avons besoin.
Aujourd'hui, la version stable du client est Selenium 3.X.

Soit dit en passant, Simon Stewart a promis de présenter Selenium 4.0 lors de la conférence
SeleniumConf Japan .
Sélénium GRID
En 2008, Philippe Hanrigou a annoncé le Selenium GRID pour construire une infrastructure d'autotests avec prise en charge de différents navigateurs.

Selenium GRID se compose d'un concentrateur et de nœuds (nœuds). Le nœud est juste un processus java. Il peut être sur la même machine avec le Hub, il peut être sur une autre machine, il peut être dans le conteneur Docker. Un concentrateur est essentiellement un équilibreur pour les autotests, qui détermine le nœud auquel l'exécution d'un test particulier doit être envoyée. Vous pouvez y connecter des émulateurs mobiles.
Selenium GRID vous permet d'exécuter des tests sur différents systèmes d'exploitation et différentes versions de navigateurs. Cela permet également de gagner beaucoup de temps lors de l'exécution d'un grand nombre d'autotests, si, bien sûr, les autotests sont exécutés en parallèle à l'aide du plug-in maven-surfire ou d'un autre mécanisme de parallélisation.
Bien sûr, Selenium GRID a ses inconvénients. Lors de l'utilisation de l'implémentation standard, il faut faire face aux problèmes suivants:
- redémarrage constant du concentrateur et du nœud. Si le concentrateur et le nœud ne sont pas utilisés pendant une longue période, alors avec une connexion ultérieure, des situations sont possibles lorsque, lors de la création d'une session sur un nœud, cette même session tombe en timeout. Pour restaurer le travail, un redémarrage est nécessaire;
- limite sur le nombre de nœuds. Dépend fortement des tests et des paramètres de la grille. Sans danser avec un tambourin, il commence à ralentir avec plusieurs dizaines de nœuds connectés;
- maigre fonctionnalité;
- l'impossibilité de mise à jour sans arrêt complet du service.
Infrastructure initiale d'AutoTest chez SberTech
Plus tôt dans SberTech, il y avait l'infrastructure suivante pour les tests UI-auto. L'utilisateur a démarré l'assemblage sur Jenkins, qui, à l'aide du plugin, s'est tourné vers OpenStack pour allouer la machine virtuelle. Les VM ont été sélectionnées avec une «image» spéciale et le navigateur souhaité, et ce n'est qu'alors que des autotests ont été effectués sur cette VM.
Si vous vouliez exécuter des tests dans les navigateurs Chrome ou FireFox, les conteneurs Docker se démarquaient. Mais lorsque vous travaillez avec IE, vous avez dû générer une machine virtuelle «propre», ce qui a pris jusqu'à 5 minutes. Malheureusement, Internet Explorer est un navigateur prioritaire dans notre entreprise.

Le principal problème était que cette approche prenait beaucoup de temps lors de l'exécution d'autotests dans IE. J'ai dû séparer les tests sur les suites et démarrer les assemblages en parallèle pour obtenir au moins une certaine réduction de temps. Nous avons commencé à penser à la modernisation.
Nouvelles exigences d'infrastructure
En visitant diverses conférences sur l'automatisation, le développement et DevOps (Heisenbug, SQA Days, CodeOne, SeleniumConf et autres), nous avons progressivement constitué une liste d'exigences pour la nouvelle infrastructure:
- Réduisez le temps d'exécution des tests de régression;
- Fournir un point d'entrée unique pour les autotests, ce qui facilitera leur débogage pour un spécialiste de l'automatisation. Il n'y a pas de rares cas où tout fonctionne localement, et dès que les tests entrent dans le pipeline - chutes continues.
- Fournir une compatibilité entre navigateurs et une automatisation mobile (tests Appium).
- Restez fidèle à l'architecture cloud de la banque: les conteneurs Docker doivent être gérés dans OpenShift.
- Réduisez la mémoire et la consommation du processeur.
Un bref aperçu des solutions existantes
Après avoir défini les tâches, nous avons analysé les solutions existantes sur le marché. Les principales choses que nous avons examinées étaient les produits de l'équipe
Aerokube (Selenoid et Moon), les solutions Alfalab (Alpha Laboratory),
JW-Grid (Avito) et
Zalenium .
Le principal inconvénient de Selenoid était le manque de support pour OpenShift (un wrapper sur Kubernetes). À propos de la décision Alfalab, il y a
un article sur Habré . Il s'est avéré qu'il s'agissait de la même grille de sélénium. La solution d'Avito est décrite dans l'
article . Nous avons vu le rapport à ce sujet lors de la conférence de Heisenbug. Il y avait aussi des inconvénients que nous n'aimions pas. Zalenium est un projet open source, non seulement sans problème.
Les avantages et les inconvénients des solutions que nous considérons sont résumés dans le tableau:

En conséquence, nous avons opté pour un produit d'Aerokube - Selenoid.
Selenoid vs Moon
Pendant quatre mois, nous avons utilisé Selenoid pour automatiser l'écosystème Sberbank. C'est une bonne solution, mais la Banque s'oriente vers OpenShift, et le déploiement de Selenoid dans OpenShift n'est pas une tâche triviale. La subtilité est que Selenoid dans Kubernetes gère le docker de ce dernier, et Kubernetes n'en sait rien et ne peut pas plaisanter correctement les autres nœuds. De plus, Selenoid dans Kubernetes nécessite un GGR (Go Grid Router) dans lequel l'équilibrage de charge est boiteux.
Après avoir expérimenté avec Selenoid, nous nous sommes intéressés à l'outil Moon payant, qui se concentre spécifiquement sur le travail avec Kubernetes et présente plusieurs avantages par rapport au Selenoid gratuit. Il se développe depuis deux ans maintenant et vous permet de déployer l'infrastructure pour l'interface utilisateur de test Selenium sans dépenser d'argent pour les ingénieurs DevOps qui ont des connaissances secrètes sur la façon de déployer Selenoid dans Kubernetes. Il s'agit d'un avantage important - essayez de mettre à niveau le cluster Selenoid sans temps d'arrêt et de réduire la capacité lors de l'exécution des tests?

La lune n'était pas la seule option. Par exemple, vous pouvez prendre le Zalenium mentionné ci-dessus, mais en fait c'est la même grille de sélénium. Il a une liste complète des sessions à l'intérieur du concentrateur stockées à l'intérieur, et si le concentrateur se bloque, les tests se terminent. Dans ce contexte, la Lune gagne du fait qu'elle n'a pas d'état interne, donc la chute d'une de ses répliques est généralement imperceptible. La Lune a tout "gracieusement" - elle peut être redémarrée sans crainte, sans attendre la fin de la session.
Le zalénium a d'autres limites. Par exemple, il ne prend pas en charge Quota. Vous ne pouvez pas en mettre deux copies pour un équilibreur de charge, car il ne sait pas comment répartir son état entre deux ou plusieurs «têtes». Et en général, il est difficile de démarrer sur son cluster. Zalenium utilise PersistentVolume pour stocker des données: journaux et tests vidéo enregistrés, mais cela concerne principalement les disques dans les nuages, et non le S3 plus tolérant aux pannes.
Infrastructure de test automatique
L'infrastructure actuelle utilisant Moon et OpenShift est la suivante:

L'utilisateur peut exécuter des tests à la fois localement et en utilisant un serveur CI (dans notre cas, Jenkins, mais il peut y en avoir d'autres). Dans les deux cas, nous utilisons RemoteWebDriver pour accéder à OpenShift, dans lequel le service avec plusieurs répliques Moon est déployé. De plus, la demande dans laquelle le navigateur dont nous avons besoin est indiqué est traitée dans Moon, à la suite de quoi l'API Kubernetes lance la création d'un foyer avec ce navigateur. Ensuite, Moon envoie directement les requêtes au conteneur, où les tests réussissent.
À la fin de l'exécution, la session se termine, sous sont supprimés, les ressources sont libérées.
Lancer Internet Explorer
Bien sûr, il y a eu quelques difficultés. Comme mentionné précédemment, le navigateur cible pour nous est Internet Explorer - la plupart de nos applications utilisent des composants ActiveX. Depuis que nous utilisons OpenShift, nos conteneurs Docker fonctionnent sur RedHat Enterprise Linux. Ainsi, la question se pose: comment démarrer Internet Explorer dans le conteneur Docker lorsque la machine hôte est sous Linux?
Les gars de l'équipe de développement Moon ont partagé leur décision de lancer Internet Explorer et Microsoft Edge.
L'inconvénient de cette solution est que le conteneur Docker doit s'exécuter en mode privilégié. Ainsi, il faut 10 secondes pour initialiser le conteneur avec Internet Explorer après le démarrage du test, ce qui est 30 fois plus rapide que l'utilisation de l'infrastructure précédente.
Dépannage
En conclusion, nous aimerions partager avec vous des solutions à certains des problèmes que nous avons rencontrés lors du déploiement et de la configuration du cluster.
Le premier problème est la distribution des images de service. Lorsque la lune lance la création du navigateur, en plus du conteneur avec le navigateur, des conteneurs de services supplémentaires sont lancés - un enregistreur, un défenseur, un enregistreur vidéo.

Tout cela est lancé dans un seul pod. Et si les images de ces conteneurs ne sont pas mises en cache sur les nœuds, elles seront fournies à partir du hub Docker. A ce stade, tout est tombé pour nous, car le réseau interne a été utilisé. Par conséquent, les gars d'Aerokube ont rapidement mis ce paramètre dans la configuration de la carte. Si vous utilisez également un réseau interne, nous vous conseillons de vider ces images dans votre registre et de spécifier le chemin d'accès à celles-ci dans la carte de configuration moon-config. Dans le fichier service.json, vous devez ajouter la section images:
"images": { "videoRecorder": "ufs-selenoid-cluster/moon-video-recorder:latest", "defender": "ufs-selenoid-cluster/defender:latest", "logger": "ufs-selenoid-cluster/logger:latest" }
Le problème suivant a déjà été identifié au début des tests. L'infrastructure entière a été créée dynamiquement, mais le test s'est écrasé après 30 secondes avec l'erreur suivante:
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.
Pourquoi est-ce arrivé? Le fait est que le test via RemoteWebDriver se réfère initialement à la couche de routage OpenShift, qui est chargée d'interagir avec l'environnement externe. Le rôle de cette couche est Haproxy, qui redirige les demandes vers les conteneurs dont nous avons besoin. En pratique, le test s'est tourné vers cette couche, elle a été redirigée vers notre container, qui devait créer un navigateur. Mais il n'a pas pu le créer, car les ressources étaient épuisées. Par conséquent, le test est entré dans la file d'attente, et après 30 secondes, le serveur proxy l'a supprimé par timeout, car par défaut, c'était cet intervalle de temps.

Comment résoudre ça? Tout s'est avéré assez simple - il vous suffit de redéfinir l'annotation haproxy.router.openshift.io/timeout pour le routeur de notre conteneur.
$oc annotate route moon --overwrite haproxy.router.openshift.io/timeout=10m
Le cas suivant est un travail avec un stockage compatible S3. Moon est capable d'enregistrer ce qui se passe dans le conteneur avec le navigateur. Sur un nœud, les conteneurs de services montent avec le navigateur, dont l'un est un enregistreur vidéo. Il enregistre tout ce qui se passe dans le conteneur et après la fin de la session envoie des données au stockage compatible S3. Pour envoyer des données vers un tel stockage, vous devez spécifier l'URL, les mots de passe de participation et le nom du panier dans les paramètres.
Il semble que tout soit simple. Nous avons entré les données et commencé à exécuter les tests, mais il n'y avait aucun fichier dans le référentiel. Après avoir analysé les journaux, nous avons réalisé que le client avait l'habitude d'interagir avec S3 juré par le manque de certificats, car dans le champ url, nous avions spécifié l'adresse de S3 avec https. La solution consiste à spécifier un mode http non protégé ou à ajouter vos certificats au conteneur. Cette dernière option est plus difficile si vous ne savez pas ce qui se trouve dans le conteneur et comment tout cela fonctionne.
Et enfin ...
Chaque conteneur de navigateur peut être configuré indépendamment - tous les paramètres disponibles sont dans la documentation de Moon. Prenons attention aux paramètres personnalisés tels que privileged et nodeSelector.
Ils sont nécessaires pour cela. Un conteneur avec Internet Explorer, comme mentionné ci-dessus, ne doit s'exécuter qu'en mode privilégié. Le fonctionnement dans le mode requis est fourni par l'indicateur privilégié avec la délivrance de droits pour lancer de tels conteneurs sur le compte de service.
Pour exécuter sur des nœuds distincts, vous devez enregistrer nodeSelector:
"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 } } }
Dernier conseil. Gardez une trace du nombre de sessions en cours. Nous affichons tous les lancements à Grafana:

Où allons nous
Nous ne sommes pas satisfaits de tout dans l'infrastructure actuelle et la solution ne peut pas encore être qualifiée de complète. Dans un avenir proche, nous prévoyons de stabiliser IE dans Docker, d'obtenir une interface utilisateur «riche» dans Moon et de tester également Appium pour les autotests mobiles.