Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Ciclos de lançamento mais rápidos são uma grande vantagem das arquiteturas de microserviços. Sem um processo fiável de integração contínua e entrega contínua (CI/CD), perde-se a agilidade que os microserviços proporcionam. Este artigo descreve os desafios comuns de CI/CD em arquiteturas de microserviços e recomenda abordagens para construir, validar, proteger e implementar serviços de forma independente.
O que é CI/CD?
CI/CD refere-se a vários processos relacionados: integração contínua, entrega contínua e implementação contínua.
Integração contínua (IC): As alterações de código são frequentemente integradas no ramo principal. Processos automatizados de construção e teste garantem que o código na ramificação principal é sempre de qualidade de produção.
Entrega contínua (CD): As alterações ao código que passam pelo processo de CI são automaticamente publicadas num ambiente semelhante à produção. A implementação no ambiente de produção ao vivo pode exigir aprovação manual, mas é de resto automatizada. O objetivo é que o seu código esteja sempre pronto para ser implementado em produção.
Implantação contínua: Alterações de código que ultrapassam os dois passos anteriores são automaticamente implementadas em produção.
Considere os seguintes 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 implementada em produção, esta é implementada em ambientes de desenvolvimento/teste e 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. Os pipelines autenticam-se no Azure com credenciais federadas e de curta duração, em vez de segredos de longa validade.
Para cargas de trabalho em contêineres, você pode confiar nas imagens de contêiner que são implantadas na produção. Essa confiança é estabelecida através de imagens assinadas, atestados de materiais de software (SBOM) e análise de vulnerabilidades imposta no pipeline.
Por que um pipeline de CI/CD robusto é importante
Numa aplicação monolítica tradicional, um único pipeline de build produz o executável da aplicação. Todo o trabalho de desenvolvimento alimenta esta linha de produção. Se a equipa detetar um bug de alta prioridade, a correção deve ser integrada, testada e publicada, o que pode atrasar o lançamento de novas funcionalidades. Pode reduzir estes problemas usando módulos bem fatorizados e ramos de características para limitar o efeito das alterações no código. Mas à medida que a aplicação se torna mais complexa e mais funcionalidades são adicionadas, o processo de lançamento de um monólito tende a tornar-se mais complicado e provável de falhar.
De acordo com a filosofia dos microserviços, nunca deveria existir um longo ciclo de lançamentos em que cada equipa tenha de esperar pela sua vez. A equipa que constrói o serviço A pode lançar uma atualização quando quiser e não precisa de esperar por alterações no serviço B para fundir, testar e implementar.
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 implementar uma atualização deficiente, deve ter uma forma fiável de reverter ou avançar rapidamente para uma versão anterior de um serviço.
Challenges
Muitas pequenas bases de código independentes: Cada equipa é responsável por construir o seu próprio serviço, com o seu próprio pipeline de construção. Em algumas organizações, as equipas podem usar repositórios de código separados. Repositórios separados podem dispersar o conhecimento sobre como construir o sistema entre as equipas. Como resultado, ninguém na organização sabe como implementar toda a aplicação.
Mitigação: Ter um pipeline unificado e automatizado ou pelo menos uma infraestrutura de pipeline comum para construir e implementar serviços, de modo a que este conhecimento não fique escondido em cada equipa. Modelos de pipeline reutilizáveis, como fluxos de trabalho reutilizáveis do GitHub Actions ou modelos do Azure Pipelines, ajudam a padronizar as etapas de compilação, teste, análise e implantação em cada serviço.
Múltiplas linguagens e frameworks: Cada equipa utiliza a sua própria combinação de tecnologias, pelo que pode ser difícil criar um único processo de construção que funcione ao longo da carga de trabalho. O processo de construção deve ser suficientemente flexível para que cada equipa possa adaptá-lo à linguagem ou framework escolhido.
Mitigação: Containerize o processo de compilação para cada serviço para que o sistema de compilação apenas precise de executar os contentores. Plataformas como GitHub Actions, Azure Pipelines e Azure Container Registry tasks conseguem construir e publicar imagens de contentores de forma consistente, independentemente da linguagem de origem.
Integração e testes de carga: As equipas lançam atualizações ao seu próprio ritmo, por isso pode ser desafiante conceber testes robustos de ponta a ponta, especialmente quando os serviços dependem de outros serviços. Operar um cluster de produção completo pode ser dispendioso, pelo que é improvável que cada equipa opere o seu próprio cluster completo à escala de produção apenas para testes.
Mitigation: Use ambientes de pré-visualização efémeros, como namespaces por pull request em Kubernetes ou ambientes Azure Container Apps criados sob demanda. Use testes por contrato para que os problemas de integração sejam revelados cedo, sem exigir um duplicado em escala real da produção.
Gestão de lançamentos: Todas as equipas devem ser capazes de lançar uma atualização em produção. Esse requisito não significa que todos os membros da equipa tenham permissões para implementar. Um papel centralizado de gestor de lançamentos pode reduzir a velocidade de implementação.
Mitigação: Quanto mais automatizado e fiável for o seu processo CI/CD, menos precisa de uma autoridade central. Ainda podes ter políticas diferentes para lançar grandes atualizações de funcionalidades em comparação com pequenas correções de bugs. Uma abordagem descentralizada não significa governação zero. Impor aprovações ao utilizar ambientes e aprovações do Azure Pipelines ou ambientes de implementação e revisores obrigatórios do GitHub Actions, e codificar a política no lado do cluster ao utilizar Azure Policy para Azure Kubernetes Service (AKS) ou OPA Gatekeeper.
Atualizações de serviço: Quando atualiza um serviço para uma nova versão, a atualização não deve causar a falha de outros serviços que dependem dele.
Mitigação: Use técnicas de implementação como lançamentos azul-esverdeados ou canários para mudanças que não quebrem o controlo. Para interromper as alterações da API, implante a nova versão lado a lado com a versão anterior. Com esta abordagem, os serviços que consomem a API anterior podem ser atualizados e testados para a nova API. Para mais informações, consulte Serviços de atualização.
Identidade e gestão de segredos do pipeline: Os segredos de service principals de longa duração armazenados em pipelines constituem uma fonte frequente de comprometimento e de trabalho operacional. Os segredos do principal de serviço expiram, podem vazar e requerem rotação em muitos pipelines independentes de microserviços.
Mitigação: Autentique os pipelines no Azure com federação de identidades de carga de trabalho, que utiliza OpenID Connect (OIDC), para que não seja armazenado nenhum segredo do cliente no pipeline. Para mais informações, consulte Identidades de cargas de trabalho para o Azure Pipelines e Configurar o OpenID Connect no Azure para o GitHub Actions. Armazene quaisquer segredos restantes em Azure Key Vault e consulte-os em tempo de execução.
Segurança da cadeia de abastecimento: Tudo o que enviares para produção deve ser rastreável ao código e às dependências de onde foi construído. Os microserviços aumentam o número de imagens, registos e pipelines, o que aumenta a sua superfície de ataque na cadeia de abastecimento.
Mitigation: Sinalize imagens de contentores usando Notation e Key Vault, e verifique as assinaturas no momento da admissão usando AKS image integrity ou Ratify. Gera um SBOM como artefacto de build. Analise código, dependências e pipelines utilizando Microsoft Defender para a Cloud DevOps security e GitHub Advanced Security. Escaneie imagens em tempo de execução usando Microsoft Defender para Containers. Exigir que todas as análises passem antes que a libertação possa avançar.
Monorepo vs. multirepo
Antes de criar um fluxo de trabalho CI/CD, deve saber como a base de código é estruturada e gerida, incluindo:
- Seja as equipas trabalhem em repositórios separados ou num monorepositório.
- A sua estratégia de ramificação.
- Quem pode enviar código para produção e se existe um gestor de lançamento.
As equipas utilizam amplamente ambas as abordagens em produção. A tua escolha depende da topologia da equipa, maturidade das ferramentas e da quantidade de código partilhada entre serviços.
| Monorepo | Vários repositórios | |
|---|---|---|
| Vantagens | - Partilha de código - Facilitar a padronização do código e das ferramentas - Mais fácil de refatorar código - Facilidade de descoberta (uma visualização única do código) |
- Responsabilidade clara para cada equipa - Potencialmente menos conflitos de fusão - Ajuda a impor o desacoplamento de microserviços |
| Desafios | - Alterações ao código partilhado podem afetar múltiplos microserviços - Maior potencial para conflitos de fusão - As ferramentas devem escalar para uma grande base de código - Controlo de acesso - Processo de implementação mais complexo |
- Código mais difícil de partilhar - Mais difícil de aplicar padrões de codificação - Gestão de dependências - Base de código difusa, fraca descoberta - Falta de infraestrutura partilhada |
Independentemente do modelo que escolher, use acionadores limitados ao caminho nos seus pipelines, como filtros de caminho no GitHub Actions ou caminhos de acionamento no Azure Pipelines. Os gatilhos com escopo de caminho ajudam a garantir que apenas os microserviços afetados reconstroem e voltam a ser implementados em cada commit.
Serviços de atualização
Existem várias estratégias para atualizar um serviço que já se encontra em produção, incluindo atualização faseada, implementação azul-verde e lançamento canário. Estes padrões são frequentemente coordenados através de um fluxo de trabalho GitOps. Para mais informações, consulte GitOps e entrega progressiva.
Atualizações contínuas
Numa atualização progressiva, implementas novas instâncias de um serviço, e as novas instâncias começam a receber pedidos imediatamente. À medida que as novas instâncias ficam prontas, as instâncias anteriores são removidas.
Exemplo no Kubernetes: No Kubernetes, atualizações progressivas são o comportamento padrão quando se atualiza a especificação do pod para uma implementação. O controlador de implementação cria um novo ReplicaSet para os pods atualizados. Depois, escala o novo ReplicaSet enquanto reduz o ReplicaSet anterior para manter a contagem de réplicas desejada. Não elimina os pods anteriores até que os novos pods estejam prontos. O Kubernetes mantém um histórico da atualização, para que você possa reverter uma atualização, se necessário.
Exemplo em Aplicações de Contentores: O Container Apps utiliza revisões para gerir atualizações contínuas. Quando implementa uma nova revisão, as Aplicações Container podem transferir gradualmente o tráfego da revisão anterior para a nova revisão usando regras de divisão de tráfego. Se a nova revisão encontrar problemas, pode recuar, redirecionando o tráfego para a revisão anterior. Pode configurar várias revisões ativas simultaneamente e controlar a percentagem de tráfego que cada revisão recebe.
Um dos desafios das atualizações contínuas é que, durante o processo de atualização, uma mistura de versões anteriores e novas corre e recebe tráfego. Durante este período, o sistema pode encaminhar qualquer pedido para qualquer uma das 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. Para mais informações, consulte versionamento 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 houver algum problema, podes voltar a transferir o tráfego para a versão anterior. Se não houver problemas, pode apagar a versão anterior.
Com uma aplicação mais tradicional e monolítica ou N-tier, a implementação azul-verde geralmente significa que se criam dois ambientes idênticos. Implementas a nova versão para um ambiente de staging e depois redirecionas o tráfego do cliente para esse ambiente, por exemplo, trocando um endereço IP virtual. Numa arquitetura de microserviços, as atualizações ocorrem ao nível do microserviço, por isso normalmente implementa a atualização no mesmo ambiente e utiliza um mecanismo de descoberta de serviços para transferir o tráfego.
Exemplo no Kubernetes: No Kubernetes, não é necessário criar um cluster separado para fazer implementações azul-verde. Em vez disso, você pode aproveitar os seletores. Cria um novo recurso de implementação com uma nova especificação de pod e um conjunto diferente de etiquetas. Crie esta implementação, mas não apague a implementação anterior nem modifique o serviço que aponta para ela. Depois de os novos pods serem executados, podes atualizar o seletor do serviço para corresponder à nova implementação.
Uma desvantagem da implementação azul-esverdeada é que, durante a atualização, executa o dobro dos pods para o serviço (atual e próximo). Se os pods usarem recursos substanciais de CPU ou memória, pode ser necessário escalar temporariamente o cluster para satisfazer a maior procura de recursos.
Libertação das canárias
Numa versão canário, implementas uma versão atualizada para um pequeno subconjunto de clientes e depois monitorizas o comportamento do novo serviço antes de o implementares para todos os clientes. Com esta abordagem, pode implementar gradualmente e de forma controlada, monitorizar dados reais e identificar problemas antes que afetem todos os clientes.
Um lançamento canário é mais complexo de gerir do que uma atualização azul-verde ou contínua porque é necessário encaminhar os pedidos dinamicamente para diferentes versões do serviço.
Exemplo no Kubernetes: No Kubernetes, podes configurar um serviço para abranger dois conjuntos de réplicas (um para cada versão) e ajustar manualmente as contagens de réplicas. No entanto, esta abordagem é rudimentar devido à forma como o Kubernetes distribui a carga entre os pods. Por exemplo, com um total de 10 réplicas, só é possível redirecionar o tráfego em incrementos de 10%. Se usar uma malha de serviços, pode usar as respetivas regras de encaminhamento para implementar uma estratégia de lançamento em canário mais sofisticada.
Exemplo no Container Apps: No Container Apps, pode usar a divisão de tráfego para encaminhar uma percentagem definida do tráfego para uma nova revisão (como 10% para v2 enquanto 90% permanece em v1) e ajustar as ponderações à medida que a confiança aumenta, sem necessidade de uma malha de serviço externa.
Entrega progressiva e GitOps
Para equipas que operam muitos microserviços no Kubernetes, um modelo baseado em pull do GitOps complementa os exemplos anteriores baseados em push. O estado desejado do cluster está em Git, e um operador dentro do cluster reconcilia o cluster com esse estado. O CI constrói, testa, digitaliza, sinaliza e impulsiona a imagem. O CD sincroniza o cluster com o manifesto. Esta separação dá-lhe registos de auditoria e uma recuperação de desastres (DR) mais fácil. Também elimina a necessidade de o runner CI deter credenciais diretas do cluster.
Passos seguintes
- Caminho de aprendizagem: Definir e implementar IC
- Formação: Desenvolvimento para DevOps Empresarial
- Arquitetura de microsserviços