O Sertão será Cloud

Descubra o Segredo para Conectar sua Azure Function ao Cosmos DB como um Profissional!

O Azure Cosmos DB é um banco de dados NoSQL da Microsoft, projetado para alta escalabilidade e baixa latência, ideal para aplicações modernas que exigem resposta rápida e flexibilidade. Configurar o cliente do Cosmos DB corretamente é essencial para garantir segurança e eficiência, especialmente em cenários serverless. Neste artigo, demonstrarei como configurar uma Azure Function para se conectar ao Cosmos DB utilizando diferentes métodos de autenticação, aplicando boas práticas de Clean Code.

O que é o Azure Cosmos DB NoSQL?

Quando se trata de bancos de dados NoSQL, o Azure Cosmos DB é uma opção que realmente se destaca. É um serviço da Microsoft projetado para oferecer alta disponibilidade, baixa latência e escalabilidade global. O que eu mais gosto é que ele suporta vários modelos de dados, como documentos, chave-valor, grafos e famílias de colunas, o que o torna muito flexível para diferentes tipos de aplicações.

Principais Características:

  1. Distribuição Global: Um dos pontos fortes do Cosmos DB é a capacidade de replicar dados em múltiplas regiões do Azure, garantindo que os usuários tenham acesso rápido, independentemente de onde estejam.
  2. Multi-Modelo: Eu adoro que ele suporte várias APIs, incluindo SQL (Core), MongoDB, Cassandra, Gremlin (para grafos) e Table API. Isso me permite escolher o modelo que melhor se adapta às necessidades do meu projeto.
  3. Baixa Latência: Oferece tempos de resposta em milissegundos em operações de leitura e gravação, mesmo quando escalado. Isso é essencial para aplicações que precisam de desempenho rápido.
  4. Escalabilidade Automática: Outra vantagem é a capacidade de ajustar o throughput e o armazenamento de maneira dinâmica, sem downtime. Isso significa que posso adaptar meus recursos conforme as demandas do aplicativo mudam.
  5. Segurança e Conformidade: O Cosmos DB inclui recursos avançados de segurança, como criptografia em repouso e em trânsito, e está em conformidade com várias normas e regulamentações, o que é crucial para mim ao lidar com dados sensíveis.

Casos de Uso Comuns:

Eu vejo o Azure Cosmos DB sendo amplamente utilizado em:

  • Aplicações web e móveis que requerem desempenho rápido e confiável.
  • Sistemas de gerenciamento de conteúdo que precisam de flexibilidade no modelo de dados.
  • Jogos online que exigem baixa latência e escalabilidade.
  • Aplicações IoT que geram grandes volumes de dados.

O Azure Cosmos DB é uma solução robusta e escalável, ideal para lidar com as crescentes necessidades de dados em um ambiente digital que está sempre evoluindo.

Azure Cosmos DB: Melhores Práticas

Ao implementar o Azure Cosmos DB, aproveitar os princípios do Azure Well-Architected Framework (WAF) e do Cloud Adoption Framework (CAF) pode melhorar significativamente a eficácia e a confiabilidade da sua solução de banco de dados.

1. Eficiência de Desempenho

  • Otimização de Throughput: Utilize o modelo de throughput provisionado de forma eficaz, escalando recursos com base na demanda da sua aplicação. Monitore padrões de uso e ajuste o throughput para manter o desempenho enquanto controla os custos.
  • Particionamento de Dados: Projete seu modelo de dados considerando o particionamento para garantir uma distribuição equilibrada e minimizar a latência das requisições. Escolha chaves de partição apropriadas com base nos padrões de acesso.

2. Gestão de Custos

  • Dimensionamento Correto: Avalie e ajuste regularmente o throughput e o armazenamento alocados às suas instâncias do Cosmos DB. Use as ferramentas de Gestão de Custos do Azure para analisar despesas e identificar áreas de otimização.
  • Uso do Nível Gratuito e Escalabilidade: Se você está começando, aproveite o nível gratuito do Azure Cosmos DB para explorar e desenvolver sua aplicação sem incorrer em custos, escalando conforme necessário.

3. Segurança

  • Identidades Gerenciadas: Utilize a Identidade Gerenciada do Azure para acesso seguro ao Cosmos DB sem a necessidade de codificar credenciais. Isso reduz o risco de exposição de credenciais.
  • Segurança de Rede: Implemente endpoints de serviço de rede virtual ou links privados para restringir o acesso às suas contas do Cosmos DB, garantindo que apenas aplicações confiáveis possam se conectar.

