Posted October 16, 20231 yr O avanço das arquiteturas de microsserviços levou a uma reorganização das responsabilidades entre serviços. Isso impulsionou a adoção de contêineres e consequentemente, soluções de orquestração como o Kubernetes. No entanto, essa abordagem trouxe desafios, como o acoplamento excessivo entre aplicações e serviços externos. Para mitigar isso, surgiu o Dapr, um runtime que permite comunicação desacoplada entre aplicações. Este artigo foca em apresentar o Dapr e como configurá-lo no Azure Container Apps, abordando funcionalidades essenciais e procedimentos práticos para uma integração bem-sucedida. Azure Container Apps O Azure Container Apps é a solução de orquestração de contêineres do Azure. Ele permite que você implante e gerencie contêineres sem se preocupar com a infraestrutura subjacente. O Azure Container Apps é baseado no Azure Kubernetes Service, mas oferece uma experiência simplificada para implantar e gerenciar contêineres. Podemos definir o Azure Container Apps como sendo uma solução serverless de Kubernetes. Esse serviço é um ótimo acelerador para empresas que estão iniciando sua jornada de adoção de contêineres, pois permite que você implante e gerencie contêineres sem se preocupar com a infraestrutura. Dapr O Dapr codifica as melhores práticas para a construção de aplicativos baseados em microsserviços em APIs abertas e independentes chamadas de building block, que permitem que você crie aplicativos portáteis com a linguagem e o framework de sua escolha. Cada building block é totalmente independente, e você pode usar um, alguns ou todos eles em seu aplicativo. Usando o Dapr, você pode migrar gradualmente seus aplicativos existentes para uma arquitetura de microsserviços, adotando padrões nativos da nuvem, como dimensionamento sob demanda, resiliência e implantações independentes. Além disso, o Dapr é independente de plataforma, o que significa que você pode executar seus aplicativos localmente, em qualquer cluster Kubernetes, em máquinas virtuais ou físicas e em outros ambientes de hospedagem que o Dapr integra. Isso permite que você construa aplicativos baseados em microsserviços que podem ser executados na nuvem e na borda. Building Blocks Um building block é uma API HTTP ou gRPC, que pode ser chamada a partir do seu código e utiliza um ou mais componentes do Dapr. O Dapr é composto por um conjunto de unidades de construção de API, com a capacidade de adicionar novas unidades de construção para estender suas funcionalidades. Abaixo está uma lista dos principais building blocks do Dapr: State: O building block State permite que os aplicativos armazenem e recuperem estado de maneira confiável. Ele oferece suporte a várias opções de estado, incluindo estado persistente e temporário. [*] Pub/Sub: O Pub/Sub (Publicação/Assinatura) facilita a comunicação entre os diferentes componentes de um aplicativo distribuído. Os aplicativos podem publicar eventos e se inscrever para receber notificações quando esses eventos ocorrem. [*] Bindings: Bindings permitem que os aplicativos interajam facilmente com recursos externos, como bancos de dados, sistemas de mensagens e serviços da nuvem, por meio de adaptadores pré-construídos. [*] Secrets: O building block Secrets gerencia e fornece acesso seguro a segredos sensíveis, como chaves de autenticação e senhas, para os aplicativos. [*] Actors: O Actors é um modelo de programação baseado em atores que facilita a criação de aplicativos de estado escaláveis e com estado isolado. [*] Observability: O Observability é uma parte fundamental do Dapr que fornece recursos de rastreamento, métricas e registro para facilitar a monitoração e solução de problemas de aplicativos. [*] Middleware: Middleware permite a adição de funcionalidades personalizadas a aplicativos Dapr, como autenticação, autorização e manipulação de solicitações HTTP. [*] HTTP API: O HTTP API permite que os aplicativos exponham APIs HTTP de maneira simplificada, facilitando a comunicação com outros serviços. Na perspectiva das aplicações em execução no Kubernetes, esses building blocks são implementados como sidecars de contêineres. Isso significa que cada building block é executado como um contêiner adjacente à aplicação principal. Componente Um building block é exposto através de um contêiner sidecar associado a cada aplicação, atuando de forma isolada para fornecer uma funcionalidade específica, como gerenciamento de estado ou comunicação de eventos. Por outro lado, um componente do Dapr é uma peça central que lida com a complexidade da comunicação com o serviço final. Em vez de ser um contêiner sidecar individual, ele serve como uma camada intermediária entre a aplicação e os serviços externos. Portanto, enquanto os building blocks oferecem funcionalidades específicas diretamente para cada aplicação por meio de sidecars, os componentes do Dapr desempenham um papel mais central e abrangente. A imagem abaixo representa claramente o papel do componente: Componente no Container App Environment Os componentes usam um design modular, e podem ser compartilhados entre aplicações. Eles são executados como um processo separado, fora do escopo da aplicação. Os componentes são configurados em nível de ambiente do Container App, mesmo que a plataforma se responsabilize por criar e configurar o Dapr, ainda precisaremos configurar os componentes. Essa configuração é feita através de arquivos de definição yaml, cada tipo de componente conterá suas próprias propriedades. No artigo, estamos demonstrando a integração do componente de pub/sub do Dapr com o serviço do Service Bus. Existe uma diferença entre o arquivo utilizado para criação de componentes direto em um cluster Kubernetes e o arquivo utilizado para criação de componentes no ambiente do Container App. Todos os componentes de código aberto do Dapr seguem o seguinte esquema básico: apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: [COMPONENT-NAME] namespace: [COMPONENT-NAMESPACE] spec: type: [COMPONENT-TYPE] version: v1 initTimeout: [TIMEOUT-DURATION] ignoreErrors: [bOOLEAN] metadata: - name: [METADATA-NAME] value: [METADATA-VALUE] No Azure Container Apps, o esquema acima foi ligeiramente simplificado para suportar os componentes do Dapr e remover campos desnecessários, incluindo apiVersion, kind e propriedades redundantes de metadados e especificações. componentType: [COMPONENT-TYPE] version: v1 initTimeout: [TIMEOUT-DURATION] ignoreErrors: [bOOLEAN] metadata: - name: [METADATA-NAME] value: [METADATA-VALUE] No nosso exemplo, o arquivo ficou da seguinte forma: componentType: pubsub.azure.servicebus.queues version: v1 ignoreErrors: false secrets: - name: connectionstring value: <VALOR DA CONNECTION-STRING> metadata: - name: connectionString secretRef: connectionstring scopes: - app-publisher - app-subscriber A propriedade scopes define quais aplicações terão acesso ao componente, portanto é obrigatória. Para criar o componente no ambiente do Container App, rode o comando: az containerapp env dapr-component set --name ENVIRONMENT_NAME --resource-group RESOURCE_GROUP_NAME --dapr-component-name pubsub --yaml "./pubsub.yaml" Se o comando funcionou corretamente, verá um resultado parecido com: { "id": "/subscriptions/60e0dc1e-a3f7-44cb-8561-17980fce2670/resourceGroups/tdc-huebr/providers/Microsoft.App/managedEnvironments/tdc-huebr-env/daprComponents/pubsubgeneric", "name": "pubsub", "properties": { "componentType": "pubsub.azure.servicebus.queues", "ignoreErrors": false, "metadata": [ { "name": "connectionString", "secretRef": "connectionstring" } ], "scopes": [ "app-publisher", "app-subscriber" ], "secrets": [ { "name": "connectionstring" } ], "version": "v1" }, "resourceGroup": "RESOURCE_GROUP_NAME", "systemData": { "createdAt": "2023-09-13T13:08:13.2180324Z", "createdBy": "<SEU-EMAIL>", "createdByType": "User", "lastModifiedAt": "2023-09-13T13:08:13.2180324Z", "lastModifiedBy": "<SEU-EMAIL>", "lastModifiedByType": "User" }, "type": "Microsoft.App/managedEnvironments/daprComponents" } Projeto PUB/SUB Para demonstrar o uso do Dapr, criamos um projeto de exemplo que utiliza o componente de pub/sub do Dapr para publicar e consumir mensagens de um tópico do Service Bus. O projeto é composto por duas aplicações, uma que publica mensagens e outra que consome as mensagens. O building block de publicação e inscrição do Dapr fornece um framework de API agnóstico à plataforma para enviar e receber mensagens. Seus serviços publicam mensagens em um tópico. Seus serviços se inscrevem em um tópico para consumir mensagens. O serviço faz chamadas à API de publicação/assinatura (pub/sub) no sidecar do Dapr. O sidecar então faz chamadas para o componente do Dapr criado previamente que encapsula a lógica para comunicação com o Service Bus. Aplicação Publicadora Utilizamos o Dapr .NET Sdk, para lidar com a complexidade da comunicação com o container sidecar do Dapr. O código abaixo mostra como configurar o DaprClient para publicar mensagens no tópico TOPICO: using Dapr.Client; var daprClient = new DaprClientBuilder().Build(); await daprClient.PublishEventAsync<int>("NOME_DO_COMPONENTE", "TOPICO", new object()); Esse trecho de código será responsável por encapsular a chamada HTTP ou gRPC para o sidecar do Dapr, essa chamada vai acontecer no Endpoint: http://localhost:<dapr-port>/v1.0/publish/<pub-sub-name>/<topic> Aplicação Consumidora Essa aplicação vai funcionar de forma passiva, onde o sidecar do Dapr vai ficar escutando o tópico TOPICO e quando uma mensagem for publicada, o sidecar vai fazer uma chamada HTTP ou gRPC para a aplicação consumidora. No início, o tempo de execução do Dapr chamará o aplicativo em um ponto de extremidade conhecido para identificar e criar as assinaturas necessárias: http://localhost:<appPort>/dapr/subscribe O sidecar vai utilizar da resposta desse endpoint como insumo para mapear quais são os tópicos que a aplicação consome. A biblioteca Dapr.AspNetCore deixa esse processo trivial para o desenvolvedor, onde com poucas linhas de código, podemos configurar esse endpoint que o sidecar vai usar posteriormente. O primeiro passo é instalar o pacote Dapr.AspNetCore: dotnet add package Dapr.AspNetCore Podemos utilizar a classe de atributo Topic sobre o método que vai receber as mensagens do tópico TOPICO, dessa forma: [Topic("NOME_DO_COMPONENTE","TOPICO")] [HttpPost("/count")] public IActionResult Post(int value) { ... return Ok(); } Após isso, precisamos configurar a aplicação fazendo modificações na classe Program.cs: var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers().AddDapr(); var app = builder.Build(); app.UseCloudEvents(); app.MapControllers(); app.MapSubscribeHandler(); app.Run(); O segmento de código .AddDapr() registra os serviços necessários como o DaprClient. O segmento de código MapSubscribeHandler() registra o endpoint que o sidecar vai utilizar para identificar e criar as assinaturas necessárias, para atingir esse objetivo ele mapeia todos os endpoints decorados com o atributo Topic. A fins de curiosidade, a lógica que o método MapSubscribeHandler() utiliza para identificar os endpoints decorados com o atributo Topic é a seguinte: private static IEndpointConventionBuilder CreateSubscribeEndPoint(IEndpointRouteBuilder endpoints, SubscribeOptions options = null) { if (endpoints is null) { throw new System.ArgumentNullException(nameof(endpoints)); } return endpoints.MapGet("dapr/subscribe", async context => { var logger = context.RequestServices.GetService<ILoggerFactory>().CreateLogger("DaprTopicSubscription"); var dataSource = context.RequestServices.GetRequiredService<EndpointDataSource>(); var subscriptions = dataSource.Endpoints .OfType<RouteEndpoint>() .Where(e => e.Metadata.GetOrderedMetadata<ITopicMetadata>().Any(t => t.Name != null)) .SelectMany(e => { ... }) .Distinct() .GroupBy(e => new { e.PubsubName, e.Name }) .Select(e => e.OrderBy(e => e.Priority)) .Select(e => { ... }) .OrderBy(e => (e.PubsubName, e.Topic)); await context.Response.WriteAsync(JsonSerializer.Serialize(subscriptions, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull })); }); } Utilizando a classe EndpointDatasource a lib consegue identificar todos os endpoints decorados com o atributo Topic através do seguimento de código .Where(e => e.Metadata.GetOrderedMetadata<ITopicMetadata>().Any(t => t.Name != null)). No final ele vai retornar um json com todas as assinaturas que o sidecar vai criar, no endpoint dapr/subscribe. Para saber mais veja o código fonte no repositório: Dapr.AspNetCore. Teste local Para testar seu projeto, você pode executar as aplicações localmente, para isso, você precisa instalar o Dapr CLI. Após instalar o Dapr CLI, você precisa inicializar o Dapr localmente, para isso, rode o comando: dapr init Após isso, você precisa configurar o componente, para isso entre na pasta .dapr no local de instalação, depois na pasta components, crie o arquivo pubsub.yaml e cole o conteúdo abaixo: apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: pubsub spec: type: pubsub.azure.servicebus.queues version: v1 metadata: - name: connectionString value: "<CONNECTION_STRING>" Após a criação do componente, basta rodar as aplicações, para isso, abra os terminais no diretório das aplicações e rode os comandos: dapr run --app-id publisher -- dotnet run dapr run --app-id subscriber -- dotnet run Conclusão Ao realizar a leitura deste artigo, você estará pronto para iniciar sua jornada de adoção do Dapr no Azure Container Apps. O Dapr é uma ferramenta poderosa para desacoplar aplicações, ele permite que você implante e gerencie contêineres sem se preocupar com a infraestrutura. Além disso, o Dapr é independente de plataforma, o que significa que você pode executar seus aplicativos localmente, em qualquer cluster Kubernetes, em máquinas virtuais ou físicas e em outros ambientes de hospedagem que o Dapr integra. Continue reading...
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.