Editar

Partilhar via


CI/CD para arquiteturas de microsserviços

Azure

Ciclos de liberação mais rápidos são uma das principais vantagens das arquiteturas de microsserviços. Mas sem um bom processo de CI/CD, você não alcançará a agilidade que os microsserviços prometem. Este artigo descreve os desafios e recomenda algumas abordagens para o problema.

O que é CI/CD?

Quando falamos de CI/CD, estamos realmente falando de vários processos relacionados: Integração contínua, entrega contínua e implantação contínua.

  • Integração contínua. As alterações de código são frequentemente mescladas na ramificação principal. Processos automatizados de compilação e teste garantem que o código na ramificação principal seja sempre de qualidade de produção.

  • Entrega contínua. Todas as alterações de código que passam pelo processo de CI são publicadas automaticamente em um ambiente semelhante ao de produção. A implantação no ambiente de produção ao vivo pode exigir aprovação manual, mas é automatizada. O objetivo é que seu código esteja sempre pronto para ser implantado na produção.

  • Implantação contínua. As alterações de código que passam pelas duas etapas anteriores são implantadas automaticamente node produção.

Aqui estão alguns objetivos de um processo robusto de CI/CD para uma arquitetura de microsserviços:

  • Cada equipe pode criar e implantar os serviços que possui de forma independente, sem afetar ou interromper outras equipes.

  • Antes de uma nova versão de um serviço ser implantada na produção, ela é implantada em ambientes de desenvolvimento/teste/QA para validação. Os portões de qualidade são aplicados em cada etapa.

  • Uma nova versão de um serviço pode ser implantada lado a lado com a versão anterior.

  • Estão em vigor políticas de controlo de acesso suficientes.

  • Para cargas de trabalho em contêineres, você pode confiar nas imagens de contêiner que são implantadas na produção.

Por que um pipeline de CI/CD robusto é importante

Em um aplicativo monolítico tradicional, há um único pipeline de compilação cuja saída é o executável do aplicativo. Todo o trabalho de desenvolvimento alimenta este pipeline. Se um bug de alta prioridade for encontrado, uma correção deve ser integrada, testada e publicada, o que pode atrasar o lançamento de novos recursos. Você pode atenuar esses problemas tendo módulos bem fatorados e usando ramificações de recursos para minimizar o impacto das alterações de código. Mas à medida que o aplicativo se torna mais complexo e mais recursos são adicionados, o processo de liberação de um monólito tende a se tornar mais frágil e com probabilidade de quebrar.

Seguindo a filosofia dos microsserviços, nunca deve haver um trem de lançamento longo onde todas as equipes têm que entrar na fila. A equipe que cria o serviço "A" pode lançar uma atualização a qualquer momento, sem esperar que as alterações no serviço "B" sejam mescladas, testadas e implantadas.

Diagrama de um monólito de CI/CD

Para alcançar uma alta velocidade de liberação, seu pipeline de liberação deve ser automatizado e altamente confiável para minimizar o risco. Se você liberar para produção uma ou mais vezes ao dia, regressões ou interrupções de serviço devem ser raras. Ao mesmo tempo, se uma atualização incorreta for implantada, você deve ter uma maneira confiável de reverter ou avançar rapidamente para uma versão anterior de um serviço.

