Como construir um desenvolvimento interno completo usando DevOps - experiência VTB

As práticas de DevOps funcionam. Nós mesmos ficamos convencidos disso quando reduzimos o tempo de instalação do release em 10 vezes. No sistema FIS Profile, que usamos no VTB, a instalação agora leva 90 minutos em vez de 10. O tempo de compilação da versão diminuiu de duas semanas para dois dias. O número de defeitos de implementação persistentes caiu quase ao mínimo. Para fugir do “trabalho manual” e eliminar a dependência do fornecedor, tivemos que trabalhar com muletas e encontrar soluções inesperadas. Abaixo do corte está uma história detalhada sobre como construímos um desenvolvimento interno completo.

Como construir um desenvolvimento interno completo usando DevOps - experiência VTB
 

Prólogo: DevOps é uma filosofia

No ano passado, trabalhamos muito para organizar o desenvolvimento interno e a implementação de práticas DevOps na VTB:

  • Construímos processos internos de desenvolvimento para 12 sistemas;
  • Lançamos 15 oleodutos, dos quais quatro entraram em produção;
  • Cenários de teste automatizados 1445;
  • Implementamos com sucesso uma série de versões preparadas por equipes internas.

Um dos mais difíceis de organizar o desenvolvimento interno e a implementação de práticas DevSecOps acabou sendo o sistema FIS Profile - um processador de produtos de varejo em um SGBD não relacional. Mesmo assim, conseguimos construir o desenvolvimento, lançar o pipeline, instalar pacotes individuais de não lançamento no produto e aprender como montar lançamentos. A tarefa não foi fácil, mas interessante e sem restrições óbvias de implementação: aqui está o sistema - você precisa construir um desenvolvimento interno. A única condição é utilizar o CD antes de um ambiente produtivo.

A princípio, o algoritmo de implementação parecia simples e claro:

  • Desenvolvemos experiência inicial em desenvolvimento e alcançamos um nível aceitável de qualidade da equipe de código sem defeitos grosseiros;
  • Integramo-nos tanto quanto possível nos processos existentes;
  • Para transferir código entre estágios óbvios, cortamos um pipeline e empurramos uma de suas extremidades para a continuação.

Durante esse período, a equipe de desenvolvimento do tamanho necessário deve desenvolver habilidades e aumentar a parcela de sua contribuição para os lançamentos a um nível aceitável. E pronto, podemos considerar a tarefa concluída.

Parece que este é um caminho totalmente energeticamente eficiente para o resultado desejado: aqui está o DevOps, aqui estão as métricas de desempenho da equipe, aqui está a experiência acumulada... Mas, na prática, recebemos outra confirmação de que DevOps ainda é uma questão de filosofia , e não “anexado ao processo gitlab, ansible, nexus e mais abaixo na lista”.

Tendo analisado mais uma vez o plano de ação, percebemos que estávamos construindo dentro de nós uma espécie de fornecedor terceirizado. Portanto, ao algoritmo descrito acima foi acrescentada a reengenharia de processos, bem como o desenvolvimento de expertise ao longo de todo o percurso de desenvolvimento para alcançar um protagonismo neste processo. Não é a opção mais fácil, mas este é o caminho do desenvolvimento ideologicamente correto.
 

Onde começa o desenvolvimento interno? 

Não era o sistema mais amigável para se trabalhar. Arquitetonicamente, era um grande SGBD não relacional, consistindo em muitos objetos executáveis ​​​​separados (scripts, procedimentos, lotes, etc.), que eram chamados conforme necessário e funcionavam no princípio de uma caixa preta: ele recebe uma solicitação e emite uma resposta. Outras dificuldades dignas de nota incluem:

  • Linguagem exótica (MUMPS);
  • Interface do console;
  • Falta de integração com ferramentas e estruturas de automação populares;
  • Volume de dados em dezenas de terabytes;
  • Carga superior a 2 milhões de operações por hora;
  • Significância - Crítica para os Negócios.

