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.
Exam Tip
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.
Exam Tip
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 Exam Tip
--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
Common Gotcha
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
Exam Tip
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.
Exam Tip
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 Exam Tip
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 Common Gotcha
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 Exam Tip
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}
) Exam Tip
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.