Desafios

  • Muitas pequenas bases de código independentes. Cada equipe é responsável por construir seu próprio serviço, com seu próprio pipeline de construção. Em algumas organizações, as equipes podem usar repositórios de código separados. Repositórios separados podem levar a uma situação em que o conhecimento de como criar o sistema está espalhado entre as equipes e ninguém na organização sabe como implantar o aplicativo inteiro. Por exemplo, o que acontece em um cenário de recuperação de desastres, se você precisar implantar rapidamente em um novo cluster?

    de mitigação: Tenha um pipeline unificado e automatizado para criar e implantar serviços, para que esse conhecimento não fique "escondido" dentro de cada equipe.

  • Várias linguagens e frameworks. Com cada equipe usando sua própria combinação de tecnologias, pode ser difícil criar um único processo de compilação que funcione em toda a organização. O processo de construção deve ser flexível o suficiente para que cada equipe possa adaptá-lo para sua escolha de linguagem ou estrutura.

    de mitigação: Conteinerize o processo de compilação para cada serviço. Dessa forma, o sistema de compilação só precisa ser capaz de executar os contêineres.

  • Integração e teste de carga. Com as equipes lançando atualizações em seu próprio ritmo, pode ser um desafio projetar testes robustos de ponta a ponta, especialmente quando os serviços têm dependências de outros serviços. Além disso, executar um cluster de produção completo pode ser caro, portanto, é improvável que cada equipe execute seu próprio cluster completo em escalas de produção, apenas para testes.

  • Gestão de versões. Cada equipe deve ser capaz de implantar uma atualização na produção. Isso não significa que todos os membros da equipe tenham permissões para fazê-lo. Mas ter uma função centralizada do Release Manager pode reduzir a velocidade das implantações.

    Mitigação: Quanto mais o seu processo de CI/CD for automatizado e confiável, menor deverá ser a necessidade de uma autoridade central. Dito isso, você pode ter políticas diferentes para lançar grandes atualizações de recursos versus pequenas correções de bugs. Ser descentralizado não significa governança zero.

  • Atualizações de serviço. Quando você atualiza um serviço para uma nova versão, ele não deve interromper outros serviços que dependem dele.

    Mitigação: Use técnicas de implantação, como liberação azul-verde ou canária, para alterações ininterruptas. Para interromper as alterações da API, implante a nova versão lado a lado com a versão anterior. Dessa forma, os serviços que consomem a API anterior podem ser atualizados e testados para a nova API. Consulte Serviços de atualização, abaixo.

Monorepo vs. multi-repo

Antes de criar um fluxo de trabalho de CI/CD, você deve saber como a base de código será estruturada e gerenciada.

  • As equipes trabalham em repositórios separados ou em um monorepo (repositório único)?
  • Qual é a sua estratégia de ramificação?
  • Quem pode enviar código para a produção? Existe uma função de gestor de versões?

A abordagem monorepo tem vindo a ganhar favor, mas há vantagens e desvantagens para ambos.

  Monorepo Vários repositórios
Vantagens Partilha de código
Mais fácil padronizar código e ferramentas
Código mais fácil de refatorar
Detetabilidade - visualização única do código
Propriedade clara por equipa
Potencialmente menos conflitos de mesclagem
Ajuda a impor a dissociação de microsserviços
Desafios Alterações no código compartilhado podem afetar vários microsserviços
Maior potencial para conflitos de fusão
As ferramentas devem ser dimensionadas para uma base de código grande
Controlo de acessos
Processo de implantação mais complexo
Mais difícil de compartilhar código
Mais difícil de aplicar padrões de codificação
Gestão de dependências
Base de código difusa, pouca capacidade de descoberta
Falta de infraestruturas partilhadas

Atualizando serviços

Existem várias estratégias para atualizar um serviço que já está em produção. Aqui discutimos três opções comuns: Atualização contínua, implantação azul-verde e liberação canária.

Atualizações contínuas

Em uma atualização contínua, você implanta novas instâncias de um serviço e as novas instâncias começam a receber solicitações imediatamente. À medida que as novas instâncias surgem, as instâncias anteriores são removidas.

Exemplo. No Kubernetes, as atualizações contínuas são o comportamento padrão quando você atualiza as especificações do pod para umde implantação . O controlador de implantação cria um novo ReplicaSet para os pods atualizados. Em seguida, ele aumenta a escala do novo ReplicaSet enquanto reduz o antigo, para manter a contagem de réplicas desejada. Ele não exclui pods antigos até que os novos estejam prontos. O Kubernetes mantém um histórico da atualização, para que você possa reverter uma atualização, se necessário.

