Em 14 de dezembro, em um comício em São Petersburgo, eu (Artem Sokovets), juntamente com meu colega Dmitry Markelov, conversamos sobre a atual infraestrutura de autotestes da SberTech. Uma recontagem de nosso discurso está neste post.

O que é selênio
Selenium é uma ferramenta de automação de navegador da web. Hoje, essa ferramenta é o padrão para automação WEB.

Existem muitos clientes para várias linguagens de programação que oferecem suporte à API Selenium Webdriver. Por meio da API WebDriver, por meio do protocolo JSON Wire, ocorre uma interação com o driver do navegador selecionado, que, por sua vez, trabalha com um navegador já real, executando as ações necessárias.
Hoje, a versão estável do cliente é Selenium 3.X.

A propósito, Simon Stewart prometeu apresentar o Selenium 4.0 na conferência
SeleniumConf no Japão .
Selenium GRID
Em 2008, Philippe Hanrigou anunciou o Selenium GRID para construir uma infraestrutura para autotestes com suporte para vários navegadores.

O Selenium GRID consiste em um hub e nós (nós). Nó é apenas um processo java. Ele pode estar na mesma máquina que o Hub, em outra máquina ou no contêiner do Docker. Um hub é essencialmente um balanceador para autotestes, que determina o nó ao qual a execução de um teste específico deve ser enviada. Você pode conectar emuladores móveis a ele.
O Selenium GRID permite executar testes em diferentes sistemas operacionais e diferentes versões de navegadores. Também economiza tempo significativamente ao executar um grande número de autotestes, se, é claro, os autotestes são executados em paralelo usando o maven-surfire-plugin ou outro mecanismo de paralelização.
Obviamente, o Selenium GRID tem suas desvantagens. Ao usar a implementação padrão, é necessário enfrentar os seguintes problemas:
- reinicialização constante do hub e nó. Se o hub e o nó não forem utilizados por um longo período de tempo, em uma conexão subseqüente, as situações serão possíveis quando, ao criar uma sessão em um nó, a mesma sessão cairá no tempo limite. Para restaurar o trabalho, é necessário reiniciar;
- limite no número de nós. Muito dependente de testes e configurações de grade. Sem dançar com um pandeiro, ele começa a desacelerar com várias dezenas de nós conectados;
- escassa funcionalidade;
- a impossibilidade de atualizar sem uma parada completa do serviço.
Infraestrutura inicial de AutoTeste na SberTech
Anteriormente, na SberTech, havia a seguinte infraestrutura para testes automáticos de interface do usuário. O usuário iniciou a montagem em Jenkins, que, usando o plug-in, procurou o OpenStack para alocar a máquina virtual. As VMs foram selecionadas com uma "imagem" especial e o navegador desejado, e somente então os autotestes foram executados nesta VM.
Se você queria executar testes nos navegadores Chrome ou FireFox, os contêineres do Docker se destacavam. Mas, ao trabalhar com o IE, era necessário criar uma VM "limpa", que levava até 5 minutos. Infelizmente, o Internet Explorer é um navegador prioritário em nossa empresa.

O principal problema foi que essa abordagem levou muito tempo ao executar autotestes no IE. Eu tive que separar testes em suítes e iniciar montagens em paralelo para obter pelo menos alguma redução no tempo. Começamos a pensar em modernização.
Novos requisitos de infraestrutura
Visitando várias conferências sobre automação, desenvolvimento e DevOps (Heisenbug, SQA Days, CodeOne, SeleniumConf e outros), gradualmente formamos uma lista de requisitos para a nova infraestrutura:
- Reduza o tempo para executar testes de regressão;
- Forneça um ponto de entrada único para autotestes, o que facilitará sua depuração para um especialista em automação. Não há casos raros em que tudo funciona localmente e, assim que os testes entram no pipeline - quedas contínuas.
- Para fornecer compatibilidade entre navegadores e automação móvel (Appium-tests).
- Atenha-se à arquitetura em nuvem do banco: os contêineres do Docker devem ser gerenciados no OpenShift.
- Reduza o consumo de memória e CPU.
Uma breve visão geral das soluções existentes
Tendo definido as tarefas, analisamos as soluções existentes no mercado. As principais coisas que examinamos foram os produtos da equipe
Aerokube (Selenoid e Moon), soluções Alfalab (Alpha Laboratory),
JW-Grid (Avito) e
Zalenium .
A principal desvantagem do Selenoid foi a falta de suporte ao OpenShift (um invólucro do Kubernetes). Sobre a decisão do Alfalab, há
um artigo sobre Habré . Acabou sendo a mesma grade de selênio. A solução da Avito é descrita no
artigo . Vimos o relatório na conferência Heisenbug. Também tinha contras que não gostávamos. O Zalenium é um projeto de código aberto, também sem problemas.
Os prós e contras das soluções consideradas por nós estão resumidos na tabela:

