Azure Container Services Comparison: Container Apps vs Container Instances vs AKS
Part 2: Deep Dive Implementation & Code Examples
In Part 1, we covered the conceptual differences between Azure’s container services. Now let’s get our hands dirty with real implementations, code examples, and practical scenarios that will help you understand exactly how each service works in practice.
Azure Container Instances: Quick and Simple Deployments
Let’s start with the simplest scenario – deploying a web API using ACI.
Example 1: Simple Web API with ACI
# ARM Template for ACI deployment
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"containerName": {
"type": "string",
"defaultValue": "my-web-api"
}
},
"resources": [
{
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2021-07-01",
"name": "[parameters('containerName')]",
"location": "[resourceGroup().location]",
"properties": {
"containers": [
{
"name": "web-api",
"properties": {
"image": "nginx:latest",
"ports": [
{
"port": 80,
"protocol": "TCP"
}
],
"resources": {
"requests": {
"cpu": 0.5,
"memoryInGB": 1.0
}
}
}
}
],
"osType": "Linux",
"ipAddress": {
"type": "Public",
"ports": [
{
"port": 80,
"protocol": "TCP"
}
]
}
}
}
]
}
Deployment time: ~30 seconds
Configuration complexity: Minimal
Best for: Simple stateless applications, development environments
Example 2: Batch Processing Job with ACI
# Azure CLI command for batch job
az container create \
--resource-group myResourceGroup \
--name batch-processor \
--image myregistry.azurecr.io/data-processor:latest \
--restart-policy Never \
--environment-variables \
STORAGE_ACCOUNT=mystorageaccount \
QUEUE_NAME=processing-queue \
--cpu 2 \
--memory 4 \
--registry-login-server myregistry.azurecr.io \
--registry-username myusername \
--registry-password mypassword
Key Benefits: Perfect for one-off tasks, automatic cleanup when job completes, pay-per-second billing.
Azure Container Apps: Microservices Made Easy
Container Apps shines in microservices scenarios. Let’s build a complete example with multiple services.
Example 3: Microservices Architecture with Container Apps
# 1. Create Container Apps Environment
az containerapp env create \
--name my-environment \
--resource-group myResourceGroup \
--location eastus
# 2. Deploy API Gateway Service
az containerapp create \
--name api-gateway \
--resource-group myResourceGroup \
--environment my-environment \
--image nginx:latest \
--target-port 80 \
--ingress external \
--min-replicas 1 \
--max-replicas 10 \
--cpu 0.5 \
--memory 1.0Gi
# 3. Deploy Internal User Service
az containerapp create \
--name user-service \
--resource-group myResourceGroup \
--environment my-environment \
--image myregistry.azurecr.io/user-service:v1 \
--target-port 3000 \
--ingress internal \
--min-replicas 0 \
--max-replicas 5 \
--cpu 0.25 \
--memory 0.5Gi
Container Apps YAML Configuration
properties:
configuration:
ingress:
external: true
targetPort: 80
allowInsecure: false
secrets:
- name: database-connection
value: "Server=myserver;Database=mydb;..."
registries:
- server: myregistry.azurecr.io
username: myusername
passwordSecretRef: registry-password
template:
containers:
- image: myregistry.azurecr.io/my-app:latest
name: main-app
env:
- name: DATABASE_CONNECTION
secretRef: database-connection
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
resources:
cpu: 0.5
memory: 1Gi
scale:
minReplicas: 1
maxReplicas: 10
rules:
- name: http-requests
http:
metadata:
concurrentRequests: "10"
Key Benefits: Built-in service discovery, automatic HTTPS, Dapr integration, event-driven scaling.
Azure Kubernetes Service: Enterprise-Grade Control
AKS gives you the full power of Kubernetes. Here’s a complete microservices deployment.
Example 4: Complete AKS Deployment
# 1. Create AKS Cluster
az aks create \
--resource-group myResourceGroup \
--name myAKSCluster \
--node-count 3 \
--node-vm-size Standard_B2s \
--enable-addons monitoring \
--enable-cluster-autoscaler \
--min-count 1 \
--max-count 5
# 2. Get credentials
az aks get-credentials \
--resource-group myResourceGroup \
--name myAKSCluster
Kubernetes Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: myregistry.azurecr.io/user-service:v1
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: connection-string
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: user-service-svc
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Performance Comparison: Real-World Scenarios
Scenario 1: E-commerce API Backend
Metric | ACI | Container Apps | AKS |
---|---|---|---|
Cold Start Time | 10-15 seconds | 2-5 seconds | Instant (pre-warmed) |
Auto-scaling Response | N/A | 30-60 seconds | 15-30 seconds |
Max Concurrent Requests | Limited by instance | 10,000+ per replica | Unlimited (cluster capacity) |
Setup Complexity (1-10) | 2 | 4 | 8 |
Scenario 2: Data Processing Pipeline
ACI Approach: Perfect for event-triggered batch jobs
# Triggered by Azure Function or Logic App
az container create \
--name data-processor-$(date +%s) \
--image myregistry.azurecr.io/data-processor:latest \
--restart-policy Never \
--environment-variables \
INPUT_BLOB_URL=$1 \
OUTPUT_CONTAINER=$2
Container Apps Approach: Event-driven scaling with KEDA
scale:
minReplicas: 0
maxReplicas: 20
rules:
- name: azure-servicebus
custom:
type: azure-servicebus
metadata:
queueName: processing-queue
messageCount: '10'
AKS Approach: Custom operators and advanced scheduling
apiVersion: batch/v1
kind: Job
metadata:
name: data-processor
spec:
parallelism: 5
completions: 100
template:
spec:
containers:
- name: processor
image: myregistry.azurecr.io/data-processor:latest
resources:
requests:
memory: "2Gi"
cpu: "1000m"
restartPolicy: Never
nodeSelector:
workload: compute-intensive
When Each Service Excels
Choose ACI When:
- You need to run containers for short periods (minutes to hours)
- Simplicity is more important than advanced features
- You have unpredictable, sporadic workloads
- Cost optimization for low-usage scenarios is critical
Choose Container Apps When:
- Building modern microservices architectures
- You need auto-scaling but want to avoid Kubernetes complexity
- Event-driven applications are your primary use case
- You want built-in service mesh capabilities (Dapr)
Choose AKS When:
- You need specific Kubernetes features or ecosystem tools
- Complex networking or security requirements exist
- You’re modernizing existing Kubernetes applications
- Your team has strong Kubernetes expertise
Coming Up Next
In Part 3, we’ll explore advanced scaling strategies, networking configurations, and security features that can make or break your production deployments. We’ll also dive into monitoring and observability patterns for each service.
These code examples show the practical differences between Azure’s container services. The choice often comes down to balancing simplicity with control – choose the service that matches your team’s expertise and your application’s complexity needs.