Ao mesmo tempo, não havia repositório de código-fonte do nosso lado. De forma alguma. Havia documentação, mas todos os principais conhecimentos e competências estavam do lado de uma organização externa.
Começamos a dominar o desenvolvimento do sistema quase do zero, levando em consideração suas características e baixa distribuição. Iniciado em outubro de 2018:

  • Estudei a documentação e os fundamentos da geração de código;
  • Estudamos o minicurso de desenvolvimento recebido do fornecedor;
  • Dominar habilidades de desenvolvimento inicial;
  • Compilamos um manual de treinamento para novos membros da equipe;
  • Concordamos em incluir a equipe no modo “combate”;
  • Resolvido o problema com controle de qualidade do código;
  • Organizamos um estande para o desenvolvimento.

Passamos três meses desenvolvendo expertise e mergulhando no sistema e, a partir do início de 2019, o desenvolvimento interno iniciou seu movimento em direção a um futuro brilhante, às vezes com dificuldade, mas com confiança e propósito.

Migração de repositório e autotestes

A primeira tarefa do DevOps é o repositório. Rapidamente concordamos em fornecer acesso, mas foi necessário migrar do SVN atual com uma ramificação tronco para nosso Git alvo com a transição para um modelo de diversas ramificações e desenvolvimento do Git Flow. Contamos também com 2 equipes com infraestrutura própria, além de parte da equipe do fornecedor no exterior. Tive que conviver com dois Gits e garantir a sincronização. Em tal situação, era o menor dos dois males.

A migração do repositório foi repetidamente adiada; só foi concluída em abril, com a ajuda de colegas da linha de frente. Com o Git Flow, decidimos manter as coisas simples para começar e optamos pelo esquema clássico com hotfix, desenvolvimento e lançamento. Eles decidiram abandonar o master (também conhecido como prod). A seguir explicaremos por que essa opção acabou sendo ideal para nós. Um repositório externo pertencente ao fornecedor, comum a duas equipes, foi utilizado como trabalhador. Ele sincronizou com o repositório interno de acordo com um cronograma. Agora com Git e Gitlab foi possível automatizar processos.

A questão dos autotestes foi resolvida com surpreendente facilidade - recebemos uma estrutura pronta. Levando em consideração as peculiaridades do sistema, chamar uma operação separada era uma parte compreensível do processo de negócio e ao mesmo tempo servia como teste unitário. Faltava apenas preparar os dados do teste e definir a ordem desejada de chamada dos scripts e avaliação dos resultados. À medida que a lista de cenários, formada com base nas estatísticas de operação, na criticidade dos processos e na metodologia de regressão existente, foi sendo preenchida, os testes automáticos começaram a aparecer. Agora poderíamos começar a construir o pipeline.

Como era: o modelo antes da automação

O modelo existente do processo de implementação é uma história separada. Cada modificação foi transferida manualmente como um pacote de instalação incremental separado. Em seguida veio o registro manual no Jira e a instalação manual nos ambientes. Para pacotes individuais tudo parecia claro, mas com a preparação do lançamento as coisas ficaram mais complicadas.

A montagem foi realizada ao nível das entregas individuais, que eram objetos independentes. Qualquer mudança é uma nova entrega. Entre outras coisas, 60-70 versões técnicas foram adicionadas aos pacotes 10-15 da composição principal do lançamento - versões obtidas adicionando ou excluindo algo do lançamento e refletindo mudanças nas vendas fora dos lançamentos.

Os objetos nas entregas se sobrepunham, especialmente no código executável, que era menos da metade exclusivo. Havia muitas dependências tanto do código já instalado quanto daquele cuja instalação foi planejada. 

Para obter a versão necessária do código, foi necessário seguir rigorosamente a ordem de instalação, durante a qual os objetos foram reescritos fisicamente muitas vezes, cerca de 10 a 12 vezes.

Depois de instalar um lote de pacotes, tive que seguir manualmente as instruções para inicializar as configurações. A versão foi montada e instalada pelo fornecedor. A composição do lançamento foi esclarecida quase antes do momento da implementação, o que implicou a criação de pacotes de “desacoplamento”. Como resultado, uma parte significativa dos fornecimentos passou de lançamento em lançamento com sua própria cauda de “desacoplamentos”.

