Desacoplando aplicações em Azure Container Apps utilizando Dapr

  • Thread starter Thread starter ClaudioGodoy
  • Start date Start date
C

ClaudioGodoy

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.

imagem da arquitetura alto nível do Dapr




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:


  1. 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.

  2. 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.

  3. 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.

  4. Secrets:
    • O building block Secrets gerencia e fornece acesso seguro a segredos sensíveis, como chaves de autenticação e senhas, para os aplicativos.

  5. 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.

  6. 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.

  7. Middleware:
    • Middleware permite a adição de funcionalidades personalizadas a aplicativos Dapr, como autenticação, autorização e manipulação de solicitações HTTP.

  8. 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.

imagem do building block integration




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:

Diagrama exemplificando os componentes




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...
 
Back
Top