Deploy Containers to Azure Container Apps
Introduction
2 minAzure Container Apps (ACA) is a serverless container platform built on Kubernetes β but you never touch kubectl. It handles scaling (including scale-to-zero), ingress, revision-based deployments, and built-in observability. Perfect for AI microservices: deploy a Python FastAPI embeddings service with zero Kubernetes expertise.
Container Apps Environments
5 min1. What an Environment Provides
- Shared VNet boundary β apps in the same environment share a virtual network and can reach each other via internal DNS.
- Shared Log Analytics workspace β console and system logs from all apps in one environment flow to one workspace.
- Isolation boundary β apps in different environments are fully isolated (separate VNet, separate logs).
Real-life: group AI API + background worker in one environment. Put dev and prod in separate environments.
2. Ingress: External vs Internal
- External ingress (
--ingress external) β public FQDN, accessible from the internet. Use for user-facing AI APIs. - Internal ingress (
--ingress internal) β only reachable within the environment viaappname.internal.domainname. Use for backend microservices.
az containerapp env create --name aca-env-prod --resource-group rg-ai --location eastus
az containerapp env show --name aca-env-prod --resource-group rg-ai Deploy a Container App
8 min1. Fast Deploy: az containerapp up
Creates environment and app in one command. Use for prototypes or first deployments.
az containerapp up --name ai-api --resource-group rg-ai \\
--environment aca-env-prod \\
--image mcr.microsoft.com/k8se/quickstart:latest \\
--target-port 80 --ingress external \\
--query properties.configuration.ingress.fqdn 2. Explicit Deploy: az containerapp create + update
az containerapp create --name ai-api --resource-group rg-ai \\
--environment aca-env-prod \\
--image myregistry.azurecr.io/ai-api:v1 \\
--ingress external --target-port 8000
az containerapp update --name ai-api --resource-group rg-ai \\
--image myregistry.azurecr.io/ai-api:v2 Each update with a new image creates a new revision β your safety net for rollbacks.
3. YAML-Based Deployment (Configuration as Code)
az containerapp create -n ai-api -g rg-ai --environment aca-env-prod --yaml ./app.yml
az containerapp update -n ai-api -g rg-ai --yaml ./app.yml --yaml ignores ALL other CLI flags. YAML becomes the single source of truth. Best answer for "repeatable, reviewable, git-tracked deployments". Configure Runtime: Env Vars and Secrets
7 min1. Non-Sensitive Environment Variables
az containerapp create -n ai-api -g rg-ai \\
--env-vars LOG_LEVEL=info FEATURE_EMBEDDINGS=true ...
az containerapp update -n ai-api -g rg-ai --set-env-vars LOG_LEVEL=debug --set-env-vars adds/updates without removing existing vars. Use --replace-env-vars to replace all.
2. Secrets + secretref: Pattern (Exam-Critical)
- Store as a Container Apps secret:
az containerapp secret set -n ai-api -g rg-ai \\ --secrets embeddings-api-key="sk-abc123" - Reference the secret as an env var using
secretref::az containerapp update -n ai-api -g rg-ai \\ --set-env-vars EMBEDDINGS_API_KEY=secretref:embeddings-api-key - In YAML:
env: - name: EMBEDDINGS_API_KEY secretRef: embeddings-api-key
secretref: maps a secret to an env var β the actual value is never in YAML, CLI history, or logs. This is THE secure pattern. The exam tests this explicitly. Configure Private Registry Authentication
6 min1. Managed Identity for ACR (Recommended)
az containerapp registry set -n ai-api -g rg-ai \\
--server myregistry.azurecr.io --identity system Assign managed identity + AcrPull role on the registry. Zero stored credentials.
2. Username/Password (Fallback)
az containerapp registry set -n ai-api -g rg-ai \\
--server myregistry.azurecr.io --username MyUser --password MyPassword 3. Verify Registry Config
az containerapp registry list -n ai-api -g rg-ai
az containerapp registry show -n ai-api -g rg-ai --server myregistry.azurecr.io Verify Deployments: Logs, Revisions, Replicas
7 minKEDA Event-Driven Scaling: source triggers β scale 0βN replicas
Diagnosis Order (Exam Critical)
- Container logs β app stdout/stderr
az containerapp logs show -n ai-api -g rg-ai --follow --tail 50 az containerapp logs show -n ai-api -g rg-ai --type system - Revision status β check health of each version
az containerapp revision list -n ai-api -g rg-ai -o table - Replica count β check if instances are running
az containerapp replica list -n ai-api -g rg-ai --revision myapp--xyz123
β‘ Container Apps Master Cheatsheet
az containerapp up--yaml ./app.yml (ignores all other flags)secretref:secret-name in env varshttp://appname.internal.domainnameaz containerapp logs show --followaz containerapp revision listExercise β Deploy Backend API
30 min- Create environment and deploy from ACR using managed identity
- Configure secrets as env var references using
secretref: - Update image (creates new revision) and verify with
revision list - Trigger logs and check replica status
- Deactivate a bad revision and confirm traffic routes to healthy one
Module Assessment
5 min- Q: Shared networking + logging for multiple apps? A: A Container Apps Environment
- Q: AI API key must not appear in YAML. How to inject? A: Store as Container Apps secret, reference with
secretref: - Q: App fails after image update. First diagnostic step? A:
az containerapp logs show - Q: Best approach for git-tracked, repeatable deployments? A: YAML deployment with
--yamlflag
Summary
2 minEnvironments scope networking and logging. Deploy with up (fast), create (precise), or --yaml (repeatable). Use secretref: for secure secret injection. Use managed identity + AcrPull for registry auth. Diagnose: logs β revision β replicas.
π§ Memory Tricks
Revision = immutable snapshot of your app config. Each update creates one. Use them for rollbacks and canary deployments.
secretref: = zero exposure. The value never appears in logs, YAML, or shell history. This is THE exam answer for "secure secret injection".
Azure Container Apps
π Key Facts
- secretref: β Maps Container Apps secret to env var β THE secure injection pattern
- --yaml flag β Config-as-code β IGNORES all other CLI flags
- Revision β Immutable snapshot on each update β rollback + canary traffic split
- External ingress β Public FQDN β internet accessible
- Internal ingress β Same-environment only via internal DNS
- Scale to zero β minReplicas=0 + KEDA trigger (HTTP, queue, cron)
- Registry auth (best) β Managed Identity + AcrPull role
- Diagnose order β logs β revision list β replica list
π» Commands & Patterns
az containerapp up --name ai-api -g rg --environment myenv --image myacr.azurecr.io/api:v1 --target-port 8000 --ingress external az containerapp secret set -n ai-api -g rg --secrets openai-key="sk-abc123" az containerapp update -n ai-api -g rg --set-env-vars OPENAI_KEY=secretref:openai-key az containerapp logs show -n ai-api -g rg --follow az containerapp revision list -n ai-api -g rg -o table
Scale Container Apps with KEDA and Manage Revisions
KEDA Event-Driven Autoscaling
3 minKEDA (Kubernetes Event-Driven Autoscaling) is built into Container Apps. It scales replicas based on external event sources β Service Bus queue depth, HTTP requests, custom metrics β including scaling to zero when idle. Perfect for bursty AI workloads.
KEDA Scale Rules
9 minService Bus Queue Depth Scaling
az containerapp update \
--name ai-worker \
--resource-group rg \
--min-replicas 0 \
--max-replicas 10 \
--scale-rule-name sb-queue-rule \
--scale-rule-type azure-servicebus \
--scale-rule-metadata queueName=embed-queue \
messageCount=5 \
namespace=mynamespace \
--scale-rule-auth triggerAuth=sb-auth-ref messageCount=5 means one new replica per 5 messages. With 50 messages β 10 replicas. min-replicas=0 = scale to zero when queue empty (cost savings). HTTP Concurrent Request Scaling
az containerapp update \
--name ai-api \
--resource-group rg \
--min-replicas 1 \
--max-replicas 20 \
--scale-rule-name http-rule \
--scale-rule-type http \
--scale-rule-metadata concurrentRequests=10 Revisions and Traffic Splitting
8 minCanary Deployment with Traffic Weights
# Enable multiple revision mode
az containerapp revision set-mode \
--name ai-api --resource-group rg \
--mode multiple
# Deploy new version (creates new revision)
az containerapp update \
--name ai-api --resource-group rg \
--image myregistry.azurecr.io/ai-api:v2
# Split traffic: 90% stable, 10% canary
az containerapp ingress traffic set \
--name ai-api --resource-group rg \
--revision-weight \
ai-api--v1=90 \
ai-api--v2=10 Dapr Sidecar Integration
6 minEnable Dapr for Service-to-Service Calls
# Enable Dapr on container app
az containerapp update \
--name ai-api --resource-group rg \
--dapr-enabled true \
--dapr-app-id ai-api \
--dapr-app-port 8000
# Call another service via Dapr sidecar (no service discovery needed)
import httpx
# Dapr handles retries, tracing, mTLS automatically
response = httpx.post(
"http://localhost:3500/v1.0/invoke/embedding-svc/method/embed",
json={"text": content}
) localhost:3500 (Dapr port) β Dapr routes to the target service by app-id. No hardcoded URLs. Summary
2 minKEDA in Container Apps: scale to zero on queue triggers, HTTP scaling on concurrent requests, custom metrics. Revisions: immutable snapshots, single (blue/green) vs multiple (canary with traffic weights). Traffic splitting via --revision-weight. Dapr sidecar: service-to-service calls via localhost:3500, handles retries, mTLS, tracing automatically. Min-replicas=0 only works with non-HTTP triggers.