El 14 de diciembre, en un mitin en San Petersburgo, yo (Artem Sokovets), junto con mi colega, Dmitry Markelov, hablé sobre la infraestructura actual para las pruebas automáticas en SberTech. Un recuento de nuestro discurso está en esta publicación.

¿Qué es el selenio?
Selenium es una herramienta de automatización del navegador web. Hoy, esta herramienta es el estándar para la automatización WEB.

Hay muchos clientes para varios lenguajes de programación que admiten la API Selenium Webdriver. A través de la API WebDriver, a través del protocolo JSON Wire, se produce una interacción con el controlador del navegador seleccionado, que, a su vez, funciona con un navegador ya real, realizando las acciones que necesitamos.
Hoy, la versión estable del cliente es Selenium 3.X.

Simon Stewart, por cierto, prometió presentar Selenium 4.0 en la conferencia
SeleniumConf Japón .
Rejilla de selenio
En 2008, Philippe Hanrigou anunció el Selenium GRID para construir una infraestructura para pruebas automáticas con soporte para varios navegadores.

Selenium GRID consta de un concentrador y nodos (nodos). Node es solo un proceso de Java. Puede estar en la misma máquina con el Hub, puede estar en otra máquina, puede estar en el contenedor Docker. Un concentrador es esencialmente un equilibrador para las pruebas automáticas, que determina el nodo al que se debe enviar la ejecución de una prueba en particular. Puedes conectarle emuladores móviles.
Selenium GRID le permite ejecutar pruebas en diferentes sistemas operativos y diferentes versiones de navegadores. También ahorra mucho tiempo al ejecutar una gran cantidad de pruebas automáticas, si, por supuesto, las pruebas automáticas se ejecutan en paralelo utilizando el complemento maven-surfire u otro mecanismo de paralelización.
Por supuesto, Selenium GRID tiene sus inconvenientes. Cuando se usa la implementación estándar, uno tiene que enfrentar los siguientes problemas:
- reinicio constante de hub y nodo. Si el concentrador y el nodo no se usan durante mucho tiempo, con una conexión posterior, es posible que, al crear una sesión en un nodo, esta misma sesión caiga en el tiempo de espera. Para restaurar el trabajo, se requiere un reinicio;
- límite en el número de nodos. Depende mucho de las pruebas y la configuración de la cuadrícula. Sin bailar con una pandereta, comienza a disminuir con varias docenas de nodos conectados;
- escasa funcionalidad;
- La imposibilidad de actualizar sin una parada completa del servicio.
Infraestructura inicial de AutoTest en SberTech
Anteriormente en SberTech existía la siguiente infraestructura para las pruebas automáticas de UI. El usuario comenzó el ensamblaje en Jenkins, quien, utilizando el complemento, recurrió a OpenStack para asignar la máquina virtual. Las máquinas virtuales se seleccionaron con una "imagen" especial y el navegador deseado, y solo entonces se ejecutaron las pruebas automáticas en esta máquina virtual.
Si desea ejecutar pruebas en los navegadores Chrome o Firefox, los contenedores Docker se destacaron. Pero cuando trabajaba con IE, tenía que generar una máquina virtual "limpia", que tardó hasta 5 minutos. Lamentablemente, Internet Explorer es un navegador prioritario en nuestra empresa.

El principal problema era que este enfoque requería mucho tiempo cuando se ejecutaban las pruebas automáticas en IE. Tuve que separar las pruebas en las suites y comenzar los ensambles en paralelo para lograr al menos una reducción en el tiempo. Comenzamos a pensar en la modernización.
Nuevos requisitos de infraestructura
Al visitar varias conferencias sobre automatización, desarrollo y DevOps (Heisenbug, SQA Days, CodeOne, SeleniumConf y otros), formamos gradualmente una lista de requisitos para la nueva infraestructura:
- Reduzca el tiempo para ejecutar pruebas de regresión;
- Proporcione un único punto de entrada para las pruebas automáticas, lo que facilitará su depuración para un especialista en automatización. No hay casos raros cuando todo funciona localmente, y tan pronto como las pruebas entran en la tubería, caídas continuas.
- Proporcionar compatibilidad entre navegadores y automatización móvil (pruebas de Appium).
- Apéguese a la arquitectura de nube del banco: los contenedores Docker deben administrarse en OpenShift.
- Reduce el consumo de memoria y CPU.
Una breve descripción de las soluciones existentes.
Una vez definidas las tareas, analizamos las soluciones existentes en el mercado. Lo principal que examinamos fueron los productos del equipo de
Aerokube (Selenoid y Moon), las soluciones de Alfalab (Alpha Laboratory),
JW-Grid (Avito) y
Zalenium .
La desventaja clave de Selenoid fue la falta de soporte para OpenShift (un contenedor sobre Kubernetes). Sobre la decisión de Alfalab hay
un artículo sobre Habré . Resultó ser la misma Selenium Grid. La solución de Avito se describe en el
artículo . Vimos el informe al respecto en la conferencia de Heisenbug. También tenía contras que no nos gustaban. Zalenium es un proyecto de código abierto, también no exento de problemas.
Los pros y los contras de las soluciones consideradas por nosotros se resumen en la tabla:

