Partilhar via


Microsserviços em contentores

Gorjeta

Este conteúdo é um excerto do eBook, Enterprise Application Patterns Using .NET MAUI, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.

Padrões de aplicativos corporativos usando a miniatura da capa do eBook .NET MAUI .

O desenvolvimento de aplicativos cliente-servidor resultou em um foco na criação de aplicativos hierárquicos que usam tecnologias específicas em cada camada. Tais aplicações são muitas vezes referidas como monolíticas e são empacotadas em hardware pré-dimensionado para picos de carga. As principais desvantagens dessa abordagem de desenvolvimento são o acoplamento estreito entre componentes dentro de cada camada, que os componentes individuais não podem ser facilmente dimensionados e o custo dos testes. Uma simples atualização pode ter efeitos imprevistos no restante da camada, portanto, uma alteração em um componente de aplicativo exige que toda a camada seja testada e reimplantada.

Particularmente preocupante, na era da nuvem, é que os componentes individuais não podem ser facilmente dimensionados. Um aplicativo monolítico contém funcionalidade específica do domínio e normalmente é dividido por camadas funcionais, como front-end, lógica de negócios e armazenamento de dados. A imagem abaixo ilustra que um aplicativo monolítico é dimensionado clonando todo o aplicativo em várias máquinas.

Abordagem de dimensionamento de aplicativos monolíticos.

Microsserviços

Os microsserviços oferecem uma abordagem diferente para o desenvolvimento e a implantação de aplicativos, uma abordagem adequada aos requisitos de agilidade, escala e confiabilidade dos aplicativos em nuvem modernos. Um aplicativo de microsserviços é dividido em componentes independentes que trabalham juntos para fornecer a funcionalidade geral do aplicativo. O termo microsserviço enfatiza que os aplicativos devem ser compostos por serviços pequenos o suficiente para refletir preocupações particulares, de modo que cada microsserviço implementa uma única função. Além disso, cada microsserviço tem contratos bem definidos com os quais outros microsserviços se comunicam e compartilham dados. Exemplos típicos de microsserviços incluem carrinhos de compras, processamento de inventário, subsistemas de compra e processamento de pagamentos.

Os microsserviços podem ser dimensionados de forma independente em comparação com aplicações monolíticas gigantes que são dimensionadas em conjunto. Isso significa que uma área funcional específica que requer mais poder de processamento ou largura de banda de rede para suportar a demanda pode ser dimensionada em vez de expandir desnecessariamente outras áreas de aplicativos. A imagem abaixo ilustra essa abordagem, onde os microsserviços são implantados e dimensionados de forma independente, criando instâncias de serviços entre máquinas.

Abordagem de dimensionamento de aplicativos de microsserviços.

O escalonamento de microsserviços pode ser quase instantâneo, permitindo que um aplicativo se adapte às mudanças de carga. Por exemplo, um único microsserviço na funcionalidade voltada para a Web de um aplicativo pode ser o único microsserviço que precisa ser dimensionado para lidar com tráfego de entrada adicional.

O modelo clássico para escalabilidade de aplicativos é ter uma camada sem monitoração de estado com balanceamento de carga com um armazenamento de dados externo compartilhado para armazenar dados persistentes. Os microsserviços com estado gerenciam seus próprios dados persistentes, geralmente armazenando-os localmente nos servidores nos quais são colocados, para evitar a sobrecarga de acesso à rede e a complexidade das operações entre serviços. Isso permite o processamento mais rápido possível de dados e pode eliminar a necessidade de sistemas de cache. Além disso, os microsserviços com estado escaláveis geralmente particionam dados entre suas instâncias, a fim de gerenciar o tamanho dos dados e a taxa de transferência além da qual um único servidor pode suportar.

Os microsserviços também suportam atualizações independentes. Esse acoplamento flexível entre microsserviços proporciona uma evolução rápida e confiável do aplicativo. Sua natureza independente e distribuída ajuda a rolar atualizações, onde apenas um subconjunto de instâncias de um único microsserviço será atualizado a qualquer momento. Portanto, se um problema for detetado, uma atualização com bug pode ser revertida, antes que todas as instâncias sejam atualizadas com o código ou configuração defeituosos. Da mesma forma, os microsserviços normalmente usam o controle de versão de esquema, para que os clientes vejam uma versão consistente quando as atualizações estão sendo aplicadas, independentemente de qual instância de microsserviço está sendo comunicada.

Portanto, os aplicativos de microsserviço têm muitos benefícios em relação aos aplicativos monolíticos:

  • Cada microsserviço é relativamente pequeno, fácil de gerenciar e evoluir.
  • Cada microsserviço pode ser desenvolvido e implantado independentemente de outros serviços.
  • Cada microsserviço pode ser dimensionado de forma independente. Por exemplo, um serviço de catálogo ou um serviço de carrinho de compras pode precisar ser dimensionado mais do que um serviço de pedidos. Portanto, a infraestrutura resultante consumirá recursos de forma mais eficiente ao dimensionar.
  • Cada microsserviço isola quaisquer problemas. Por exemplo, se houver um problema em um serviço, isso afetará apenas esse serviço. Os outros serviços podem continuar a lidar com solicitações.
  • Cada microsserviço pode usar as tecnologias mais recentes. Como os microsserviços são autônomos e executados lado a lado, as tecnologias e estruturas mais recentes podem ser usadas, em vez de serem forçadas a usar uma estrutura mais antiga que pode ser usada por um aplicativo monolítico.