Agora está claro que com esta abordagem - montando o quebra-cabeça de lançamento no nível do pacote - um único branch master não tinha significado prático. A instalação em produção levou de uma hora e meia a duas horas de trabalho manual. É bom que pelo menos no nível do instalador a ordem de processamento dos objetos tenha sido especificada: campos e estruturas foram inseridos antes dos dados para eles e procedimentos. No entanto, isso só funcionou em um pacote separado.

O resultado lógico dessa abordagem foram os defeitos de instalação obrigatórios na forma de versões distorcidas de objetos, código desnecessário, instruções ausentes e influências mútuas de objetos não contabilizadas, que foram febrilmente eliminados após o lançamento. 

Primeiras atualizações: commit montagem e entrega

A automação começou transmitindo código através de um tubo ao longo desta rota:

  • Retire a entrega finalizada do armazenamento;
  • Instale-o em um ambiente dedicado;
  • Execute autotestes;
  • Avalie o resultado da instalação;
  • Chame o pipeline a seguir ao lado do comando de teste.

O próximo pipeline deve registrar a tarefa no Jira e aguardar a distribuição dos comandos para os loops de teste selecionados, que dependem do tempo de implementação da tarefa. Gatilho - uma carta sobre disponibilidade para entrega em um determinado endereço. Isto, claro, era uma muleta óbvia, mas eu tinha que começar por algum lado. Em maio de 2019, a transferência de código começou com verificações em nossos ambientes. O processo começou, resta apenas colocá-lo em uma forma decente:

  • Cada modificação é executada em uma ramificação separada, que corresponde ao pacote de instalação e é mesclada na ramificação mestre de destino;
  • O gatilho de lançamento do pipeline é o aparecimento de um novo commit no branch master através de uma solicitação de merge, que é fechada pelos mantenedores da equipe interna;
  • Os repositórios são sincronizados uma vez a cada cinco minutos;
  • A montagem do pacote de instalação é iniciada - utilizando o montador recebido do fornecedor.

Depois disso, já existiam etapas de verificação e transferência do código, para lançar o pipe e montar do nosso lado.

Esta opção foi lançada em julho. As dificuldades da transição resultaram em alguma insatisfação entre o fornecedor e a linha de frente, mas no mês seguinte conseguimos remover todas as arestas e estabelecer um processo entre as equipes. Agora temos montagem por commit e delivery.
Em agosto, conseguimos concluir a primeira instalação de um pacote separado em produção usando nosso pipeline e, desde setembro, sem exceção, todas as instalações de pacotes individuais não lançados foram realizadas por meio de nossa ferramenta de CD. Além disso, conseguimos uma participação de tarefas internas em 40% da composição de lançamento com uma equipe menor que a do fornecedor - este é um sucesso definitivo. A tarefa mais séria permaneceu - montar e instalar o lançamento.

A solução final: pacotes de instalação cumulativos 

Compreendemos perfeitamente que criar scripts para as instruções do fornecedor era uma automação moderada; tivemos que repensar o processo em si. A solução era óbvia - coletar um suprimento cumulativo da filial de lançamento com todos os objetos das versões necessárias.

Começamos com a prova de conceito: montamos manualmente o pacote de lançamento de acordo com o conteúdo da implementação anterior e o instalamos em nossos ambientes. Deu tudo certo, o conceito se mostrou viável. Em seguida, resolvemos o problema de criar scripts para as configurações de inicialização e incluí-las no commit. Preparamos um novo pacote e o testamos em ambientes de teste como parte da atualização do contorno. A instalação foi bem-sucedida, embora com diversos comentários da equipe de implementação. Mas o principal é que recebemos autorização para entrar em produção no lançamento de novembro com nossa montagem.