Como resultado, optamos por um produto da Aerokube - Selenoid.
Selenoid vs Moon
Por quatro meses, usamos o Selenoid para automatizar o ecossistema do Sberbank. Essa é uma boa solução, mas o Banco está adotando o OpenShift, e implantar o Selenoid no OpenShift não é uma tarefa trivial. A sutileza é que o Selenoid no Kubernetes gerencia a janela de encaixe deste último, e o Kubernetes não sabe nada sobre ele e não pode brincar com outros nós corretamente. Além disso, o Selenoid no Kubernetes requer um GGR (Go Grid Router) no qual o balanceamento de carga é fraco.
Depois de experimentar o Selenoid, ficamos interessados na ferramenta Lua paga, focada especificamente no trabalho com o Kubernetes e com várias vantagens em comparação ao Selenoid gratuito. Ele está em desenvolvimento há dois anos e permite implantar a infraestrutura para a interface do usuário de teste do Selenium sem gastar dinheiro com os engenheiros do DevOps que possuem conhecimento secreto sobre como implantar o Selenoid no Kubernetes. Essa é uma vantagem importante - tente atualizar o cluster Selenoid sem tempo de inatividade e reduzir a capacidade ao executar testes?

Moon não era a única opção. Por exemplo, você pode usar o zalênio mencionado acima, mas na verdade é a mesma grade de selênio. Ele possui uma lista completa de sessões dentro do hub armazenadas nele e, se o hub travar, os testes terminam. Nesse contexto, a Lua vence devido ao fato de não ter um estado interno; portanto, a queda de uma de suas réplicas geralmente é imperceptível. A Lua tem tudo "graciosamente" - pode ser reiniciada sem medo, sem esperar o final da sessão.
O zalênio tem outras limitações. Por exemplo, ele não suporta Cota. Você não pode colocar duas cópias para um balanceador de carga, porque ele não sabe como distribuir seu estado entre duas ou mais "cabeças". E, em geral, é difícil começar no cluster. O Zalenium usa o PersistentVolume para armazenar dados: logs e testes de vídeo gravados, mas isso diz respeito principalmente a discos nas nuvens e não ao S3 mais tolerante a falhas.
Infra-estrutura de teste automático
A infraestrutura atual usando Moon e OpenShift é a seguinte:

O usuário pode executar testes localmente e usando um servidor de IC (no nosso caso, Jenkins, mas pode haver outros). Nos dois casos, usamos o RemoteWebDriver para acessar o OpenShift, no qual o serviço com várias réplicas da Moon é implantado. Além disso, a solicitação na qual o navegador que precisamos é indicado é processada no Moon, como resultado da API Kubernetes inicia a criação de uma lareira com este navegador. Em seguida, Moon solicita proxies diretamente ao contêiner, onde os testes passam.
No final da execução, a sessão termina, em são excluídos, os recursos são liberados.
Iniciar o Internet Explorer
Claro, houve algumas dificuldades. Como mencionado anteriormente, o navegador de destino para nós é o Internet Explorer - a maioria de nossos aplicativos usa componentes ActiveX. Como usamos o OpenShift, nossos contêineres do Docker são executados no RedHat Enterprise Linux. Assim, surge a pergunta: como iniciar o Internet Explorer no contêiner Docker quando a máquina host está no Linux?
Os caras da equipe de desenvolvimento da Moon compartilharam sua decisão de lançar o Internet Explorer e o Microsoft Edge.
A desvantagem desta solução é que o contêiner do Docker deve ser executado no modo privilegiado. Portanto, leva 10 segundos para inicializar o contêiner com o Internet Explorer após o início do teste, que é 30 vezes mais rápido que o uso da infraestrutura anterior.
Solução de problemas
Em conclusão, gostaríamos de compartilhar com você soluções para alguns dos problemas que encontramos durante a implantação e configuração do cluster.
O primeiro problema é a distribuição de imagens de serviço. Quando a lua inicia a criação do navegador, além do contêiner com o navegador, lançamos contêineres de serviço adicionais - um registrador, um defensor, um gravador de vídeo.