No entanto, uma solução baseada em microsserviços também tem desvantagens potenciais:

  • Escolher como particionar um aplicativo em microsserviços pode ser um desafio, pois cada microsserviço tem que ser completamente autônomo, de ponta a ponta, incluindo a responsabilidade por suas fontes de dados.
  • Os desenvolvedores devem implementar a comunicação entre serviços, o que adiciona complexidade e latência ao aplicativo.
  • Transações atômicas entre vários microsserviços geralmente não são possíveis. Portanto, os requisitos de negócios devem abraçar a consistência eventual entre microsserviços.
  • Na produção, há uma complexidade operacional na implantação e gerenciamento de um sistema comprometido de muitos serviços independentes.
  • A comunicação direta cliente-microsserviço pode dificultar a refatoração dos contratos de microsserviços. Por exemplo, ao longo do tempo, a forma como o sistema é particionado em serviços pode ter de ser alterada. Um único serviço pode se dividir em dois ou mais serviços e dois serviços podem se fundir. Quando os clientes se comunicam diretamente com microsserviços, esse trabalho de refatoração pode quebrar a compatibilidade com aplicativos cliente.

Contentorização

A conteinerização é uma abordagem ao desenvolvimento de software na qual um aplicativo e seu conjunto versionado de dependências, além de sua configuração de ambiente abstraída como arquivos de manifesto de implantação, são empacotados juntos como uma imagem de contêiner, testados como uma unidade e implantados em um sistema operacional host.

Um contêiner é um ambiente operacional isolado, controlado por recursos e portátil, onde um aplicativo pode ser executado sem tocar nos recursos de outros contêineres ou no host. Portanto, um contêiner se parece e age como um computador físico recém-instalado ou uma máquina virtual.

Há muitas semelhanças entre contêineres e máquinas virtuais, conforme ilustrado abaixo.

Comparação de máquinas virtuais e contêineres.

Um contêiner executa um sistema operacional, tem um sistema de arquivos e pode ser acessado por uma rede como se fosse uma máquina física ou virtual. No entanto, a tecnologia e os conceitos usados pelos contêineres são muito diferentes das máquinas virtuais. As máquinas virtuais incluem os aplicativos, as dependências necessárias e um sistema operacional convidado completo. Os contêineres incluem o aplicativo e suas dependências, mas compartilham o sistema operacional com outros contêineres, executando como processos isolados no sistema operacional host (exceto contêineres Hyper-V que são executados dentro de uma máquina virtual especial por contêiner). Portanto, os contêineres compartilham recursos e normalmente exigem menos recursos do que as máquinas virtuais.

A vantagem de uma abordagem de desenvolvimento e implantação orientada a contêineres é que ela elimina a maioria dos problemas que surgem de configurações de ambiente inconsistentes e os problemas que vêm com elas. Além disso, os contêineres permitem a rápida funcionalidade de escalonamento de aplicativos, instanciando novos contêineres conforme necessário.

Os conceitos-chave ao criar e trabalhar com contêineres são:

Conceito Description
Host do contêiner A máquina física ou virtual configurada para hospedar contêineres. O host de contêiner executará um ou mais contêineres.
Imagem de Contentor Uma imagem consiste em uma união de sistemas de arquivos em camadas empilhados uns sobre os outros e é a base de um contêiner. Uma imagem não tem estado e nunca muda à medida que é implantada em ambientes diferentes.
Contentor Um contêiner é uma instância de tempo de execução de uma imagem.
Imagem do SO do contêiner Os contêineres são implantados a partir de imagens. A imagem do sistema operacional do contêiner é a primeira camada em potencialmente muitas camadas de imagem que compõem um contêiner. Um sistema operacional de contêiner é imutável e não pode ser modificado.
Repositório de contêineres Cada vez que uma imagem de contêiner é criada, a imagem e suas dependências são armazenadas em um repositório local. Essas imagens podem ser reutilizadas muitas vezes no host do contêiner. As imagens de contêiner também podem ser armazenadas em um registro público ou privado, como o Docker Hub, para que possam ser usadas em diferentes hosts de contêiner.

As empresas estão adotando cada vez mais contêineres ao implementar aplicativos baseados em microsserviços, e o Docker se tornou a implementação de contêiner padrão que foi adotada pela maioria das plataformas de software e fornecedores de nuvem.

O aplicativo de referência eShop usa o Docker para hospedar quatro microsserviços back-end em contêineres, conforme ilustrado no diagrama abaixo.

Microsserviços back-end de aplicação de referência eShop.

A arquitetura dos serviços de back-end no aplicativo de referência é decomposta em vários subsistemas autônomos na forma de microsserviços e contêineres colaboradores. Cada microsserviço fornece uma única área de funcionalidade: um serviço de identidade, um serviço de catálogo, um serviço de pedidos e um serviço de cesto.

