> ## Content Index
> Fetch the complete content index at: https://www.azurebrasil.cloud/llms.txt
> Use this file to discover other available public pages before exploring further.

# Azure Container Apps em produção: serverless containers com escala automática, Dapr e observabilidade - Parte 1
- URL: https://www.azurebrasil.cloud/blog/azure-container-apps-em-producao-serverless-containers-com-escala-automatica-dapr-e-observabilidade-parte-1/
- Published: 2026-05-20T12:55:18.000Z
- Updated: 2026-05-20T12:55:18.000Z
- Author: Ricardo Martins

## Introdução

Se você acompanha esta série sobre infraestrutura Azure, já vimos como operar **AKS em cenários multi-cluster para IA**, como implementar **observabilidade avançada para GPU** e como orquestrar **networking avançado entre clusters**. Mas nem todo workload precisa da complexidade do Kubernetes. Muitas equipes querem rodar containers em produção — com escala automática, integração com filas e eventos, e observabilidade nativa — sem gerenciar clusters, node pools ou upgrades de control plane.

É exatamente aqui que o **Azure Container Apps (ACA)** se posiciona: uma plataforma serverless para containers que abstrai toda a infraestrutura subjacente (que, por baixo dos panos, roda sobre Kubernetes e KEDA), oferecendo escala automática de zero a milhares de réplicas, integração nativa com **Dapr** para microserviços, **Jobs** para processamento batch, e um modelo de billing por segundo de consumo.

Neste artigo, vamos explorar Container Apps em profundidade: desde os conceitos fundamentais até arquiteturas avançadas de microserviços em produção, passando por implementação prática com CLI e Bicep, escala com KEDA, integração com Dapr, Jobs para batch processing, networking com VNet, observabilidade com Azure Monitor e os erros mais comuns em ambientes reais.

## Quando usar Container Apps (e quando não usar)

Antes de mergulhar na implementação, é fundamental entender o posicionamento do Azure Container Apps no ecossistema de compute do Azure. A escolha errada de plataforma gera atrito operacional que acompanha o projeto por toda sua vida útil.

### Tabela comparativa: Container Apps vs AKS vs App Service vs Functions

| Critério                          | Container Apps             | AKS                          | App Service                         | Azure Functions     |
| --------------------------------- | -------------------------- | ---------------------------- | ----------------------------------- | ------------------- |
| **Gerenciamento de infra**        | Zero (serverless)          | Você gerencia nodes/clusters | Gerenciado (com limitações)         | Zero (serverless)   |
| **Escala para zero**              | ✅ Sim                      | ❌ Mínimo 1 node              | ❌ Mínimo 1 instância (exceto Linux) | ✅ Sim (Consumption) |
| **Containers customizados**       | ✅ Qualquer imagem          | ✅ Qualquer imagem            | ⚠️ Limitado                         | ⚠️ Custom handlers  |
| **Orquestração de microserviços** | ✅ Dapr nativo              | ✅ Total controle             | ❌ Não é o foco                      | ❌ Não é o foco      |
| **Jobs/Batch**                    | ✅ ACA Jobs                 | ✅ CronJobs/Jobs              | ✅ WebJobs                           | ✅ Timer triggers    |
| **Networking avançado**           | ✅ VNet + Private Endpoints | ✅ Total controle             | ✅ VNet integration                  | ✅ VNet integration  |
| **GPU**                           | ✅ Serverless e dedicado    | ✅ GPU node pools             | ❌                                   | ❌                   |
| **Custo em idle**                 | $0 (escala a zero)         | $$ (nodes sempre rodando)    | $$ (plano always-on)                | $0 (Consumption)    |
| **Complexidade operacional**      | Baixa                      | Alta                         | Baixa                               | Muito baixa         |

### Use Container Apps quando:

- Você tem **microserviços em containers** e quer evitar a complexidade do Kubernetes
- Precisa de **escala automática baseada em eventos** (filas, tópicos, HTTP)
- Quer **escala para zero** com billing por segundo
- Usa ou quer usar **Dapr** para comunicação entre serviços, pub/sub e state management
- Tem workloads de **batch/ETL** que rodam periodicamente ou sob demanda
- Precisa de **GPU serverless** para inferência de IA sem gerenciar VMs

