Gerência de Projetos Dev & TI

11 mai, 2016

Um case sobre automatização de implementação contínua no Instagram

Publicidade

A implementação contínua ainda é uma dessas magias negras que rondam por aí. Alguns defendem, outros condenam e assim continuamos a fazer nossos deploys da maneira que achamos que funciona para nós.

Como comunidade, o importante é experimentarmos, fazermos testes, compartilharmos acertos e erros para ajudar os demais profissionais. Sendo assim, vamos expor um caso de uso de implementação contínua automatizada publicado por Michael Gorven no site do Instagram.

De acordo com Gorven, os engenheiros do Instagram hoje fazem deploy no seu backend de 30 a 50 vezes por dia, sempre que necessário. E o mais interessante: a maioria dos deploys são feitos sem nenhuma intervenção humana. E isso gera uma série de vantagens para eles, como:

  • Os engenheiros não são limitados a horários específicos;
  • Agilidade nos lançamentos;
  • Facilidade na identificação de commits ruins. Eles não precisam procurar em muitas linhas de código, visto que fazem vários commits, e a iteração entre um e outro costuma ser pequena; sendo assim, o escopo de busca fica em torno de uma, no máximo três iterações. Eles conseguem identificar, por meio de métricas, a hora exata do erro e, então, encontrar os commits ruins que foram implementados naquele momento;
  • Com facilidade e agilidade na localização de commits ruins, eles não ficam com aquela bagunça no master, nem causam atrasos. Estão sempre em um estado no qual conseguem obter correções importantes rapidamente.

O início
alex01

Mas antes desse cenário de CNTP que temos acima, havia um cenário um pouco mais obscuro.

Antes de trabalharem com a implantação contínua, os engenheiros implantavam as mudanças em uma base ad-hoc. Ou seja, entregavam as mudanças mais necessárias e faziam o lançamento. O restante entrava na fila de demandas. Então, eles faziam o lançamento em uma máquina, registravam os logs, e verificavam e executavam um segundo lançamento em todas as outras. Isso era implementado utilizando um Fabric script, um banco de dados básico e uma UI chamada “Sauron”, que armazenava os logs de lançamento.

Então foi feito o teste canário. Para ele, foram utilizados scripts de testes feitos pelos engenheiros, mas em vez de focar em apenas uma máquina, o script fazia o deploy nas máquinas canário, amarrando os logs com os usuários. Assim, era perguntado para o usuário se o deploy poderia seguir para a implementação completa. Em sequência, era feita uma análise das máquinas canário: um script coletava os códigos de status HTTP para cada solicitação; então, eles eram categorizados e aplicados limiares percentuais codificados, por exemplo, menos que 0,5% 5xx, pelo menos 90% 2xx. Porém, os usuários só seriam notificados se os limiares falhassem.

Já havia testes que eram executados, mas apenas nas máquinas dos engenheiros, e os revisores de código tinham que aceitar a palavra deles sobre o assunto. E, assim, não se sabia o status do resultado dos commits. Para corrigir isso, eles configuraram o jenkins para executar os testes em novos commits e relatar os resultados para Sauron. Então, Sauron acompanhava o commit mais recente que havia passado nos testes e, quando fosse feito um lançamento, Sauron sugeria que esse commit fosse utilizado. Com isso, estava se desenhando o processo de automatização.

É chegada a hora de automatizar o processo

Para que isso fosse implementado com sucesso, primeiro eles tiveram que estabelecer alguns fundamentos. Foram criados estados de lançamento, ou seja, para cada lançamento havia um status do seu estado – “em execução”, “pronto” e “erro”. Assim, foi desenvolvido um script que avisasse os usuários se o lançamento anterior não estivesse no estado de “pronto”. Também foi inserido um botão “abortar”, que possui um script para verificar o estado ocasionalmente e reagir. Além disso, foi adicionado um trackeamento completo do commit. Dessa maneira, Sauron não enxergava mais apenas o commit mais recente que havia passado nos testes, mas ele tinha um registro completo de todos os commits, assim como o seu estado específico.

alex02

Com isso, restavam poucas decisões para os profissionais. Mas, com o intuito de automatizar ainda mais, eles criaram um algoritmo que selecionava os commits que passaram nos testes, e selecionavam o menor número possível de commits. Uma das regras era que nunca fossem selecionados mais de três. Se todos passassem nos testes, ele selecionaria um novo commit de cada vez, podendo ter no máximo dois commits consecutivos com execuções de teste do tipo “non-passing”. Então, ele tomava uma segunda decisão, que era se o lançamento havia sido bem-sucedido. Sendo assim, se mais de 1% dos commits falhassem na implantação dos hosts, o processo seria considerado falho.

alex03

Agora, quando um lançamento era feito, e tudo havia corrido da maneira esperada, o único ato necessário para os profissionais era responder um “sim” algumas vezes, aceitando o commit sugerido, iniciando o canário e prosseguindo até o deploy completo. Então, essas questões também passaram a ser respondidas automaticamente, e o Jenkins executava os scripts de lançamento. A princípio, isso ocorria sob a supervisão dos engenheiros, mas, conforme eles foram se sentindo confortáveis com a situação, eles deixaram de supervisionar.