Cada microsserviço tem seu próprio banco de dados, permitindo que ele seja totalmente dissociado dos outros microsserviços. Quando necessário, a consistência entre bancos de dados de diferentes microsserviços é alcançada usando eventos no nível do aplicativo. Para obter mais informações, consulte Comunicação entre microsserviços.

Comunicação entre cliente e microsserviços

O aplicativo multiplataforma eShop se comunica com os microsserviços back-end em contêineres usando comunicação direta cliente-microsserviço , conforme mostrado abaixo.

Comunicação direta cliente-microsserviço.

Com comunicação direta cliente-microsserviço, o aplicativo multiplataforma faz solicitações a cada microsserviço diretamente por meio de seu ponto de extremidade público, com uma porta TCP diferente por microsserviço. Na produção, o ponto de extremidade normalmente seria mapeado para o balanceador de carga do microsserviço, que distribui solicitações entre as instâncias disponíveis.

Gorjeta

Considere o uso da comunicação do gateway de API.

A comunicação direta cliente-microsserviço pode ter desvantagens ao criar um aplicativo baseado em microsserviço grande e complexo, mas é mais do que adequada para um aplicativo pequeno. Considere o uso da comunicação de gateway de API ao projetar um grande aplicativo baseado em microsserviços com dezenas de microsserviços.

Comunicação entre microsserviços

Um aplicativo baseado em microsserviços é um sistema distribuído, potencialmente executado em várias máquinas. Cada instância de serviço é normalmente um processo. Portanto, os serviços devem interagir usando um protocolo de comunicação entre processos, como HTTP, TCP, AMQP (Advanced Message Queuing Protocol) ou protocolos binários, dependendo da natureza de cada serviço.

As duas abordagens comuns para comunicação de microsserviço para microsserviço são a comunicação REST baseada em HTTP ao consultar dados e mensagens assíncronas leves ao comunicar atualizações em vários microsserviços.

A comunicação assíncrona orientada a eventos baseada em mensagens é crítica ao propagar alterações em vários microsserviços. Com essa abordagem, um microsserviço publica um evento quando algo notável acontece, por exemplo, quando atualiza uma entidade comercial. Outros microsserviços subscrevem estes eventos. Então, quando um microsserviço recebe um evento, ele atualiza suas próprias entidades comerciais, o que, por sua vez, pode levar à publicação de mais eventos. Essa funcionalidade de publicação-assinatura geralmente é alcançada com um barramento de eventos.

Um barramento de eventos permite a comunicação publicação-assinatura entre microsserviços sem exigir que os componentes estejam explicitamente cientes uns dos outros, conforme mostrado abaixo.

Publique-se e inscreva-se com um ônibus de eventos.

Do ponto de vista do aplicativo, o barramento de eventos é simplesmente um canal de publicação-assinatura exposto por meio de uma interface. No entanto, a forma como o barramento de eventos é implementado pode variar. Por exemplo, uma implementação de barramento de evento pode usar RabbitMQ, Azure Service Bus ou outros barramentos de serviço, como NServiceBus e MassTransit. O diagrama abaixo mostra como um barramento de eventos é usado no aplicativo de referência eShop.

Comunicação assíncrona orientada a eventos no aplicativo de referência.

O barramento de eventos eShop, implementado usando RabbitMQ, fornece funcionalidade de publicação-assinatura assíncrona um-para-muitos. Isso significa que, após a publicação de um evento, pode haver vários assinantes ouvindo o mesmo evento. O diagrama abaixo ilustra esta relação.

Comunicação um-para-muitos

Essa abordagem de comunicação um-para-muitos usa eventos para implementar transações comerciais que abrangem vários serviços, garantindo uma eventual consistência entre os serviços. Uma transação consistente com o evento consiste em uma série de etapas distribuídas. Portanto, quando o microsserviço de perfil de usuário recebe o comando UpdateUser, ele atualiza os detalhes do usuário em seu banco de dados e publica o evento UserUpdated no barramento de eventos. Tanto o microsserviço de cesto como o microsserviço de encomenda subscreveram para receber este evento e, em resposta, atualizam as informações do comprador nas respetivas bases de dados.

Resumo

Os microsserviços oferecem uma abordagem de desenvolvimento e implantação de aplicativos adequada aos requisitos de agilidade, escala e confiabilidade dos aplicativos em nuvem modernos. Uma das principais vantagens dos microsserviços é que eles podem ser dimensionados de forma independente, o que significa que uma área funcional específica pode ser dimensionada que requer mais poder de processamento ou largura de banda de rede para suportar a demanda sem dimensionar desnecessariamente áreas do aplicativo que não estão enfrentando aumento da demanda.

Um contêiner é um ambiente operacional isolado, controlado por recursos e portátil onde um aplicativo pode ser executado sem tocar nos recursos de outros contêineres ou do host. As empresas estão adotando cada vez mais contêineres ao implementar aplicativos baseados em microsserviços, e o Docker se tornou a implementação de contêiner padrão que a maioria das plataformas de software e fornecedores de nuvem adotaram.