Como resultado, optamos por un producto de Aerokube - Selenoid.
Selenoide vs Luna
Durante cuatro meses, utilizamos Selenoid para automatizar el ecosistema Sberbank. Esta es una buena solución, pero el Banco se está moviendo hacia OpenShift, y desplegar Selenoid en OpenShift no es una tarea trivial. La sutileza es que Selenoid en Kubernetes maneja el acoplador de este último, y Kubernetes no sabe nada al respecto y no puede bromear correctamente con otros nodos. Además, Selenoid en Kubernetes requiere un GGR (Go Grid Router) en el que el equilibrio de carga es poco convincente.
Después de experimentar con Selenoid, nos interesamos en la herramienta Moon pagada, que se centra específicamente en trabajar con Kubernetes y tiene una serie de ventajas en comparación con el Selenoid gratuito. Se ha desarrollado durante dos años y le permite implementar la infraestructura para la interfaz de usuario de prueba de Selenium sin gastar dinero en ingenieros de DevOps que tienen conocimiento secreto sobre cómo implementar Selenoid en Kubernetes. Esta es una ventaja importante: ¿intente actualizar el clúster Selenoid sin tiempo de inactividad y reducir la capacidad al ejecutar pruebas?

Moon no era la única opción. Por ejemplo, podría tomar el Zalenium mencionado anteriormente, pero de hecho es el mismo Selenium Grid. Tiene una lista completa de sesiones dentro del concentrador almacenado dentro de él, y si el concentrador falla, entonces las pruebas finalizan. En este contexto, Moon gana debido al hecho de que no tiene un estado interno, por lo que la caída de una de sus réplicas es generalmente imperceptible. Moon lo tiene todo "con gracia": se puede reiniciar sin miedo, sin esperar el final de la sesión.
Zalenium tiene otras limitaciones. Por ejemplo, no admite Cuota. No puede colocar dos copias para un equilibrador de carga, porque él no sabe cómo distribuir su estado entre dos o más "cabezas". Y, en general, es difícil comenzar en su clúster. Zalenium utiliza PersistentVolume para almacenar datos: registros y pruebas de video grabadas, pero esto se refiere principalmente a discos en las nubes, y no al S3 más tolerante a fallas.
Infraestructura de prueba automática
La infraestructura actual que usa Moon y OpenShift es la siguiente:

El usuario puede ejecutar pruebas tanto localmente como utilizando un servidor CI (en nuestro caso, Jenkins, pero puede haber otros). En ambos casos, utilizamos RemoteWebDriver para acceder a OpenShift, en el que se implementa el servicio con varias réplicas de Moon. Además, la solicitud en la que se indica el navegador que necesitamos se procesa en Moon, como resultado de lo cual la API de Kubernetes inicia la creación de un hogar con este navegador. Entonces Moon envía directamente las solicitudes al contenedor, donde pasan las pruebas.
Al final de la ejecución, la sesión finaliza, bajo se eliminan, los recursos se liberan.
Inicie Internet Explorer
Por supuesto, hubo algunas dificultades. Como se mencionó anteriormente, el navegador de destino para nosotros es Internet Explorer: la mayoría de nuestras aplicaciones usan componentes ActiveX. Como usamos OpenShift, nuestros contenedores Docker se ejecutan en RedHat Enterprise Linux. Por lo tanto, surge la pregunta: ¿cómo iniciar Internet Explorer en el contenedor Docker cuando la máquina host está en Linux?
Los chicos del equipo de desarrollo de Moon compartieron su decisión de lanzar Internet Explorer y Microsoft Edge.
La desventaja de esta solución es que el contenedor Docker debe ejecutarse en modo privilegiado. Por lo tanto, se tarda 10 segundos en inicializar el contenedor con Internet Explorer después de comenzar la prueba, que es 30 veces más rápido que usar la infraestructura anterior.
Solución de problemas
En conclusión, nos gustaría compartir con usted soluciones a algunos de los problemas que encontramos durante la implementación y configuración del clúster.
El primer problema es la distribución de imágenes de servicio. Cuando la luna inicia la creación del navegador, además del contenedor con el navegador, se lanzan contenedores de servicios adicionales: un registrador, un defensor, una grabadora de video.