Tudo isso é lançado em um pod. E se as imagens desses contêineres não forem armazenadas em cache nos nós, elas serão entregues no hub do Docker. Nesta fase, tudo caiu para nós, porque a rede interna foi usada. Portanto, os caras do Aerokube rapidamente colocaram essa configuração na configuração do mapa. Se você também usa uma rede interna, recomendamos que você libere essas imagens em seu registro e indique o caminho para elas na configuração do mapa moon-config. No arquivo service.json, você precisa adicionar a seção de imagens:
"images": { "videoRecorder": "ufs-selenoid-cluster/moon-video-recorder:latest", "defender": "ufs-selenoid-cluster/defender:latest", "logger": "ufs-selenoid-cluster/logger:latest" }
O seguinte problema já foi identificado no início dos testes. Toda a infraestrutura foi criada dinamicamente, mas o teste travou após 30 segundos com o seguinte erro:
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 que isso aconteceu? O fato é que o teste através do RemoteWebDriver se refere inicialmente à camada de roteamento OpenShift, responsável pela interação com o ambiente externo. O papel dessa camada é o Haproxy, que redireciona solicitações para os contêineres de que precisamos. Na prática, o teste virou para essa camada, foi redirecionado para o contêiner, que deveria criar um navegador. Mas ele não conseguiu criá-lo, pois os recursos acabaram. Portanto, o teste entrou na fila e, após 30 segundos, o servidor proxy o deixou cair por tempo limite, porque, por padrão, era esse intervalo de tempo.

Como resolver isso? Tudo acabou sendo bastante simples - você apenas precisou redefinir a anotação haproxy.router.openshift.io/timeout para o roteador do nosso contêiner.
$oc annotate route moon --overwrite haproxy.router.openshift.io/timeout=10m
O próximo caso é o trabalho com armazenamento compatível com S3. Moon é capaz de registrar o que acontece no contêiner com o navegador. Em um nó, os contêineres de serviço aumentam junto com o navegador, um dos quais é um gravador de vídeo. Ele grava tudo o que acontece no contêiner e, após o final da sessão, envia dados para o armazenamento compatível com o S3. Para enviar dados para esse armazenamento, você precisa especificar URL, senhas de participação e o nome da cesta nas configurações.
Parece que tudo é simples. Nós inserimos os dados e começamos a executar os testes, mas não havia arquivos no repositório. Após analisar os logs, percebemos que o cliente costumava interagir com o S3 jurava por falta de certificados, pois no campo url especificamos o endereço para S3 com https. A solução é especificar um modo http desprotegido ou adicionar seus certificados ao contêiner. A última opção é mais difícil se você não souber o que está no contêiner e como tudo funciona.
E finalmente ...
Cada contêiner de navegador pode ser configurado de forma independente - todos os parâmetros disponíveis estão na documentação do Moon. Vamos prestar atenção a configurações personalizadas como privilegiado e nodeSelector.
Eles são necessários para isso. Um contêiner com o Internet Explorer, como mencionado acima, deve ser executado apenas no modo privilegiado. A operação no modo necessário é fornecida pelo sinalizador privilegiado, juntamente com a emissão de direitos para iniciar esses contêineres na conta de serviço.
Para executar em nós separados, é necessário registrar o 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 } } }
Última dica. Acompanhe o número de sessões em execução. Exibimos todos os lançamentos em Grafana:

Para onde vamos
Não estamos satisfeitos com tudo na infraestrutura atual e a solução ainda não pode ser considerada completa. Em um futuro próximo, planejamos estabilizar o IE no Docker, obter uma interface de interface do usuário "rica" na Lua e também testar o Appium para autotestes móveis.