Nem tudo são flores

Durante o processo, ocorreram muitas falhas. Várias vezes os engenheiros utilizavam diffs que quebravam os testes, o que, por sua vez, causava uma falha em todos os outros processos subsequentes, impedindo que qualquer commit fosse implementado.

Para resolver isso, foi necessário utilizar o OnCall, que revertia o commit infrator, esperava que ele passasse nos testes de reversão e, então, manualmente, os backlogs eram corrigidos antes de a automação poder continuar.

Isso era um dos piores problemas, pois acabava com a grande vantagem da implementação contínua, que era implementar pouquíssimos commits por lançamento. Assim, foi identificado um problema: os testes eram lentos e pouco confiáveis. Para resolver isso, os testes precisavam ser corrigidos. E foram. Os testes, que antes duravam cerca de 15 minutos, agora duravam 5. Além disso, foram corrigidos os problemas de infraestrutura que faziam com que os testes não fossem confiáveis.

Ademais, existiam ainda problemas com os backlogs. Embora os engenheiros do Instagram tenham conseguido essas melhorias, ainda havia um acúmulo de mudanças que precisavam ser implementadas. O principal motivo desses atrasos eram erros nos testes canário, falsos positivos e falsos negativos, além de outras quebras esporádicas. Mesmo assim, quando as raízes dos problemas eram resolvidas, a automação iria implementar um commit de cada vez e, assim, o backlog demorava para ser limpo e causava atrasos nas novas inserções de diffs. O OnCall normalmente interfere e implanta todo o backlog de uma só vez, o que anula, mais uma vez, uma das principais vantagens da implantação contínua.

alex04

Para resolver esse outro problema, os engenheiros implementaram uma seleção lógica que manipulava os commits, por meio da qual eram implantados vários commits quando havia um backlog. O algoritmo utilizado se baseava em uma meta de tempo de duração para cada commit – 30 minutos. Assim, para cada commit que está na fila, o algoritmo calcula o tempo necessário para implementar a fim de atingir a meta de lançamentos no tempo estimado, usando um valor hard-coded e o número de commits que deveriam ser implementados por lançamento. Assim, ele tem uma relação do número de commits por lançamento e encapsula em três, o que permite aos engenheiros fazerem tantos lançamentos quanto possível, deixando cada commit com um tempo razoável.

Embora isso tenha resolvido o problema até o momento, os lançamentos foram ficando mais lentos por conta da escalabilidade. Conforme a infraestrutura aumenta, o tempo dos lançamentos também aumenta. Aqui, o ssh-agent atropelava todas as conexões SSH e o processo fab, que também estava atrelado a um núcleo de gestão de todas as tarefas. A solução encontrada para esse problema foi utilizar o sistema de SSH distribuído do Facebook.

E na minha empresa, é possível?

Segundo os próprios engenheiros do Instagram, você pode realizar o processo que eles estão desenvolvendo para a sua empresa. Para isso, basta seguir as seguintes guidelines:

alex05

  • Testes: O conjunto de testes precisa ser rápido e ter uma boa cobertura, mas não precisa ser necessariamente perfeito. Os testes também precisam ser executados muitas vezes, durante a revisão, antes de aterrar e após o lançamento.
  • Canário: São necessários canários automatizados para impedir que commits ruins sejam implementados em todas as outras máquinas. O teste canário não precisa ser perfeito, mas um conjunto simples de estatísticas e limiares deve ser capaz de suprir suas necessidades.
  • Automatizar o casual: Não é necessário automatizar todas as situações, entretanto, as situações comuns precisam ser automatizadas. Se existem situações fora do padrão, deixe que as pessoas resolvam.
  • Deixe os profissionais confortáveis: Uma das principais barreiras para esse tipo de automação são os profissionais. Eles não podem se sentir desconfortáveis ou fora do controle da situação. Sendo assim, o sistema precisa fornecer ao usuário uma boa visualização do que foi feito, do que está sendo feito e, principalmente, do que será feito, além de possuir mecanismos de parada.
  • Deploys ruins acontecerão: Mudanças ruins vão acontecer, mas tudo bem. O importante é ter uma estratégia de mitigação dos problemas e conseguir localizar e corrigir os erros rapidamente.

Como vimos, nem tudo foram flores no desenvolvimento e aplicação do sistema de implantação automatizada no Instagram. Mas eles conseguiram atingir um patamar que não apenas funciona, mas é maleável e escalável. Esse é um dos fatores mais importantes, visto que você precisa pensar que sua empresa vai crescer e não pode ter um sistema engessado.

E o que sua empresa precisa ter em mente para atingir esse objetivo é: sistemas de implantação contínua não precisam ser complexos. Comece com algo simples, que foque os princípios acima e desenvolva seu modelo a partir daí.

***

Confira o artigo original aqui