Todo esto se lanza en una sola cápsula. Y si las imágenes de estos contenedores no se almacenan en caché en los nodos, se entregarán desde el concentrador Docker. En esta etapa, todo nos enamoró, porque se utilizó la red interna. Por lo tanto, los chicos de Aerokube rápidamente pusieron esta configuración en la configuración del mapa. Si también utiliza una red interna, le recomendamos que vacíe estas imágenes en su registro y especifique la ruta a ellas en el mapa de configuración de configuración de la luna. En el archivo service.json, debe agregar la sección de imágenes:
"images": { "videoRecorder": "ufs-selenoid-cluster/moon-video-recorder:latest", "defender": "ufs-selenoid-cluster/defender:latest", "logger": "ufs-selenoid-cluster/logger:latest" }
El siguiente problema ya se identificó al comienzo de las pruebas. Toda la infraestructura se creó dinámicamente, pero la prueba se bloqueó después de 30 segundos con el siguiente error:
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.
¿Por qué sucedió esto? El hecho es que la prueba a través de RemoteWebDriver se refiere inicialmente a la capa de enrutamiento OpenShift, que es responsable de interactuar con el entorno externo. El papel de esta capa es Haproxy, que redirige las solicitudes a los contenedores que necesitamos. En la práctica, la prueba se dirigió a esta capa, se redirigió a nuestro contenedor, que se suponía que debía crear un navegador. Pero no pudo crearlo, ya que los recursos se agotaron. Por lo tanto, la prueba entró en la cola y, después de 30 segundos, el servidor proxy lo abandonó por tiempo de espera, porque de forma predeterminada era este intervalo de tiempo.

¿Cómo resolver esto? Todo resultó ser bastante simple: solo tenía que redefinir la anotación haproxy.router.openshift.io/timeout para el enrutador de nuestro contenedor.
$oc annotate route moon --overwrite haproxy.router.openshift.io/timeout=10m
El siguiente caso es trabajar con almacenamiento compatible con S3. Moon puede registrar lo que sucede en el contenedor con el navegador. En un nodo, los contenedores de servicios se elevan junto con el navegador, uno de los cuales es una grabadora de video. Registra todo lo que sucede en el contenedor y después del final de la sesión envía datos al almacenamiento compatible con S3. Para enviar datos a dicho almacenamiento, debe especificar la URL, las contraseñas de participación y el nombre de la cesta en la configuración.
Parece que todo es simple. Ingresamos los datos y comenzamos a ejecutar las pruebas, pero no había archivos en el repositorio. Después de analizar los registros, nos dimos cuenta de que el cliente solía interactuar con S3 y juró la falta de certificados, ya que en el campo de URL especificamos la dirección de S3 con https. La solución es especificar un modo http desprotegido o agregar sus certificados al contenedor. La última opción es más difícil si no sabe qué hay en el contenedor y cómo funciona todo.
Y finalmente ...
Cada contenedor de navegador se puede configurar de forma independiente: todos los parámetros disponibles se encuentran en la documentación de Moon. Prestemos atención a configuraciones personalizadas como privilegiado y nodeSelector.
Son necesarios para esto. Un contenedor con Internet Explorer, como se mencionó anteriormente, solo debe ejecutarse en modo privilegiado. La operación en el modo requerido es proporcionada por la bandera privilegiada junto con la emisión de derechos para lanzar dichos contenedores a la cuenta de servicio.
Para ejecutar en nodos separados, debe registrar 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 } } }
Último consejo Mantenga un registro de la cantidad de sesiones en ejecución. Mostramos todos los lanzamientos en Grafana:

A donde vamos
No estamos satisfechos con todo en la infraestructura actual, y la solución aún no se puede llamar completa. En un futuro próximo, planeamos estabilizar IE en Docker, obtener una interfaz de usuario "rica" en Moon y también probar Appium para pruebas automáticas móviles.