### Não use Container Apps quando:

- Precisa de **controle total sobre o cluster Kubernetes** (custom operators, CRDs complexos, service mesh customizado)
- Tem workloads que exigem **acesso direto ao kernel** ou configurações de rede de baixo nível
- Precisa de **volumes persistentes complexos** (StatefulSets com storage dinâmico)
- Já tem um time maduro de **platform engineering** com AKS bem operado

## Conceitos fundamentais

Para trabalhar com Container Apps de forma eficiente, você precisa dominar quatro conceitos-chave que formam a hierarquia da plataforma.

### Environment (Ambiente)

O **Container Apps Environment** é o boundary de isolamento lógico. Todos os apps dentro de um environment compartilham a mesma rede virtual, configuração de logging e região. Pense nele como o equivalente a um namespace com rede compartilhada.

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2026/04/container-apps-environment-hierarchy.drawio.png)

**Tipos de planos:**

| Plano           | Características                                               | Ideal para                                                 |
| --------------- | ------------------------------------------------------------- | ---------------------------------------------------------- |
| **Consumption** | Serverless puro, escala a zero, billing por segundo           | Maioria dos workloads, dev/test, APIs com tráfego variável |
| **Dedicated**   | Compute reservado, GPUs, workloads que não toleram cold start | Inferência de IA, workloads latency-sensitive, compliance  |

### Container App

A **Container App** é a unidade de deploy. Cada app pode ter um ou mais containers (sidecar pattern), configurações de ingress (HTTP ou TCP), secrets, variáveis de ambiente e regras de escala.

### Revision

Uma **Revision** é um snapshot imutável do app em um ponto no tempo. Cada vez que você atualiza a configuração ou a imagem do container, uma nova revision é criada. Você pode fazer **traffic splitting** entre revisions para blue-green deployments ou canary releases.

### Replica

Uma **Replica** é uma instância individual do container rodando. A escala automática controla quantas réplicas existem (de 0 a N) baseado nas regras configuradas.

## Implementação prática

Vamos construir uma aplicação completa com uma API REST e um worker que processa mensagens de uma fila — o cenário clássico de microserviços em produção.

### Pré-requisitos

```bash
# Instalar/atualizar a extensão de Container Apps
az extension add --name containerapp --upgrade

# Registrar os providers necessários
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights

```

### Criando o Environment

```bash
# Variáveis
RESOURCE_GROUP="rg-containerapps-prod"
LOCATION="brazilsouth"
ENVIRONMENT="env-producao"

# Criar resource group
az group create \
  --name $RESOURCE_GROUP \
  --location $LOCATION

# Criar o environment com Log Analytics
az containerapp env create \
  --name $ENVIRONMENT \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION \
  --logs-destination log-analytics

```

### Deploy de uma API REST

```bash
# Deploy da API com ingress externo
az containerapp create \
  --name api-pedidos \
  --resource-group $RESOURCE_GROUP \
  --environment $ENVIRONMENT \
  --image mcr.microsoft.com/k8se/quickstart:latest \
  --target-port 80 \
  --ingress external \
  --min-replicas 1 \
  --max-replicas 10 \
  --cpu 0.5 \
  --memory 1.0Gi \
  --env-vars "APP_ENV=production" "LOG_LEVEL=info"

```

O parâmetro `--ingress external` expõe o app com um FQDN público. Use `--ingress internal` para apps que só devem ser acessíveis dentro do environment.

### Deploy com imagem de um Azure Container Registry (ACR) privado

Na prática, você vai usar imagens do seu próprio registry. A abordagem recomendada para produção é **user-assigned managed identity** com role `AcrPull`:

