DevOps on Azure, part 2: Compute — App Service, AKS, Container Apps, Functions
Part 2from the DevOps on Azure series · 6 parts in all
Part 2 of the Azure series: where code runs. Azure's compute lineup is the most opinionated of the three clouds — App Service in particular does so much for you that knowing its limits is a skill in itself.
App Service: the PaaS baseline
Push a repository or container; App Service handles the runtime, patching, TLS, load balancing, and autoscale. MapleCart's storefront lives here:
# Create the plan and web app (CLI; in prod this is the Terraform in part 6)
az appservice plan create -g maplecart-rg -n maplecart-plan \
--is-linux --sku P1v3 --number-of-workers 2 # Premium: autoscale + slots
az webapp create -g maplecart-rg -p maplecart-plan -n maplecart-web \
--container-image maplecart.azurecr.io/web:v42
# Deployment slots: staging exists alongside production
az webapp deployment slot create -g maplecart-rg -n maplecart-web --slot staging
az webapp deployment slot swap -g maplecart-rg -n maplecart-web --slot staging \
--target-name production # warm swap: zero-downtime, one operation
# Auto-scale on CPU between 2 and 6 instances
az monitor autoscale create -g maplecart-rg --resource maplecart-plan \
--name cpu-scale --min-count 2 --max-count 6 --count 2
az monitor autoscale rule create -g maplecart-rg --autoscale-name cpu-scale \
--condition "Percentage CPU > 70 avg 10m" --scale out 2
az monitor autoscale rule create -g maplecart-rg --autoscale-name cpu-scale \
--condition "Percentage CPU < 30 avg 15m" --scale in 1
The slot swap deserves emphasis: deploy to staging, run smoke tests against
the real staging URL, then swap — App Service warms the new instances before
they receive traffic. It is the best zero-downtime story on any cloud, and it's why MapleCart
picks App Service over Kubernetes it doesn't need.
AKS and Container Apps: when you need Kubernetes — and when you don't
FleetView runs a dozen microservices with per-service scaling and sidecars: that's AKS (managed Kubernetes) territory, or its lighter cousin Container Apps (Kubernetes features — scale-to-zero, KEDA event scaling — without owning a cluster):
az aks create -g fleetview-rg -n fleetview-aks \
--node-count 3 --node-vm-size Standard_D4s_v5 \
--enable-managed-identity --network-plugin azure \
--enable-oidc-issuer --enable-workload-identity # pods get Entra identities
az containerapp create -g fleetview-rg -n ingest-api --environment fleetview-env \
--image fleetview.azurecr.io/ingest:v9 \
--scale-rule-name queue --scale-rule-type azure-servicebus \ # scale on queue depth
--min-replicas 0 --max-replicas 30 # scale to zero overnight
Functions: the event-driven tier
NewsGrid's API and image pipeline are Azure Functions on the Consumption plan — billed per execution, zero when idle:
# FunctionApp (Python, isolated model). The trigger is the whole deployment story.
import azure.functions as func
app = func.FunctionApp()
@app.function_name(name="ResizeImage")
@app.blob_trigger(arg_name="blob", path="uploads/{name}",
connection="NewsGridStorage")
def resize_image(blob: func.InputStream):
from PIL import Image; import os
if not blob.name.lower().endswith((".jpg", ".png")):
return # guard first
im = Image.open(blob) # stream from storage
im.thumbnail((800, 800))
out = os.path.join("/tmp", os.path.basename(blob.name))
im.save(out, optimize=True)
# connection string comes from app settings fed by managed identity
The decision table
| App Service | AKS / Container Apps | Functions | |
|---|---|---|---|
| You manage | code | manifests (or cluster) | function |
| Idle cost | plan price | per-pod / zero (CA) | zero |
| Slots/blue-green | built-in (best in class) | rollouts | slots (premium) |
| Fit | web apps, APIs | microservice fleets | events, spiky |
Next: the data layer — Azure SQL, Cosmos DB, Blob Storage — and how each company's data shape dictates its choice.