AKS Microservices Deploy Guide (for rookies): from cluster to ingress

A practical, microservice-oriented guide to Azure Kubernetes Service (AKS): node pools, ingress networking, deployment patterns, autoscaling, observability.

Jul 5, 202617 min read

Why AKS for microservices?

Microservices increase the number of deployable units, but they also increase the operational surface area: networking, scaling, rollouts, failures, and observability become harder. AKS (Azure Kubernetes Service) gives you the missing platform layer:

  • <strong>Scheduling &amp; isolation</strong>: run many microservices with different resource needs on shared infrastructure.
  • <strong>Declarative deployments</strong>: the desired state of each microservice (image, config, replicas) lives in manifests.
  • <strong>Horizontal scaling</strong>: autoscale per service instead of scaling whole applications.
  • <strong>Service discovery &amp; routing</strong>: stable service endpoints and controlled ingress.
  • <strong>Day-2 operations</strong>: rolling updates, rollbacks, health checks, and monitoring integrations.

What AKS actually is (in plain terms)

AKS is a managed Kubernetes control plane on Azure, plus the worker infrastructure (nodes) where your workloads run. Key parts you’ll see in the Azure portal and in day-to-day operations:

  • <strong>Resource Group</strong>: where AKS resources live.
  • <strong>Virtual Network (VNet)</strong>: your cluster’s network boundary.
  • <strong>Node Pools</strong>: groups of nodes with similar VM sizes / scaling settings.
  • <strong>Kubernetes control plane (managed by Microsoft)</strong>: the API server, etcd, and core control components.
  • <strong>Ingress / Load Balancing</strong>: how external traffic gets into your services.

Microservices architecture mapping (Kubernetes mental model)

A microservices system maps cleanly onto Kubernetes primitives:

  • <strong>Service</strong> (Kubernetes object) → stable internal endpoint for a microservice
  • <strong>Deployment</strong> → how a microservice runs (replicas, rollout strategy)
  • <strong>Ingress</strong> → HTTP(S) routing to microservices
  • <strong>ConfigMap / Secret</strong> → configuration and credentials
  • <strong>HPA</strong> → scaling rules per microservice
  • <strong>Namespace</strong> (optional but common) → environment separation (dev/stage/prod) or domain separation
  • <strong>PodDisruptionBudget / readiness+liveness probes</strong> → reliability during upgrades and node maintenance

A useful rule: treat each microservice as a <strong>small, replaceable unit</strong> that should survive redeploys and scale events without manual intervention.

Node Pools and environments: a practical setup

For microservices, node pools help you separate concerns:

  • <strong>System pool</strong>: nodes reserved for cluster-critical components (often you don’t touch this).
  • <strong>Application pools</strong>: your services.
  • <strong>Specialized pool (optional)</strong>: dedicated nodes for GPU workloads, batch jobs, or different scaling patterns.

Typical “good enough” starting point:

  • One application node pool for most services
  • Separate pool for workloads that require different VM sizes or isolation

You’ll also want to plan capacity:

  • <strong>Node auto-provisioning</strong> (cluster autoscaler) to reduce overprovisioning
  • <strong>Min/Max node bounds</strong> to control cost

Networking basics: VNet, Ingress, and service-to-service traffic

Microservices networking usually has two paths:

1) North-South traffic (outside → inside)

Ingress is where you route incoming traffic to services. A common pattern:

  • <code>api.company.com</code> routes to <code>/users</code> → <code>users-service</code>
  • <code>api.company.com</code> routes to <code>/orders</code> → <code>orders-service</code>

Implementation options:

  • <strong>Ingress Controller</strong> (e.g., NGINX ingress)
  • <strong>Azure Load Balancer</strong> integration (varies by AKS networking mode)

2) East-West traffic (service → service)

Inside the cluster, microservices talk to each other via Kubernetes Services. Important operational points:

  • Use <strong>readiness probes</strong> so services only receive traffic when they’re ready.
  • Prefer <strong>timeouts and retries</strong> in clients (and avoid retry storms).
  • If you later introduce service mesh or mTLS, plan the migration path early.

Deploying microservices on AKS (end-to-end workflow for rookies)

This section is a “do it step-by-step” path you can follow to deploy your <strong>first microservice</strong> to <strong>AKS</strong>.

What you will build

  • An AKS cluster (small demo)
  • A Kubernetes Deployment (runs your microservice)
  • A Kubernetes Service (gives it a stable internal IP/DNS)
  • An Ingress (routes traffic from <code>api.example.com</code> to your service)

&gt; Assumption: your microservice is already containerized (has a Docker image).