```bash
ACR_NAME="acrprodcontainers"

# 1. Criar user-assigned managed identity
az identity create \
  --name id-containerapps-acr \
  --resource-group $RESOURCE_GROUP

IDENTITY_ID=$(az identity show \
  --name id-containerapps-acr \
  --resource-group $RESOURCE_GROUP \
  --query id -o tsv)

IDENTITY_CLIENT_ID=$(az identity show \
  --name id-containerapps-acr \
  --resource-group $RESOURCE_GROUP \
  --query clientId -o tsv)

# 2. Atribuir role AcrPull no ACR
ACR_ID=$(az acr show --name $ACR_NAME --query id -o tsv)
az role assignment create \
  --assignee $IDENTITY_CLIENT_ID \
  --role AcrPull \
  --scope $ACR_ID

# 3. Deploy usando user-assigned identity (recomendado para produção)
az containerapp create \
  --name api-pedidos \
  --resource-group $RESOURCE_GROUP \
  --environment $ENVIRONMENT \
  --image "$ACR_NAME.azurecr.io/api-pedidos:v1.2.0" \
  --registry-server "$ACR_NAME.azurecr.io" \
  --registry-identity "$IDENTITY_ID" \
  --user-assigned "$IDENTITY_ID" \
  --target-port 8080 \
  --ingress external \
  --min-replicas 1 \
  --max-replicas 20 \
  --cpu 1.0 \
  --memory 2.0Gi

```

> **Nota**: system-assigned identity também funciona, mas exige um fluxo em duas etapas (criar app → atribuir role → atualizar imagem). User-assigned é o caminho mais limpo para produção.

### Configurando secrets e variáveis de ambiente

```bash
# Criar secrets (valores sensíveis)
az containerapp secret set \
  --name api-pedidos \
  --resource-group $RESOURCE_GROUP \
  --secrets "db-connection=Server=tcp:sql-prod.database.windows.net;Database=pedidos;Authentication=Active Directory Managed Identity;" \
            "storage-key=<sua-key>"

# Referenciar secrets como variáveis de ambiente
az containerapp update \
  --name api-pedidos \
  --resource-group $RESOURCE_GROUP \
  --set-env-vars "DATABASE_URL=secretref:db-connection" \
                 "STORAGE_KEY=secretref:storage-key" \
                 "APP_VERSION=v1.2.0"

```

### Infrastructure as Code com Bicep

Para produção, defina tudo via IaC. Aqui um template Bicep completo:

```bicep
@description('Nome do environment')
param environmentName string = 'env-producao'

@description('Localização')
param location string = resourceGroup().location

@description('Nome do Log Analytics workspace')
param logAnalyticsName string = 'law-containerapps'

// Log Analytics Workspace
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
  name: logAnalyticsName
  location: location
  properties: {
    sku: {
      name: 'PerGB2018'
    }
    retentionInDays: 30
  }
}

// Container Apps Environment
resource environment 'Microsoft.App/managedEnvironments@2024-03-01' = {
  name: environmentName
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalytics.properties.customerId
        sharedKey: logAnalytics.listKeys().primarySharedKey
      }
    }
    zoneRedundant: true
  }
}

// Container App - API de Pedidos
resource apiPedidos 'Microsoft.App/containerApps@2024-03-01' = {
  name: 'api-pedidos'
  location: location
  properties: {
    environmentId: environment.id
    configuration: {
      ingress: {
        external: true
        targetPort: 8080
        transport: 'http'
        traffic: [
          {
            latestRevision: true
            weight: 100
          }
        ]
      }
      secrets: [
        {
          name: 'db-connection'
          value: 'Server=tcp:sql-prod.database.windows.net;...'
        }
      ]
    }
    template: {
      containers: [
        {
          name: 'api'
          image: 'acrprod.azurecr.io/api-pedidos:v1.2.0'
          resources: {
            cpu: json('1.0')
            memory: '2Gi'
          }
          env: [
            {
              name: 'DATABASE_URL'
              secretRef: 'db-connection'
            }
            {
              name: 'APP_ENV'
              value: 'production'
            }
          ]
          probes: [
            {
              type: 'Startup'
              httpGet: {
                path: '/healthz'
                port: 8080
              }
              failureThreshold: 30
              periodSeconds: 2
            }
            {
              type: 'Liveness'
              httpGet: {
                path: '/healthz'
                port: 8080
              }
              periodSeconds: 10
            }
            {
              type: 'Readiness'
              httpGet: {
                path: '/ready'
                port: 8080
              }
              initialDelaySeconds: 5
            }
          ]
        }
      ]
      scale: {
        minReplicas: 1
        maxReplicas: 20
        rules: [
          {
            name: 'http-scaling'
            http: {
              metadata: {
                concurrentRequests: '50'
              }
            }
          }
        ]
      }
    }
  }
}

output apiUrl string = 'https://${apiPedidos.properties.configuration.ingress.fqdn}'

```