Faltando pouco mais de um mês, os suprimentos escolhidos a dedo indicavam claramente que o tempo estava se esgotando. Eles decidiram fazer a compilação a partir do branch de lançamento, mas por que deveria ser separado? Não temos um Prod e as ramificações existentes não são boas - há muito código desnecessário. Precisamos urgentemente cortar os prod-likes, e isso representa mais de três mil commits. Montar manualmente não é uma opção. Esboçamos um script que percorre o log de instalação do produto e coleta commits para o branch. Na terceira vez funcionou corretamente, e depois de “terminar com um arquivo” o branch estava pronto. 

Escrevemos nosso próprio construtor para o pacote de instalação e o finalizamos em uma semana. Então tivemos que modificar o instalador da funcionalidade principal do sistema, já que é de código aberto. Após uma série de verificações e modificações, o resultado foi considerado bem-sucedido. Nesse ínterim, tomou forma a composição do lançamento, para a correta instalação da qual foi necessário alinhar o circuito de teste com o de produção, e para isso foi escrito um roteiro separado.

Naturalmente, houve muitos comentários sobre a primeira instalação, mas no geral o código funcionou. E depois da terceira instalação tudo começou a ficar bem. O controle de composição e o controle de versão dos objetos eram monitorados separadamente em modo manual, o que nesta fase era bastante justificado.

Um desafio adicional foi o grande número de não-liberações que tiveram de ser tidas em conta. Mas com o branch Prod e o Rebase, a tarefa tornou-se transparente.

Primeira vez, rapidamente e sem erros

Abordamos o lançamento com uma atitude otimista e com mais de uma dúzia de instalações bem-sucedidas em diferentes circuitos. Mas, literalmente, um dia antes do prazo, descobriu-se que o fornecedor não havia concluído o trabalho de preparação da liberação para instalação da forma aceita. Se por algum motivo nossa compilação não funcionar, o lançamento será interrompido. Além disso, através dos nossos esforços, o que é especialmente desagradável. Não tínhamos como recuar. Por isso, pensamos em opções alternativas, elaboramos planos de ação e iniciamos a instalação.

Surpreendentemente, todo o lançamento, composto por mais de 800 objetos, começou corretamente, na primeira vez e em apenas 10 minutos. Passamos uma hora verificando os logs em busca de erros, mas não encontramos nenhum.

Durante todo o dia seguinte houve silêncio no chat de lançamento: sem problemas de implementação, versões distorcidas ou código “inadequado”. Foi até meio estranho. Posteriormente, surgiram alguns comentários, mas em comparação com outros sistemas e experiências anteriores, o seu número e prioridade eram visivelmente mais baixos.

Um efeito adicional do efeito cumulativo foi um aumento na qualidade da montagem e dos testes. Devido a múltiplas instalações da versão completa, defeitos de construção e erros de implantação foram identificados em tempo hábil. Os testes em configurações de versão completa permitiram identificar adicionalmente defeitos na influência mútua de objetos que não apareceram durante as instalações incrementais. Foi definitivamente um sucesso, especialmente tendo em conta a nossa contribuição de 57% para o lançamento.

Resultados e conclusões

Em menos de um ano conseguimos:

  • Construa um desenvolvimento interno completo usando um sistema exótico;
  • Elimine a dependência crítica do fornecedor;
  • Lançar CI/CD para um legado muito hostil;
  • Elevar os processos de implementação a um novo nível técnico;
  • Reduzir significativamente o tempo de implantação;
  • Reduzir significativamente o número de erros de implementação;
  • Declare-se com confiança como um especialista líder em desenvolvimento.

É claro que muito do que é descrito parece uma porcaria, mas esses são os recursos do sistema e as limitações do processo que existem nele. No momento, as mudanças afetaram os produtos e serviços do IS Profile (contas máster, cartões plásticos, contas poupança, escrow, empréstimos em dinheiro), mas potencialmente a abordagem pode ser aplicada a qualquer SI para o qual esteja definida a tarefa de implementar práticas DevOps. O modelo cumulativo pode ser replicado com segurança para implementações subsequentes (incluindo aquelas que não são de lançamento) de muitas entregas.

Fonte: habr.com

Adicionar um comentário