Prerequisites

  • Azure CLI installed (<code>az</code>)
  • Docker installed + logged in to a container registry
  • <code>kubectl</code> installed
  • (Recommended) an Azure Container Registry (ACR)

Step 1: Create an AKS cluster (demo-friendly)

Run a command like this (customize names):

bash
az group create --name rg-aks-microservices --location eastus

az aks create \
  --resource-group rg-aks-microservices \
  --name aks-microservices-demo \
  --node-count 2 \
  --enable-managed-identity \
  --generate-cert \
  --node-vm-size Standard_B2s \
  --enable-cluster-autoscaler \
  --min-count 1 \
  --max-count 5

What this does (rookie translation):

  • Creates a Kubernetes cluster in Azure.
  • Uses <strong>2 nodes</strong> to start so you can schedule your microservice.
  • Enables <strong>cluster autoscaler</strong> so AKS can add/remove nodes automatically.

Step 2: Get cluster credentials so <code>kubectl</code> can talk to AKS

bash
az aks get-credentials \
  --resource-group rg-aks-microservices \
  --name aks-microservices-demo \
  --overwrite-existing

Now test access:

bash
kubectl get nodes
kubectl get namespaces

Step 3: Prepare your container image

You need one image like:

  • <code>myregistry.azurecr.io/orders:1.0.0</code>

Build and push (example):

bash
# build
docker build -t orders:1.0.0 .

# tag for your registry
docker tag orders:1.0.0 myregistry.azurecr.io/orders:1.0.0

# push
docker push myregistry.azurecr.io/orders:1.0.0

&gt; If you don’t have ACR, you can use Docker Hub or another registry-just update the image name.

Step 4: Create a Kubernetes namespace (optional, but recommended)

Namespace helps separate environments and keeps things tidy.

bash
kubectl create namespace microservices
kubectl config set-context --current --namespace=microservices

Step 5: Create your Deployment (the microservice runtime)

Create a file <code>orders-deployment.yaml</code>:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: orders
spec:
  replicas: 2
  selector:
    matchLabels:
      app: orders
  template:
    metadata:
      labels:
        app: orders
    spec:
      containers:
        - name: orders
          image: yourregistry.azurecr.io/orders:1.0.0
          ports:
            - containerPort: 8080
          # Health checks let Kubernetes know when it is safe to send traffic
          readinessProbe:
            httpGet:
              path: /health/ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /health/live
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 10
          resources:
            requests:
              cpu: 200m
              memory: 256Mi
            limits:
              cpu: 1
              memory: 512Mi

Apply it:

bash
kubectl apply -f orders-deployment.yaml

Check status:

bash
kubectl get deployments
kubectl get pods
kubectl describe pod -l app=orders

Common rookie issue:

  • Pods in <code>CrashLoopBackOff</code> → check logs.
bash
kubectl logs -l app=orders --tail=200

Step 6: Create a Service (stable network identity)

Create a file <code>orders-service.yaml</code>:

yaml
apiVersion: v1
kind: Service
metadata:
  name: orders
spec:
  selector:
    app: orders
  ports:
    - name: http
      port: 80
      targetPort: 8080
  # You can keep this default ClusterIP for internal routing

Apply it:

bash
kubectl apply -f orders-service.yaml

Verify:

bash
kubectl get svc orders

Step 7: Add Ingress (route external traffic to the Service)

Ingress depends on your environment and an ingress controller. In AKS, you typically install NGINX Ingress Controller (or rely on an AKS default). For rookies, here’s the Ingress manifest: Create <code>orders-ingress.yaml</code>:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: orders-ingress
spec:
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /orders
            pathType: Prefix
            backend:
              service:
                name: orders
                port:
                  number: 80

Apply it:

bash
kubectl apply -f orders-ingress.yaml

Verify ingress resources:

bash
kubectl get ingress
kubectl describe ingress orders-ingress

&gt; You will still need DNS (or a local hosts entry) to point <code>api.example.com</code> to your ingress load balancer.

Step 8: Test your microservice locally (from inside the cluster)

A simple first test is <code>kubectl port-forward</code>.

bash
# forward local port 8081 to service port 80
kubectl port-forward svc/orders 8081:80

Then call:

bash
curl -v http://localhost:8081/orders

(If your app doesn’t include <code>/orders</code> path internally, adjust the route.)

Step 9: Deploy more microservices consistently

Once your first service is working, repeat the same pattern:

  • Deployment (image + probes + resources)
  • Service (ClusterIP for internal routing)
  • Ingress (for external access)

A huge rookie win: use the same label conventions everywhere (<code>app: &lt;service-name&gt;</code>).

