Azure Container Apps: A Solutions Architect’s Guide to Serverless Containers

Azure Container Apps represents Microsoft’s serverless container platform, offering Kubernetes-like capabilities without cluster management complexity, powered by KEDA auto-scaling and native Dapr integration.

Container Apps Architecture

Container Apps Architecture

Platform Comparison

Platform Comparison

Key Features

FeatureDescriptionUse Case
RevisionsImmutable snapshots of app versionBlue-green, canary deployments
Traffic SplittingRoute % traffic to different revisionsA/B testing, gradual rollouts
KEDA ScalingEvent-driven auto-scale (HTTP, Queue)Variable workloads, cost optimization
Dapr IntegrationService mesh, state, pub/subMicroservices communication
Scale to Zero0 replicas when idleBatch jobs, dev environments
Managed IdentityAzure AD authenticationSecure access to Azure services

Deployment with Azure CLI

# Deploy Container App with Azure CLI
az containerapp create   --name my-api   --resource-group my-rg   --environment my-env   --image myregistry.azurecr.io/api:v1   --target-port 8080   --ingress external   --min-replicas 0   --max-replicas 10   --cpu 0.5   --memory 1.0Gi   --env-vars "DB_HOST=secretref:db-host"   --secrets "db-host=mydb.postgres.database.azure.com"   --registry-server myregistry.azurecr.io   --registry-identity system

# KEDA HTTP scaling rule
az containerapp update   --name my-api   --resource-group my-rg   --scale-rule-name http-rule   --scale-rule-type http   --scale-rule-http-concurrency 100

# KEDA Azure Queue scaling
az containerapp update   --name my-worker   --resource-group my-rg   --scale-rule-name queue-rule   --scale-rule-type azure-queue   --scale-rule-metadata "queueName=jobs" "queueLength=5" "connectionFromEnv=STORAGE_CONNECTION"

Bicep Infrastructure

# Bicep: Container Apps Environment + App
param location string = resourceGroup().location
param envName string = 'my-env'
param appName string = 'my-api'

resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
  name: '${envName}-logs'
  location: location
  properties: {
    sku: {
      name: 'PerGB2018'
    }
  }
}

resource environment 'Microsoft.App/managedEnvironments@2023-05-01' = {
  name: envName
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalytics.properties.customerId
        sharedKey: logAnalytics.listKeys().primarySharedKey
      }
    }
    daprAIInstrumentationKey: null
    zoneRedundant: false
  }
}

resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {
  name: appName
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    environmentId: environment.id
    configuration: {
      ingress: {
        external: true
        targetPort: 8080
        transport: 'auto'
        traffic: [
          {
            latestRevision: true
            weight: 100
          }
        ]
      }
      dapr: {
        enabled: true
        appId: appName
        appPort: 8080
      }
      secrets: [
        {
          name: 'db-password'
          value: 'YourSecurePassword'
        }
      ]
    }
    template: {
      containers: [
        {
          name: 'api'
          image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
          resources: {
            cpu: '0.5'
            memory: '1.0Gi'
          }
          env: [
            {
              name: 'DB_PASSWORD'
              secretRef: 'db-password'
            }
          ]
        }
      ]
      scale: {
        minReplicas: 0
        maxReplicas: 10
        rules: [
          {
            name: 'http-rule'
            http: {
              metadata: {
                concurrentRequests: '100'
              }
            }
          }
        ]
      }
    }
  }
}

output fqdn string = containerApp.properties.configuration.ingress.fqdn

Traffic Splitting & Canary

# Traffic splitting: Blue-Green deployment
az containerapp revision copy   --name my-api   --resource-group my-rg   --image myregistry.azurecr.io/api:v2

# Split traffic: 80% old, 20% new (canary)
az containerapp ingress traffic set   --name my-api   --resource-group my-rg   --revision-weight my-api--v1=80 my-api--v2=20

# After validation, shift 100% to new
az containerapp ingress traffic set   --name my-api   --resource-group my-rg   --revision-weight my-api--v2=100

# List all revisions
az containerapp revision list   --name my-api   --resource-group my-rg   --output table

Dapr Configuration

# Enable Dapr state management
az containerapp dapr enable   --name my-api   --resource-group my-rg   --dapr-app-id my-api   --dapr-app-port 8080

# Create Dapr component (Redis state store)
az containerapp env dapr-component set   --name my-env   --resource-group my-rg   --dapr-component-name statestore   --yaml "
componentType: state.redis
version: v1
metadata:
- name: redisHost
  value: myredis.redis.cache.windows.net:6380
- name: redisPassword
  secretRef: redis-password
- name: enableTLS
  value: true
scopes:
- my-api
"

KEDA Scaling Triggers

TriggerMetricExample
HTTPConcurrent requestsScale at 100 requests/replica
Azure QueueQueue lengthScale at 5 messages/replica
Azure Service BusQueue/topic lengthScale based on pending messages
Azure Event HubLag in consumer groupScale to process backlog
CPUCPU percentageScale at 70% CPU utilization
MemoryMemory percentageScale at 80% memory usage
CustomAny metricPrometheus, AppInsights metrics

Best Practices

  • Use managed identities: Avoid storing secrets for Azure services
  • Enable Dapr: Simplify microservices communication patterns
  • Configure health probes: Startup, liveness, readiness checks
  • Leverage traffic splitting: Safe deployments with canary releases
  • Scale to zero: Optimize costs for variable workloads
  • VNet integration: Secure communication with private endpoints
  • Use Log Analytics: Centralized logging and monitoring
  • Set resource limits: Prevent cost overruns with max replicas
  • Multiple scaling rules: Combine HTTP + queue triggers
  • Revision labels: Tag revisions for easier traffic management

Pricing Breakdown

  • vCPU: $0.000012 per vCPU-second
  • Memory: $0.0000013 per GB-second
  • HTTP requests: First 2M free, then $0.40 per million
  • Free tier: 180,000 vCPU-seconds + 360,000 GB-seconds/month
  • Example (0.5 vCPU, 1GB, 24/7): ~$15-20/month
  • Example (scaled to zero 80% of time): ~$3-4/month

References


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.