Exemplo. O Azure Service Fabric usa a estratégia de atualização contínua por padrão. Essa estratégia é mais adequada para implantar uma versão de um serviço com novos recursos sem alterar as APIs existentes. O Service Fabric inicia uma implantação de atualização atualizando o tipo de aplicativo para um subconjunto dos nós ou um domínio de atualização. Em seguida, ele avança para o próximo domínio de atualização até que todos os domínios sejam atualizados. Se um domínio de atualização falhar na atualização, o tipo de aplicativo será revertido para a versão anterior em todos os domínios. Lembre-se de que um tipo de aplicativo com vários serviços (e se todos os serviços forem atualizados como parte de uma implantação de atualização) é propenso a falhas. Se um serviço falhar na atualização, todo o aplicativo será revertido para a versão anterior e os outros serviços não serão atualizados.

Um desafio de rolar atualizações é que, durante o processo de atualização, uma mistura de versões antigas e novas estão sendo executadas e recebendo tráfego. Durante esse período, qualquer solicitação pode ser encaminhada para qualquer uma das duas versões.

Para quebrar as alterações da API, uma boa prática é oferecer suporte a ambas as versões lado a lado, até que todos os clientes da versão anterior sejam atualizados. Consulte de controle de versão da API .

Implantação azul-verde

Em uma implantação azul-verde, você implanta a nova versão ao lado da versão anterior. Depois de validar a nova versão, você alterna todo o tráfego de uma só vez da versão anterior para a nova versão. Após a mudança, você monitora o aplicativo para quaisquer problemas. Se algo der errado, você pode trocar de volta para a versão antiga. Supondo que não haja problemas, você pode excluir a versão antiga.

Com um aplicativo monolítico ou de N camadas mais tradicional, a implantação azul-verde geralmente significava o provisionamento de dois ambientes idênticos. Você implantaria a nova versão em um ambiente de preparo e, em seguida, redirecionaria o tráfego do cliente para o ambiente de preparo — por exemplo, trocando endereços VIP. Em uma arquitetura de microsserviços, as atualizações acontecem no nível de microsserviço, portanto, você normalmente implantaria a atualização no mesmo ambiente e usaria um mecanismo de descoberta de serviço para trocar.

Exemplo. No Kubernetes, você não precisa provisionar um cluster separado para fazer implantações azul-verde. Em vez disso, você pode aproveitar os seletores. Crie um novo recurso de de implantação de com uma nova especificação de pod e um conjunto diferente de rótulos. Crie essa implantação, sem excluir a implantação anterior ou modificar o serviço que aponta para ela. Quando os novos pods estiverem em execução, você poderá atualizar o seletor do serviço para corresponder à nova implantação.

Uma desvantagem da implantação azul-verde é que, durante a atualização, você está executando o dobro de pods para o serviço (atual e próximo). Se os pods exigirem muitos recursos de CPU ou memória, talvez seja necessário expandir o cluster temporariamente para lidar com o consumo de recursos.

Libertação das canárias

Em uma versão canária, você lança uma versão atualizada para um pequeno número de clientes. Em seguida, você monitora o comportamento do novo serviço antes de implementá-lo para todos os clientes. Isso permite que você faça uma implementação lenta de forma controlada, observe dados reais e identifique problemas antes que todos os clientes sejam afetados.

Uma versão canária é mais complexa de gerenciar do que uma atualização azul-verde ou contínua, porque você deve rotear dinamicamente as solicitações para diferentes versões do serviço.

Exemplo. No Kubernetes, você pode configurar um de Serviço para abranger dois conjuntos de réplicas (um para cada versão) e ajustar as contagens de réplicas manualmente. No entanto, essa abordagem é bastante grosseira, devido à maneira como o Kubernetes balanceia a carga entre pods. Por exemplo, se você tiver um total de 10 réplicas, só poderá deslocar o tráfego em incrementos de 10%. Se você estiver usando uma malha de serviço, poderá usar as regras de roteamento de malha de serviço para implementar uma estratégia de liberação canária mais sofisticada.

Próximos passos