## Escala automática: HTTP, KEDA e eventos

A escala automática é o coração do Container Apps. A plataforma usa **KEDA (Kubernetes Event Driven Autoscaling)** internamente, mas você não precisa instalar nem configurar nada — basta declarar as regras.

### Escala por HTTP (padrão)

Para apps com ingress HTTP, a escala é baseada em requisições concorrentes:

```bash
az containerapp update \
  --name api-pedidos \
  --resource-group $RESOURCE_GROUP \
  --min-replicas 1 \
  --max-replicas 30 \
  --scale-rule-name http-rule \
  --scale-rule-type http \
  --scale-rule-http-concurrency 50

```

Isso significa: cada réplica aguenta 50 requisições concorrentes. Se houver 200 requisições simultâneas, o ACA escala para 4 réplicas. Se o tráfego cair para zero e `min-replicas` for 0, escala até zero réplicas.

### Escala por Azure Service Bus Queue

Este é o cenário mais poderoso para arquiteturas event-driven:

```bash
az containerapp create \
  --name worker-processamento \
  --resource-group $RESOURCE_GROUP \
  --environment $ENVIRONMENT \
  --image acrprod.azurecr.io/worker:v1.0.0 \
  --min-replicas 0 \
  --max-replicas 50 \
  --scale-rule-name queue-rule \
  --scale-rule-type azure-servicebus \
  --scale-rule-metadata \
    "queueName=pedidos-processar" \
    "namespace=sb-prod-pedidos" \
    "messageCount=5" \
  --scale-rule-auth \
    "connection=sb-connection-string" \
  --secrets "sb-connection-string=Endpoint=sb://sb-prod-pedidos.servicebus.windows.net/;SharedAccessKeyName=worker;SharedAccessKey=..."

```

Com `messageCount=5`, cada 5 mensagens na fila dispara uma nova réplica. Se a fila está vazia, o worker escala a **zero** — custo zero quando não há trabalho.

### Escala por Azure Storage Queue

```bash
az containerapp update \
  --name worker-imagens \
  --resource-group $RESOURCE_GROUP \
  --scale-rule-name queue-scale \
  --scale-rule-type azure-queue \
  --scale-rule-metadata \
    "queueName=imagens-processar" \
    "queueLength=10" \
    "accountName=stprodimagens" \
  --scale-rule-auth \
    "connection=storage-connection" \
  --secrets "storage-connection=DefaultEndpointsProtocol=https;AccountName=stprodimagens;..."

```

### Tabela de scalers KEDA disponíveis

| Scaler                  | Trigger                  | Caso de uso                |
| ----------------------- | ------------------------ | -------------------------- |
| **HTTP**                | Requisições concorrentes | APIs, web apps             |
| **Azure Service Bus**   | Mensagens na fila/tópico | Event-driven processing    |
| **Azure Storage Queue** | Mensagens na fila        | Processamento assíncrono   |
| **Azure Event Hubs**    | Eventos no hub           | Streaming, IoT             |
| **Azure Cosmos DB**     | Change feed              | Reação a mudanças de dados |
| **PostgreSQL / MySQL**  | Queries customizadas     | Batch baseado em dados     |
| **Cron**                | Horário agendado         | Scale preventivo           |
| **Custom**              | Métricas customizadas    | Qualquer fonte via KEDA    |

