Microsserviços conteinerizados
Dica
Esse conteúdo é um trecho do livro eletrônico, Padrões de Aplicativo Empresarial Usando .NETMAUI, disponível em .NET Docs ou em PDF para download gratuito que pode ser lido off-line.
O desenvolvimento de aplicativos cliente-servidor resultou em um foco na criação de aplicativos em camadas que usam tecnologias específicas em cada camada. Esses aplicativos geralmente são chamados de monolíticos e são empacotados em hardware pré-dimensionado para cargas de pico. As principais desvantagens dessa abordagem de desenvolvimento são o acoplamento estrito entre componentes em cada camada, o fato de os componentes individuais não poderem ser facilmente dimensionados e o custo do teste. Uma atualização simples pode ter efeitos imprevistos no restante da camada, ou seja, uma alteração em um componente de aplicativo requer que toda a camada seja retestada e reimplantada.
Particularmente preocupante, na era da nuvem, é que componentes individuais não possam 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 com a clonagem de todo o aplicativo em vários computadores.
Microsserviços
Os microsserviços oferecem uma abordagem diferente para desenvolvimento e implantação de aplicativos, uma abordagem adequada aos requisitos de agilidade, escala e confiabilidade de aplicativos de 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 específicas, ou seja, 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 cestas de compras, processamento de inventário, subsistemas de compra e processamento de pagamentos.
Os microsserviços podem ser colocados em escala independentemente em comparação com aplicativos monolíticos gigantes que são dimensionados juntos. Isso significa que uma área funcional específica que requeira mais capacidade de processamento ou largura de banda de rede para dar suporte à demanda pode ser colocada em escala em vez de exigir a colocação em escala desnecessária de outras áreas do aplicativo. A imagem abaixo ilustra essa abordagem, em que os microsserviços são implantados e colocados em escala de forma independente, criando instâncias de serviços entre computadores.
A expansão de microsserviços pode ser quase instantânea, permitindo que um aplicativo se adapte às cargas de alteração. 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 de escalabilidade do aplicativo é ter uma camada sem estado e 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, microsserviços escalonáveis com estado geralmente particionam dados entre suas instâncias, a fim de gerenciar o tamanho dos dados e a taxa de transferência além do limite ao qual um único servidor pode dar suporte.
Os microsserviços também dão suporte a atualizações independentes. Esse acoplamento flexível entre microsserviços fornece uma evolução rápida e confiável do aplicativo. Sua natureza independente e distribuída ajuda a lançar atualizações, nas quais apenas um subconjunto de instâncias de um único microsserviço será atualizado a qualquer momento. Portanto, se um problema for detectado, uma atualização com bug poderá ser revertida antes que todas as instâncias sejam atualizadas com o código ou a configuração com falha. 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 da instância de microsserviço com a qual estão se comunicando.
Portanto, os aplicativos de microsserviço têm muitos benefícios em relação a aplicativos monolíticos:
- Cada microsserviço é relativamente pequeno, fácil de gerenciar e desenvolver.
- Cada microsserviço pode ser desenvolvido e implantado de forma independente de outros serviços.
- Cada microsserviço pode ser colocado em escala de forma independente. Por exemplo, um serviço de catálogo ou serviço de cesta de compras pode precisar ser expandido mais do que um serviço de pedidos. Portanto, a infraestrutura resultante consumirá recursos com mais eficiência na expansão.
- Cada microsserviço isola eventuais problemas. Por exemplo, se houver um problema em um serviço, ele só afetará 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 terem a obrigação de usar uma estrutura mais antiga que pode ser usada por um aplicativo monolítico.
No entanto, uma solução baseada em microsserviço também tem possíveis desvantagens:
- A escolha de como particionar um aplicativo em microsserviços pode ser desafiadora, pois cada microsserviço precisa ser completamente autônomo, de ponta a ponta, incluindo a responsabilidade por suas fontes de dados.
- Os desenvolvedores precisam implementar a comunicação entre serviços, o que adiciona complexidade e latência ao aplicativo.
- Geralmente, as transações atômicas entre vários microsserviços não são possíveis. Portanto, os requisitos de negócios precisam adotar uma eventual consistência entre microsserviços.
- Na produção, há uma complexidade operacional na implantação e no gerenciamento de um sistema composto de muitos serviços independentes.
- A comunicação direta entre cliente e microsserviço pode dificultar a refatoração dos contratos de microsserviços. Por exemplo, ao longo do tempo, o modo como o sistema é particionado em serviços pode precisar ser alterado. Um único serviço pode ser dividido em dois ou mais serviços e dois serviços podem se mesclar. Quando os clientes se comunicam diretamente com microsserviços, esse trabalho de refatoração pode interromper a compatibilidade com aplicativos cliente.
Transporte em contêineres
A conteinerização é uma abordagem de desenvolvimento de software na qual um aplicativo e seu conjunto de dependências com controle de versão, 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, no qual um aplicativo pode ser executado sem tocar nos recursos de outros contêineres ou no host. Assim, um contêiner 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.
Um contêiner executa um sistema operacional, tem um sistema de arquivos e pode ser acessado por meio de uma rede como se fosse uma máquina 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, sendo executados como processos isolados no sistema operacional host (além dos contêineres do Hyper-V que são executados dentro de uma máquina virtual especial em cada 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 voltada para contêineres é que ela elimina a maioria dos problemas que surgem de configurações de ambiente inconsistentes e dos problemas que vêm com elas. Além disso, os contêineres permitem a funcionalidade de expansão rápida de aplicativos com a criação de instâncias de novos contêineres conforme a necessidade.
Os principais conceitos ao criar e trabalhar com contêineres são:
Conceito | Descrição |
---|---|
Host do contêiner | A máquina física ou virtual configurada para hospedar contêineres. O host do contêiner executará um ou mais contêineres. |
Imagem do contêiner | 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, pois é implantada em ambientes diferentes. |
Contêiner | Um contêiner é uma instância de runtime de uma imagem. |
Imagem do SO do contêiner | Os contêineres são implantados com base em imagens. A imagem do sistema operacional do contêiner é a primeira potencialmente de 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 | Sempre 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 várias 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 hosts de contêiner diferentes. |
As empresas estão cada vez mais adotando contêineres ao implementar aplicativos baseados em microsserviço, e o Docker tornou-se a implementação de contêiner padrão 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 de back-end conteinerizados, conforme ilustrado no diagrama abaixo.
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 cesta.
Cada microsserviço tem seu próprio banco de dados, permitindo que ele seja totalmente separado dos outros microsserviços. Quando necessário, a consistência entre bancos de dados de microsserviços diferentes é obtida usando eventos no nível do aplicativo. Para obter mais informações, confira Comunicação entre microsserviços.
Comunicação entre cliente e microsserviços
O aplicativo multiplataforma eShop se comunica com os microsserviços de back-end conteinerizados usando comunicação direta cliente com microsserviço, conforme mostrado abaixo.
Com a comunicação direta de cliente com microsserviço, o aplicativo multiplataforma faz solicitações para cada microsserviço diretamente por meio de seu ponto de extremidade público, com uma porta TCP diferente para cada microsserviço. Na produção, o ponto de extremidade normalmente é mapeado para o balanceador de carga do microsserviço, que distribui solicitações entre as instâncias disponíveis.
Dica
Considere usar a comunicação do gateway de API.
A comunicação direta entre cliente e microsserviço pode ter desvantagens na criação de um aplicativo baseado em microsserviço grande e complexo, mas é mais do que adequado para um aplicativo pequeno. Considere usar a comunicação de gateway de API ao criar um aplicativo grande baseado em microsserviço com dezenas de microsserviços.
Comunicação entre microsserviços
Um aplicativo baseado em microsserviços é um sistema distribuído, potencialmente em execução em vários computadores. Cada instância de serviço geralmente é um processo. Portanto, os serviços precisam 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 com microsserviço são a comunicação REST baseada em HTTP na consulta de dados e mensagens assíncronas leves na comunicação de atualizações em vários microsserviços.
A comunicação controlada por eventos baseada em mensagens assíncrona é essencial na propagação de alterações em vários microsserviços. Com essa abordagem, um microsserviço publica um evento quando algo notável acontece, por exemplo, quando ele atualiza uma entidade de negócios. Outros microsserviços assinam esses eventos. Em seguida, quando um microsserviço recebe um evento, ele pode atualizar suas próprias entidades de negócios, o que pode, por sua vez, levar à publicação de mais eventos. Essa funcionalidade de publicação-assinatura geralmente é obtida com um barramento de eventos.
Um barramento de evento permite comunicação publicar-assinar entre microsserviços sem a necessidade de os componentes estarem explicitamente cientes uns dos outros, como mostrado abaixo.
Do ponto de vista do aplicativo, o barramento de eventos é simplesmente um canal de publicação e assinatura exposto por meio de uma interface. No entanto, a maneira como o barramento de eventos é implementado pode variar. Por exemplo, uma implementação de barramento de eventos pode usar RabbitMQ, Barramento de Serviço do Azure ou outro barramento de serviço, como NServiceBus e MassTransit. O diagrama abaixo mostra como um barramento de eventos é usado no aplicativo de referência eShop.
O barramento de eventos do eShop, implementado usando o RabbitMQ, fornece funcionalidades assíncronas de publicação-assinatura de um para muitos. Isso significa que, depois de publicar um evento, pode haver vários assinantes escutando o mesmo evento. O diagrama a seguir ilustra essa relação.
Essa abordagem de comunicação um com muitos usa eventos para implementar transações comerciais que abrangem vários serviços, o que garante a consistência eventual entre os serviços. Uma transação eventual-consistente 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. O microsserviço da cesta e o microsserviço de pedidos assinaram para receber esse evento e, em resposta, atualizar suas informações de comprador em seus respectivos bancos de dados.
Resumo
Os microsserviços oferecem uma abordagem de desenvolvimento e implantação de aplicativos que é adequada aos requisitos de agilidade, escala e confiabilidade de aplicativos de 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, o que requer mais capacidade de processamento ou largura de banda de rede para dar suporte à demanda sem dimensionar desnecessariamente áreas do aplicativo que não estão enfrentando maior demanda.
Um contêiner é um ambiente operacional isolado, controlado por recursos e portátil, no qual um aplicativo pode ser executado sem tocar nos recursos de outros contêineres ou no host. As empresas estão cada vez mais adotando contêineres ao implementar aplicativos baseados em microsserviço, e o Docker tornou-se a implementação de contêiner padrão que a maioria das plataformas de software e fornecedores de nuvem adotou.