4. Confiabilidade

  • Replicação Multi-Regional: Aproveite as capacidades de distribuição global do Cosmos DB. Configure gravações em múltiplas regiões para garantir alta disponibilidade e recuperação de desastres, permitindo uma rápida recuperação em caso de falhas regionais.
  • Estratégias de Backup: Realize backups regulares dos seus dados e implemente políticas de backup automatizadas para garantir a recuperação dos dados em caso de perda ou corrupção acidental.

5. Governança Operacional

  • Monitoramento e Alertas: Use o Azure Monitor e o Application Insights para rastrear métricas de desempenho, configurar alertas para anomalias e garantir que suas instâncias do Cosmos DB estejam operando de forma otimizada.
  • Conformidade: Assegure que o uso do Cosmos DB esteja em conformidade com as políticas organizacionais e requisitos regulatórios, utilizando o Azure Policy para impor padrões de governança.

Ao integrar essas práticas no seu uso do Azure Cosmos DB, você pode construir aplicações robustas, seguras e custo-efetivas que aproveitam ao máximo as capacidades da plataforma, alinhando-se às melhores práticas do WAF e do CAF. vamos a prática?

Passo 1: Configuração do Ambiente

Antes de começarmos, instale as dependências necessárias:

npm install @azure/cosmos @azure/identity

Passo 2: Criando a Abstração CredentialProvider

Vamos criar uma interface CredentialProvider que define um método getCredential.

