Azure
intermediate
35-45 minutes
Azure Container Apps
Deploy containerized applications to Azure Container Apps - serverless containers with automatic scaling.
What you'll accomplish
Your containerized application running on Azure Container Apps
Prerequisites
- Azure account with subscription
- Azure CLI installed
- Docker installed locally
- Container image or Dockerfile
What is Azure Container Apps?
Azure Container Apps is a serverless container platform:
- Serverless - No infrastructure management
- Scale to zero - Pay only when running
- Built on Kubernetes - KEDA and Dapr integration
- Microservices-ready - Service discovery, traffic splitting
- Event-driven - Scale on events, queues, HTTP
Container Image → Container Apps Environment → Your App
│
├── Auto-scaling (0 to N)
├── Load balancing
├── Ingress (HTTP/TCP)
└── Service discovery
Container Apps vs Other Options
| Feature | Container Apps | App Service | AKS |
|---|---|---|---|
| Containers | Required | Optional | Required |
| Management | Serverless | Managed | Self-managed K8s |
| Scale to zero | Yes | No | Yes (with KEDA) |
| Kubernetes | Hidden | N/A | Full access |
| Complexity | Low | Low | High |
Note
Choose Container Apps for microservices, event-driven apps, and when you want Kubernetes benefits without the complexity.
Step 1: Enable Container Apps Extension
bash# Add Container Apps extension az extension add --name containerapp --upgrade # Register required providers az provider register --namespace Microsoft.App az provider register --namespace Microsoft.OperationalInsights
Step 2: Create Container Apps Environment
An environment is a secure boundary for container apps:
bash# Create resource group az group create --name myapp-rg --location eastus # Create Container Apps environment az containerapp env create \ --name myapp-env \ --resource-group myapp-rg \ --location eastus
Environment Features
- Shared resources - Log Analytics, virtual network
- Isolation - Apps in same environment can communicate
- Dapr - Built-in sidecar support
- Monitoring - Automatic log collection
Step 3: Deploy from Container Registry
Option A: Use Azure Container Registry
bash# Create container registry az acr create \ --name myappregistry \ --resource-group myapp-rg \ --sku Basic \ --admin-enabled true # Build and push image az acr build \ --registry myappregistry \ --image myapp:latest \ --file Dockerfile . # Get registry credentials ACR_PASSWORD=$(az acr credential show \ --name myappregistry \ --query "passwords[0].value" \ --output tsv) # Deploy container app az containerapp create \ --name myapp \ --resource-group myapp-rg \ --environment myapp-env \ --image myappregistry.azurecr.io/myapp:latest \ --registry-server myappregistry.azurecr.io \ --registry-username myappregistry \ --registry-password $ACR_PASSWORD \ --target-port 3000 \ --ingress external \ --min-replicas 0 \ --max-replicas 10
Option B: Use Docker Hub
bashaz containerapp create \ --name myapp \ --resource-group myapp-rg \ --environment myapp-env \ --image docker.io/username/myapp:latest \ --target-port 3000 \ --ingress external
Option C: Deploy from Source Code
bash# Deploy directly from source (builds in cloud) az containerapp up \ --name myapp \ --resource-group myapp-rg \ --environment myapp-env \ --source . \ --ingress external \ --target-port 3000
Step 4: Configure the Application
Get App URL
bashaz containerapp show \ --name myapp \ --resource-group myapp-rg \ --query properties.configuration.ingress.fqdn \ --output tsv
Environment Variables
bash# Set environment variables az containerapp update \ --name myapp \ --resource-group myapp-rg \ --set-env-vars "NODE_ENV=production" "API_URL=https://api.example.com" # View environment variables az containerapp show \ --name myapp \ --resource-group myapp-rg \ --query properties.template.containers[0].env
Secrets
bash# Create secrets az containerapp secret set \ --name myapp \ --resource-group myapp-rg \ --secrets "db-password=supersecret" "api-key=myapikey" # Use secrets as env vars az containerapp update \ --name myapp \ --resource-group myapp-rg \ --set-env-vars "DB_PASSWORD=secretref:db-password"
Resource Limits
bashaz containerapp update \ --name myapp \ --resource-group myapp-rg \ --cpu 0.5 \ --memory 1.0Gi
| CPU | Memory Options |
|---|---|
| 0.25 | 0.5Gi |
| 0.5 | 1.0Gi |
| 0.75 | 1.5Gi |
| 1.0 | 2.0Gi |
| 1.25 | 2.5Gi |
| 1.5 | 3.0Gi |
| 1.75 | 3.5Gi |
| 2.0 | 4.0Gi |
Step 5: Scaling Configuration
HTTP Scaling
bashaz containerapp update \ --name myapp \ --resource-group myapp-rg \ --min-replicas 0 \ --max-replicas 10 \ --scale-rule-name http-rule \ --scale-rule-type http \ --scale-rule-http-concurrency 100
Custom Scale Rules
yaml# scale-rules.yaml properties: template: scale: minReplicas: 1 maxReplicas: 30 rules: - name: queue-based-scaling azureQueue: queueName: myqueue queueLength: 10 auth: - secretRef: connection-string triggerParameter: connection
bashaz containerapp update \ --name myapp \ --resource-group myapp-rg \ --yaml scale-rules.yaml
Step 6: Ingress Configuration
External Ingress (Public)
bashaz containerapp ingress enable \ --name myapp \ --resource-group myapp-rg \ --type external \ --target-port 3000 \ --transport auto
Internal Ingress (Private)
bashaz containerapp ingress enable \ --name myapp \ --resource-group myapp-rg \ --type internal \ --target-port 3000
Traffic Splitting
bash# Deploy new revision az containerapp update \ --name myapp \ --resource-group myapp-rg \ --image myappregistry.azurecr.io/myapp:v2 # Split traffic 80/20 az containerapp ingress traffic set \ --name myapp \ --resource-group myapp-rg \ --revision-weight myapp--revision1=80 myapp--revision2=20
Step 7: Revisions
Every deployment creates a new revision:
bash# List revisions az containerapp revision list \ --name myapp \ --resource-group myapp-rg \ --output table # Activate/deactivate revision az containerapp revision activate \ --name myapp \ --resource-group myapp-rg \ --revision myapp--revision1 # Set revision mode (single or multiple active) az containerapp revision set-mode \ --name myapp \ --resource-group myapp-rg \ --mode multiple
Step 8: Custom Domains
bash# Add custom domain az containerapp hostname add \ --name myapp \ --resource-group myapp-rg \ --hostname www.example.com # Bind managed certificate az containerapp hostname bind \ --name myapp \ --resource-group myapp-rg \ --hostname www.example.com \ --environment myapp-env \ --validation-method CNAME
Configure DNS at your provider:
- CNAME: www → myapp.azurecontainerapps.io
- TXT: asuid.www → (validation token)
Step 9: Logging and Monitoring
View Logs
bash# Stream logs az containerapp logs show \ --name myapp \ --resource-group myapp-rg \ --follow # View system logs az containerapp logs show \ --name myapp \ --resource-group myapp-rg \ --type system
Query Logs (Log Analytics)
bash# Get Log Analytics workspace WORKSPACE_ID=$(az containerapp env show \ --name myapp-env \ --resource-group myapp-rg \ --query properties.appLogsConfiguration.logAnalyticsConfiguration.customerId \ --output tsv) # Query logs in Azure Portal: # ContainerAppConsoleLogs_CL # | where ContainerAppName_s == "myapp" # | project TimeGenerated, Log_s
Multi-Container Apps
Sidecar Containers
yaml# app.yaml properties: template: containers: - name: main-app image: myappregistry.azurecr.io/myapp:latest resources: cpu: 0.5 memory: 1.0Gi env: - name: PORT value: "3000" - name: logging-sidecar image: fluent/fluentd:latest resources: cpu: 0.25 memory: 0.5Gi
bashaz containerapp update \ --name myapp \ --resource-group myapp-rg \ --yaml app.yaml
Dapr Integration
Enable Dapr for microservices patterns:
bash# Enable Dapr az containerapp dapr enable \ --name myapp \ --resource-group myapp-rg \ --dapr-app-id myapp \ --dapr-app-port 3000 \ --dapr-app-protocol http
Service Invocation
javascript// Call another service via Dapr const response = await fetch('http://localhost:3500/v1.0/invoke/other-service/method/api/data');
State Store
bash# Create state store component az containerapp env dapr-component set \ --name myapp-env \ --resource-group myapp-rg \ --dapr-component-name statestore \ --yaml state-store.yaml
Jobs (Background Tasks)
For scheduled or event-triggered tasks:
bash# Create scheduled job az containerapp job create \ --name myjob \ --resource-group myapp-rg \ --environment myapp-env \ --image myappregistry.azurecr.io/myjob:latest \ --trigger-type Schedule \ --cron-expression "0 0 * * *" \ --replica-timeout 1800 # Create manual job az containerapp job create \ --name myjob \ --resource-group myapp-rg \ --environment myapp-env \ --image myappregistry.azurecr.io/myjob:latest \ --trigger-type Manual # Start job execution az containerapp job start \ --name myjob \ --resource-group myapp-rg
VNet Integration
Create Environment with VNet
bash# Create VNet az network vnet create \ --name myapp-vnet \ --resource-group myapp-rg \ --address-prefix 10.0.0.0/16 \ --subnet-name infrastructure-subnet \ --subnet-prefix 10.0.0.0/23 # Create environment in VNet az containerapp env create \ --name myapp-env \ --resource-group myapp-rg \ --location eastus \ --infrastructure-subnet-resource-id /subscriptions/.../subnets/infrastructure-subnet \ --internal-only false
Complete Deployment Example
bash#!/bin/bash RESOURCE_GROUP="myapp-rg" LOCATION="eastus" ENV_NAME="myapp-env" APP_NAME="myapp" REGISTRY_NAME="myappregistry" # Create resource group az group create --name $RESOURCE_GROUP --location $LOCATION # Create container registry az acr create \ --name $REGISTRY_NAME \ --resource-group $RESOURCE_GROUP \ --sku Basic \ --admin-enabled true # Build and push image az acr build \ --registry $REGISTRY_NAME \ --image $APP_NAME:latest \ --file Dockerfile . # Create Container Apps environment az containerapp env create \ --name $ENV_NAME \ --resource-group $RESOURCE_GROUP \ --location $LOCATION # Get registry password ACR_PASSWORD=$(az acr credential show \ --name $REGISTRY_NAME \ --query "passwords[0].value" \ --output tsv) # Create container app az containerapp create \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $ENV_NAME \ --image $REGISTRY_NAME.azurecr.io/$APP_NAME:latest \ --registry-server $REGISTRY_NAME.azurecr.io \ --registry-username $REGISTRY_NAME \ --registry-password $ACR_PASSWORD \ --target-port 3000 \ --ingress external \ --min-replicas 0 \ --max-replicas 10 \ --cpu 0.5 \ --memory 1.0Gi \ --env-vars "NODE_ENV=production" # Get app URL echo "App URL: https://$(az containerapp show \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --query properties.configuration.ingress.fqdn \ --output tsv)"
Troubleshooting
Container Won't Start
bash# Check logs az containerapp logs show \ --name myapp \ --resource-group myapp-rg \ --type system # Common issues: # - Wrong port (must match --target-port) # - Missing environment variables # - Image not accessible
Scaling Issues
bash# Check replica count az containerapp revision show \ --name myapp \ --resource-group myapp-rg \ --revision REVISION_NAME \ --query properties.replicas # Check scale rules az containerapp show \ --name myapp \ --resource-group myapp-rg \ --query properties.template.scale
Connection Errors
bash# Check ingress configuration az containerapp ingress show \ --name myapp \ --resource-group myapp-rg # Verify port matches application # Ensure --target-port matches what your app listens on
Note
Checkpoint: Deploy a container to Azure Container Apps, configure scaling, and verify it's accessible via the generated URL.