Selenium na Lambda: as vantagens, com a conta feita
Uma parte do que eu mantenho é um robô que trabalha sozinho, 24 horas por dia, para vários clientes. Ele faz o que uma pessoa faria na mão: entra numa plataforma com o login do cliente e executa a tarefa repetitiva que aquele cliente precisa. Para entrar, ele usa as credenciais que o próprio usuário cadastrou no sistema.
O problema aparecia sempre do mesmo jeito. O usuário digitava a senha errada, salvava, e ninguém percebia nada. Horas depois, quase sempre de madrugada, o robô chegava na vez daquele cliente, tentava entrar, não conseguia, e travava. Aí começava a parte cara: alguém abria o registro de erro, descobria de quem era a credencial, mandava mensagem no grupo daquele cliente pedindo a senha certa, esperava alguém de lá responder, corrigia o cadastro e mandava rodar de novo.
E isso não era acidente raro. Duas das plataformas obrigam a trocar a senha todo mês. Ou seja, todo mês uma leva de clientes precisa atualizar o cadastro para o robô continuar entrando, e todo mês uma parte deles digita errado. O ciclo estava garantido pelo calendário.
O erro nascia no cadastro e só aparecia no uso
Essa é a única frase que importa nessa história. Entre o momento em que o dado errado entra no sistema e o momento em que ele quebra alguma coisa, podia passar meio dia. Todo o custo estava nesse intervalo: alguém investigando, o cliente sendo incomodado, o trabalho daquele cliente parado.
A tentação é consertar onde dói, que é no robô. Fazer ele tentar de novo, tratar melhor o erro, avisar sozinho quando o login falha. Tudo isso ajuda a sofrer menos, e nada disso resolve: o dado continua entrando errado, a automação continua descobrindo tarde, e o cliente continua recebendo no grupo um pedido de senha que ele digitou de manhã.
Validar no momento do cadastro é mais barato por um motivo simples: naquele instante existe uma pessoa na frente da tela, que sabe a senha e pode corrigir sozinha. Horas depois, essa pessoa já saiu, e recuperar a atenção dela depende de mensagem em grupo, que ninguém responde na hora.
A correção: testar a senha na hora em que ela é salva
O que subi foi um serviço pequeno que faz uma coisa só. Ele abre a tela de login da plataforma num navegador invisível, digita o usuário e a senha que acabaram de ser cadastrados, tenta entrar, e responde se a credencial funciona ou não. Nada além disso: não navega, não coleta nada, não executa a tarefa que o robô executa.
Isso acontece enquanto o usuário ainda está na tela. Ele salva, espera de 10 a 15 segundos, e recebe a resposta. Se a senha estiver errada, corrige ali mesmo, com ela ainda fresca na cabeça. O robô passa a receber só credencial que já entrou uma vez na plataforma.
O serviço roda na AWS Lambda, que é um jeito de hospedar código sem manter servidor ligado: você paga por execução, e entre uma execução e outra não existe nada rodando nem sendo cobrado. Para abrir a tela de login eu uso o Selenium, que controla um navegador de verdade, o mesmo Chrome de sempre, só que sem interface e dentro do servidor.
Hoje isso atende mais de 15 clientes, com média de três plataformas por cliente. Cada plataforma tem a sua própria rotina de validação, porque cada tela de login é diferente. Aí está o custo real desse serviço, e não é o da nuvem: é manter um punhado dessas rotinas funcionando conforme cada plataforma mexe no visual dela.
A conta dos dois lados
Vale fazer as contas, porque "custo baixo" sem número não quer dizer nada.
Do lado da nuvem: o preço da Lambda tem uma faixa gratuita permanente, e uma validação como a minha consome uma fração mínima dela. Cabem mais de 26 mil validações por mês sem pagar nada. O meu volume real é outra ordem de grandeza: são pouco mais de 45 credenciais cadastradas, e a validação só roda quando alguém cadastra ou troca senha. No mês típico, puxado pelas duas plataformas que expiram a senha, isso fica na casa das 50 execuções. Na pior hipótese que consigo imaginar, todo mundo trocando tudo e errando três vezes antes de acertar, não passa de 150.
Se eu apagar a faixa gratuita da conta e pagar tudo pelo preço cheio, o pior mês dá menos de quatro centavos de dólar. O mês típico não chega a dois centavos.
Do outro lado, o que isso substitui. Cada troca de senha malfeita virava uma mensagem no grupo do cliente no dia seguinte. Com duas plataformas expirando todo mês, eram umas trinta dessas por mês, só para pedir uma senha.
E o custo aqui não é escrever a mensagem, é o que acontece depois dela. Grupo não responde na hora: a pergunta fica lá, alguém vê à tarde, pergunta para outra pessoa, responde no dia seguinte. Enquanto isso a automação daquele cliente está parada, e alguém da minha equipe precisa lembrar de voltar no assunto, achar de novo qual era a credencial e refazer o cadastro. Some o tempo espalhado dos dois lados e o desgaste de cobrar o cliente por um problema que ele nem sabe que causou. Contra menos de quatro centavos de dólar no pior mês.
A comparação não é entre nuvem cara e nuvem barata. É entre máquina e gente.
E se fosse um servidor comum?
Foi a primeira alternativa que considerei: alugar uma máquina na nuvem, dessas que ficam ligadas o mês inteiro, e deixar a validação rodando lá. Vale explicar por que não segui por ali, porque a resposta serve para outras decisões parecidas.
O preço é o argumento mais fraco, e mesmo assim é grande: uma máquina que aguente abrir navegador sem apertar custa algo como 24 dólares por mês (tabela da DigitalOcean), contra centavos. Dezenas de dólares contra centavos, todo mês, para sempre.
O segundo argumento é a ociosidade. Essa máquina ficaria ligada 24 horas por dia para trabalhar alguns minutos por mês. Você paga o dia inteiro por uma capacidade que quase nunca é usada.
O terceiro é o que realmente decide: manutenção. Servidor alugado é servidor seu. Tem sistema operacional para atualizar, navegador para manter em versão compatível, disco que enche de arquivo temporário, processo que trava e não morre, e alguém para ser avisado quando qualquer uma dessas coisas acontecer. É trabalho recorrente, e trabalho recorrente para sustentar algo que roda alguns minutos por mês é o pior negócio possível.
Tem ainda a questão do pico. No começo do mês, quando as senhas expiram, vários clientes atualizam o cadastro quase ao mesmo tempo. Num servidor só, cada validação simultânea disputa memória com as outras, e a quarta ou quinta pessoa fica esperando. No modelo por execução, cada validação sobe isolada e ninguém entra em fila. Eu não precisei pensar em capacidade uma única vez.
O contraponto honesto: o robô em si eu não colocaria nesse modelo. Ele roda continuamente, é longo e mantém estado, e pagar por execução curta funciona contra ele. Para o robô, o servidor que eu recusei aqui é a escolha certa. A mesma tecnologia serve para um caso e atrapalha no outro. O que decide é o formato da tarefa, não a ferramenta estar na moda.
Detalhes para quem for repetir isso
Daqui para baixo é técnico, e é o que eu gostaria de ter lido antes de começar.
Para o navegador rodar nesse ambiente eu usei a imagem docker-selenium-lambda, que já empacota Chromium, ChromeDriver e Selenium em versões que funcionam juntas. Esse é o pedaço em que quase todo mundo perde um dia: montar isso à mão dá trabalho, o navegador é grande demais para o formato de pacote simples, e a combinação de versões entre navegador e driver quebra com frequência. Como imagem de container, o problema de tamanho sai do caminho. O Dockerfile tem três linhas:
FROM umihico/aws-lambda-selenium-python:latest
COPY app.py ./
CMD ["app.handler"]
São várias funções pequenas, uma por plataforma, todas saindo dessa mesma imagem: muda só o comando que a função executa e a rota que responde por ela. Configurei 1 GB de memória e tempo limite de 29 segundos, que é o teto do gateway HTTP na frente delas.
Uma das plataformas nem usa navegador. Descobri que dava para chamar direto o endereço de autenticação dela, então essa validação é uma requisição HTTP simples, com 256 MB de memória, e responde numa fração do tempo das outras. Vale sempre olhar a aba de rede antes de escrever a primeira linha de Selenium.
O critério de sucesso é a parte que merece cuidado. Verificar um elemento que só existe depois de logar é confiável; conferir se o endereço mudou nem sempre é. Algumas plataformas nem mostram mensagem de erro, apenas redesenham a tela de login, e nesse caso o jeito é tratar o tempo esgotado como credencial inválida, depois de confirmar na mão que é isso mesmo que acontece.
Duas armadilhas me custaram tempo. A primeira: configure tempo limite em cada camada, não só na função. Se um comando do navegador travar sem resposta, a função estoura sozinha e o seu tratamento de erro nunca roda, então o usuário recebe um erro genérico em vez de "não deu para verificar". A segunda: até o comando que encerra o navegador pode pendurar esperando o processo morrer. Eu resolvi com um alarme do sistema operacional em volta do encerramento.
Guarde também as opções que fazem o Chrome subir nesse ambiente. Sem
--single-process, --no-zygote, --disable-dev-shm-usage e sem apontar
cache e dados para /tmp, que é o único lugar gravável, o navegador não abre.
Duas recomendações que valem para qualquer versão disso:
- Não registre a senha em log. Parece óbvio, e é exatamente o tipo de coisa que escapa num teste que ficou para trás. O retorno precisa ser válido ou inválido, e nada mais.
- Trate o "não sei" como resposta. Plataforma fora do ar, tempo esgotado, mudança de layout: nesses casos a resposta não é "senha inválida", é "não deu para verificar agora". Bloquear o cadastro de um usuário porque o site do outro lado caiu é trocar um problema por outro.
O que ficou
A automação parou de travar por senha errada, e a equipe parou de pedir senha no grupo por causa disso. Não teve nada de sofisticado: um serviço pequeno, uma imagem pronta e a decisão de checar o dado onde ele entra, em vez de onde ele é usado.
É o tipo de correção que quase nunca aparece na lista de tarefas, porque o sintoma aponta para o lugar errado. O chamado diz "a automação travou". O problema estava num formulário, meio dia antes.
- AWS Lambda
- Selenium
- RPA
- custo
- automação