import { TokenCredential } from "@azure/identity";
/**
* Interface for providing Azure credentials.
*/
interface CredentialProvider {
/**
* Retrieves the Azure credential.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential;
}

Passo 3: Implementando Provedores de Credenciais

Criaremos diferentes implementações de CredentialProvider para vários métodos de autenticação.

Método 1: SystemAssignedManagedIdentityCredentialProvider

import { DefaultAzureCredential } from "@azure/identity";
/**
* Provides credentials for system-assigned managed identity.
*/
class SystemAssignedManagedIdentityCredentialProvider implements CredentialProvider {
/**
* Retrieves the Azure credential for system-assigned managed identity.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential {
return new DefaultAzureCredential();
}
}

Método 2: UserAssignedManagedIdentityCredentialProvider

import { DefaultAzureCredential } from "@azure/identity";

/**
* Provides credentials for user-assigned managed identity.
*/
class UserAssignedManagedIdentityCredentialProvider implements CredentialProvider {
private clientId: string;
/**
* Creates an instance of UserAssignedManagedIdentityCredentialProvider.
* @param {string} clientId - The client ID of the user-assigned managed identity.
*/
constructor(clientId: string) {
this.clientId = clientId;
}
/**
* Retrieves the Azure credential for user-assigned managed identity.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential {
return new DefaultAzureCredential({
managedIdentityClientId: this.clientId
});
}
}

Método 3: ServicePrincipalCredentialProvider

import { ClientSecretCredential } from "@azure/identity";
/**
* Provides credentials for service principal authentication.
*/
class ServicePrincipalCredentialProvider implements CredentialProvider {
private tenantId: string;
private clientId: string;
private clientSecret: string;
/**
* Creates an instance of ServicePrincipalCredentialProvider.
* @param {string} tenantId - The tenant ID.
* @param {string} clientId - The client ID.
* @param {string} clientSecret - The client secret.
*/
constructor(tenantId: string, clientId: string, clientSecret: string) {
this.tenantId = tenantId;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
/**
* Retrieves the Azure credential for service principal authentication.
* @returns {TokenCredential} The Azure credential.
*/
getCredential(): TokenCredential {
return new ClientSecretCredential(this.tenantId, this.clientId, this.clientSecret);
}
}

Passo 4: Arquitetura com Provedores de Credenciais

Aqui está uma fábrica para instanciar o cliente CosmoDB NoSQL com o CredentialProvider.

import { CosmosClient } from "@azure/cosmos";

/**
* @class CosmosClientFactory
* Factory responsible for creating instances of CosmosClient.
* This class abstracts the client creation logic, allowing the use
* of different credential providers for authentication.
*/
class CosmosClientFactory {
private endpoint: string;
private credentialProvider: CredentialProvider;

/**
* @constructor
* @param {string} endpoint - URL of the Azure Cosmos DB resource.
* @param {CredentialProvider} credentialProvider - Credential provider to be used.
*/
constructor(endpoint: string, credentialProvider: CredentialProvider) {
this.endpoint = endpoint;
this.credentialProvider = credentialProvider;
}

/**
* Creates a new instance of the CosmosClient with the provided credentials.
* @returns {CosmosClient} Configured instance of the CosmosClient.
*/
createCosmosClient(): CosmosClient {
const credential = this.credentialProvider.getCredential();
return new CosmosClient({ endpoint: this.endpoint, aadCredentials: credential });
}
}

Passo 5: Utilizando a Fábrica em uma Azure Function

Vamos integrar isso em uma Azure Function, usando Managed Identity.

Exemplo 1: Azure Function com Managed Identity Atribuída ao Sistema

import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { CosmosClientFactory } from "./CosmosClientFactory";
import { SystemAssignedManagedIdentityCredentialProvider } from "./SystemAssignedManagedIdentityCredentialProvider";

const endpoint = process.env.AZURE_COSMOS_RESOURCEENDPOINT;

/**
* Azure Function using system-assigned managed identity to access Azure CosmoDB NoSQL.
*
* @param context - The Azure Function context.
* @param req - The HTTP request.
*
* @returns A promise that resolves when the function is complete.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const credentialProvider = new SystemAssignedManagedIdentityCredentialProvider();
const cosmosClientFactory = new CosmosClientFactory(endpoint, credentialProvider);

const cosmosClient = cosmosClientFactory.createCosmosClient();
const database = cosmosClient.database("my-database");
const container = database.container("my-container");

const { resource } = await container.item("item-id").read();
context.res = {
status: 200,
body: resource
};
};

export default httpTrigger;

Exemplo 2: Azure Function com Managed Identity Atribuída ao Usuário

import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { CosmosClientFactory } from "./CosmosClientFactory";
import { UserAssignedManagedIdentityCredentialProvider } from "./UserAssignedManagedIdentityCredentialProvider";

const endpoint = process.env.AZURE_COSMOS_RESOURCEENDPOINT;
const clientId = process.env.AZURE_MYSQL_CLIENTID;


/**
* Azure Function using user-assigned managed identity to access Azure CosmoDB NoSQL.
*
* @param context - The Azure Function context.
* @param req - The HTTP request.
*
* @returns A promise that resolves when the function is complete.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const credentialProvider = new UserAssignedManagedIdentityCredentialProvider(
clientId,
);
const cosmosClientFactory = new CosmosClientFactory(endpoint, credentialProvider);

const cosmosClient = cosmosClientFactory.createCosmosClient();
const database = cosmosClient.database("my-database");
const container = database.container("my-container");

const { resource } = await container.item("item-id").read();
context.res = {
status: 200,
body: resource
};
};

export default httpTrigger;

Exemplo 3: Azure Function com Service Principal

import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import { CosmosClientFactory } from "./CosmosClientFactory";
import { ServicePrincipalCredentialProvider } from "./ServicePrincipalCredentialProvider";

const endpoint = process.env.AZURE_COSMOS_RESOURCEENDPOINT;
const tenantId = process.env.AZURE_COSMOS_TENANTID;
const clientId = process.env.AZURE_COSMOS_CLIENTID;
const clientSecret = process.env.AZURE_COSMOS_CLIENTSECRET;

/**
* Azure Function using user-assigned managed identity to access Azure CosmoDB NoSQL.
*
* @param context - The Azure Function context.
* @param req - The HTTP request.
*
* @returns A promise that resolves when the function is complete.
*/
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
const credentialProvider = new ServicePrincipalCredentialProvider(
tenantId, clientId, clientSecret,
);
const cosmosClientFactory = new CosmosClientFactory(endpoint, credentialProvider);

const cosmosClient = cosmosClientFactory.createCosmosClient();
const database = cosmosClient.database("my-database");
const container = database.container("my-container");

const { resource } = await container.item("item-id").read();
context.res = {
status: 200,
body: resource
};
};

export default httpTrigger;

Conclusão

Aplicando os princípios de Clean Code como modularidade e clareza, criamos um design escalável para conectar-se ao Cosmos DB.

Com essas diretrizes e a implementação apresentada, você estará apto a construir soluções robustas e seguras com o Azure Cosmos DB em um ambiente serverless.