## Integração com Dapr: microserviços simplificados

O **Dapr (Distributed Application Runtime)** é integrado nativamente ao Container Apps. Isso significa que você ganha service-to-service invocation, pub/sub, state management e bindings **sem instalar nada** — basta habilitar o sidecar.

### Habilitando Dapr

```bash
az containerapp dapr enable \
  --name api-pedidos \
  --resource-group $RESOURCE_GROUP \
  --dapr-app-id api-pedidos \
  --dapr-app-port 8080 \
  --dapr-app-protocol http

```

### Service Invocation (chamadas entre serviços)

Com Dapr, um serviço chama outro usando o app-id, sem precisar saber o endereço ou gerenciar service discovery:

```python
# api-pedidos chamando o serviço "servico-estoque"
import requests

def verificar_estoque(produto_id: str) -> dict:
    # Dapr resolve o endereço automaticamente via sidecar
    dapr_url = f"http://localhost:3500/v1.0/invoke/servico-estoque/method/estoque/{produto_id}"
    response = requests.get(dapr_url)
    return response.json()

```

```csharp
// Em C# usando o SDK do Dapr
using Dapr.Client;

var client = new DaprClientBuilder().Build();
var estoque = await client.InvokeMethodAsync<EstoqueResponse>(
    HttpMethod.Get,
    "servico-estoque",
    $"estoque/{produtoId}");

```

### Pub/Sub (publicação e assinatura de eventos)

Configure um componente de pub/sub usando Azure Service Bus:

```yaml
# pubsub-servicebus.yaml (aplicado via CLI ou Bicep)
componentType: pubsub.azure.servicebus.topics
version: v1
metadata:
  - name: connectionString
    secretRef: sb-connection
  - name: maxConcurrentHandlers
    value: "10"
scopes:
  - api-pedidos
  - worker-notificacoes

```

**Publicando eventos:**

```python
import requests
import json

def publicar_pedido_criado(pedido: dict):
    dapr_url = "http://localhost:3500/v1.0/publish/pubsub-servicebus/pedido-criado"
    requests.post(dapr_url, json=pedido)

```

**Assinando eventos:**

```python
from flask import Flask, request

app = Flask(__name__)

# Dapr chama essa rota quando chega uma mensagem no tópico
@app.route('/pedido-criado', methods=['POST'])
def processar_pedido():
    evento = request.json
    pedido = evento['data']
    # Processar pedido...
    enviar_email_confirmacao(pedido)
    return '', 200

# Declarar as subscriptions para o Dapr
@app.route('/dapr/subscribe', methods=['GET'])
def subscribe():
    return [
        {
            'pubsubname': 'pubsub-servicebus',
            'topic': 'pedido-criado',
            'route': '/pedido-criado'
        }
    ], 200

```

### State Management

Dapr oferece state store plugável. Com Container Apps, você pode usar Azure Cosmos DB, Azure Table Storage ou Redis:

```python
import requests

DAPR_URL = "http://localhost:3500/v1.0/state/statestore"

# Salvar estado
def salvar_carrinho(usuario_id: str, itens: list):
    state = [{"key": f"carrinho-{usuario_id}", "value": {"itens": itens}}]
    requests.post(DAPR_URL, json=state)

# Ler estado
def obter_carrinho(usuario_id: str) -> dict:
    resp = requests.get(f"{DAPR_URL}/carrinho-{usuario_id}")
    return resp.json()

```

## Conclusão da Parte 1

Neste artigo, exploramos os fundamentos do Azure Container Apps — desde o posicionamento no ecossistema Azure até a implementação prática com CLI e Bicep, passando pela escala automática com KEDA e a integração nativa com Dapr para comunicação entre microserviços, pub/sub e state management. Esses pilares formam a base para rodar containers em produção com custo otimizado e complexidade operacional mínima.

Na Parte 2, exploraremos Container Apps Jobs, networking avançado com VNet, observabilidade com KQL e OpenTelemetry, blue-green deployments e troubleshooting.