Troubleshooting quick checklist

  • <code>kubectl get pods</code> → are containers running?
  • <code>kubectl logs</code> → is your app healthy or crashing?
  • <code>kubectl get svc</code> → does the Service exist and target the right pods?
  • <code>kubectl get ingress</code> → is the ingress controller routing?
  • <code>kubectl describe</code> → check Events at the bottom (this often tells you the exact failure).

Example: manifests for one microservice

Deployment + Service (simplified):

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: orders
spec:
  replicas: 2
  selector:
    matchLabels:
      app: orders
  template:
    metadata:
      labels:
        app: orders
    spec:
      containers:
        - name: orders
          image: yourregistry.azurecr.io/orders:1.0.0
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /health/ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /health/live
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 10
          resources:
            requests:
              cpu: 200m
              memory: 256Mi
            limits:
              cpu: 1
              memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
  name: orders
spec:
  selector:
    app: orders
  ports:
    - name: http
      port: 80
      targetPort: 8080

Add Ingress to route traffic:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-gateway
spec:
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /orders
            pathType: Prefix
            backend:
              service:
                name: orders
                port:
                  number: 80

Health endpoints you should implement

For microservices, “health” isn’t just up/down. It’s about *traffic safety*:

  • <code>/health/live</code>: the process is running; if this fails, Kubernetes should restart the container.
  • <code>/health/ready</code>: dependencies are reachable (DB, cache, required external services). When readiness fails, Kubernetes stops sending traffic.

Autoscaling: scale like a platform, not like a monolith

Autoscaling usually involves three layers:

1) Pod scaling (HPA)

HPA adjusts replicas of a microservice based on metrics (CPU, memory, or custom metrics). Example:

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: orders
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: orders
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

2) Node scaling (Cluster Autoscaler)

If HPA needs more pods than existing capacity allows, the cluster autoscaler can add nodes. Operational tip: set <strong>min/max node counts</strong> to avoid runaway cost.

3) Application-level throttling

Even with autoscaling, you still need guardrails:

  • rate limiting
  • backpressure
  • circuit breakers

For rate limiting and resilience patterns, see: <strong>system-design-rate-limiter</strong>.

Observability: logging, metrics, and traces (minimum viable)

A microservices platform fails silently if you can’t answer three questions quickly:

  1. <strong>Is it healthy?</strong> (metrics + health)
  2. <strong>Why is it slow?</strong> (logs + traces)
  3. <strong>Which service is causing failures?</strong> (correlated traces)

Practical minimum for AKS day-to-day:

  • <strong>Centralized logs</strong> (stdout/stderr from containers)
  • <strong>Metrics dashboards</strong> (request rate, error rate, latency percentiles)
  • <strong>Tracing</strong> (propagate trace IDs across services)

Rollouts and reliability: making updates boring

Microservices require safe deployment strategies:

  • Prefer rolling updates with readiness probes
  • Use <strong>maxUnavailable</strong> and <strong>maxSurge</strong> to control rollout pressure
  • Avoid updating too many services at once

For added resilience, consider:

  • <strong>PodDisruptionBudget</strong> to protect availability during node maintenance
  • <strong>Resource requests/limits</strong> to prevent “noisy neighbor” effects

Security basics (pragmatic)

Start with foundational controls:

  • Use Kubernetes <strong>Secrets</strong> for credentials (and rotate them)
  • Use namespaces to separate environments
  • Prefer least-privilege service accounts
  • Plan ingress TLS early

If you’re using microservices heavily, consider auth consistency:

  • standardize auth headers
  • centralize token validation logic (or use a gateway)

Cost management for AKS microservices

Cost surprises often come from:

  • overprovisioned nodes
  • long-running dev/test workloads
  • no resource requests (leading to inefficient scheduling)
  • excessive replica counts

Operational checklist:

  • set min/max for node pools
  • define CPU/memory requests &amp; limits
  • review HPA behavior under load tests
  • use autoscaling (both pods and nodes) with sensible bounds

Suggested microservices on-boarding checklist (day 1 → day 7)

Day 1: Make it deployable

  • container image built and pushed
  • Deployment + Service manifests
  • readiness/liveness endpoints

Day 2: Make it routable

  • Ingress route (if external)
  • stable service DNS inside the cluster

Day 3: Make it observable

  • standardized logging format
  • metrics emitted
  • tracing context propagation

Day 4: Make it scalable

  • HPA configured per microservice
  • resource requests set

Day 5: Make it resilient

  • timeouts, retries policy, and rate limiting
  • graceful shutdown behavior

Day 6: Make it safe to update

  • rollout strategy configured
  • verify rollback procedure

Day 7: Make it secure

  • least privilege service account
  • secret management and rotation plan