Skip to content
CAMPUX Cloud Bootcamp Lab · Class 27 · AKS ← All labs
Hands-On Lab · Beginner
Time varies · AKS Free management tier · Cloud Shell
Billable resources · cleanup required
Class 27 · AKS Fundamentals

Your first cluster, and its first scar.

Create a one-node AKS cluster, deploy an app, delete one of its pods, and watch the Deployment replace it. Finish by deleting and verifying the lab resources. The Free management tier still uses billable Azure resources.

Fig. 1 · What you build — an Azure-managed control plane and a billable node
kubectl talks to the AKS control plane, which schedules pods onto a billable node. kubectl your laptop / Cloud Shell AKS control plane · API server Azure operates it — free on the Free tier you never patch or size it Node · one Standard_D2as_v4 virtual machine pod · web pod · web desired state schedules onto the node bills; other resources may too
Start

Run one step, then check it

Read the step before copying. Bash commands belong in Git Bash on Windows or Azure Cloud Shell set to Bash; PowerShell uses different syntax. A JSON, YAML, Bicep or application-code example belongs in the named file, not directly at the terminal prompt. Follow any shell-specific instructions below.

Keep the same terminal open so earlier variables remain available. Copy one complete command block without the terminal's $ prompt. A backslash continues a command onto the next line. If Bash shows a lone >, it is waiting for the rest of a quote, command or file block. Finish the complete block; if the paste was incomplete, press Ctrl+C and copy it again. Do not paste the next step into an unfinished command.

A line such as RG="training-group" stores a value without printing it. Output captured with $(...) is also stored; a blank screen alone does not prove failure. A sleep deliberately waits, and -o none suppresses normal output. Wait for your normal prompt, then run the step's checkpoint. After an error, stop and keep the exact command and message; later steps cannot repair a missing resource. Never share passwords, access tokens or secret values in a screenshot.

Why

Create is easy; the lesson is what heals it

Creating a cluster is only the setup. The lesson begins when you deploy two replicas, delete one pod, and watch the Deployment controller create a replacement. Kubernetes compares the requested replica count with the pods it can observe and acts on the difference. Cluster creation time varies, so leave time for setup and cleanup as well as the exercise.

You will create a one-node cluster on the Free management tier, connect kubectl, run a Deployment, delete a pod and observe its replacement, reach the app through a Service, then remove the lab resources. Docker is not required on your machine. Azure may charge for the node and other resources; check your subscription's pricing and quota before starting.

You did not deploy a container. You deployed a promise the cluster keeps.

Before you begin — one-time setup

You need an Azure subscription with permission and quota to create AKS, node VMs, and networking resources. Use the Azure CLI (az) in Bash. The setup page covers installation and sign-in. Azure Cloud Shell (Bash) provides az and kubectl without a local install; confirm the selected subscription before creating resources.

What it costs — read this before you start

The AKS Free tier has no cluster-management charge and no uptime SLA. This lab's Standard_D2as_v4 node is billable, and disks, public IPs, networking, or monitoring may add charges. Prices vary by region, subscription, and time in use, so this page cannot promise a total. Check current Azure pricing, quota and spending alerts. Plan to finish and verify deletion in one sitting.

Setup

One node, Free management tier

Use Bash in Cloud Shell or a local Bash terminal. Confirm your subscription, then create a uniquely named resource group and a one-node cluster on the Free management tier. Keep the printed names for cleanup. AKS creation may take longer than this page's reading time.

# Windows/Git Bash: stop it mangling /subscriptions/... arguments (harmless on macOS/Linux)
export MSYS_NO_PATHCONV=1

az account show --query '{name:name,subscriptionId:id}' -o table
RG="rg-cx27-aks-$RANDOM-$RANDOM"
CLUSTER="cx27-aks-$RANDOM"
printf 'Group: %s\nCluster: %s\n' "$RG" "$CLUSTER"
az group create -n "$RG" -l eastus

# --tier free = free managed control plane, no uptime SLA (right for a lab)
# one Standard_D2as_v4 node; managed identity for the cluster
# if Azure says this size "is not allowed in your subscription", the same error lists the sizes
# that are; pick a 2-vCPU one from that list (Standard_D2as_v4 worked for us in October 2026)
az aks create -n "$CLUSTER" -g "$RG" \
  --tier free \
  --node-count 1 \
  --node-vm-size Standard_D2as_v4 \
  --enable-managed-identity \
  --generate-ssh-keys
Checkpoint Confirm provisioningState: Succeeded before continuing. If creation fails after the resource group exists, inspect both the group and any AKS-managed resource group; clean up what Azure created. The Free management tier does not eliminate node or related resource charges.
Step 1

Point kubectl at the cluster

Everything from here is kubectl talking to the API server. Merge the cluster's credentials into your kubeconfig, then confirm the node is ready.

az aks get-credentials -n "$CLUSTER" -g "$RG"

kubectl get nodes
kubectl get pods -A      # -A = all namespaces, incl. the system ones AKS runs
Checkpoint kubectl get nodes lists one node with status Ready. kubectl get pods -A shows pods already running in kube-system — CoreDNS, the metrics server, networking — the machinery AKS placed there for you. You never deployed those; the control plane keeps them running the same way it will keep yours.
Step 2

Deploy a statement of intent

Create a Deployment of two replicas. Read the command as a sentence: "two copies of this image should exist." Then watch the cluster make it true.

kubectl create deployment web \
  --image=mcr.microsoft.com/k8se/quickstart:latest \
  --replicas=2

kubectl get pods -l app=web -w      # -w = watch; Ctrl-C once both are Running
Checkpoint Two pods appear, move through ContainerCreating, and settle on Running. You did not start two containers — you told the API server that two should exist, and a controller created them to close the gap between desired (2) and observed (0). Note the pod names: a Deployment stamps each with a random suffix, because pods are cattle, not pets.
Step 3

Break it — the moment Kubernetes clicks

Now delete one running pod by hand and immediately list them again. Do it quickly so you catch the cluster in the act.

Commands inside $(...) save their output in a variable. Check the printed identifier or the checkpoint before continuing.

# grab one pod's name, delete it, then look right away
POD=$(kubectl get pods -l app=web -o jsonpath='{.items[0].metadata.name}')
kubectl delete pod "$POD"

kubectl get pods -l app=web
Checkpoint The pod you deleted is gone — and a new one, with a different suffix, is already being created or is Running. You are still at two. Nobody restarted anything by hand; the Deployment's desired count is 2, a controller observed 1, and it recreated a pod to close the difference. This is the reconciliation loop, and it is the whole of Kubernetes in one command. Everything else is this, applied to health, scale, and traffic.
Step 4

Reach the app

The pods run, but nothing outside the cluster can reach them yet. Expose the Deployment behind a Service — a stable name and address in front of the shifting pods — then forward a local port to it. Port-forward avoids creating a public load balancer for this exercise; the node and other Azure resources can still be billed.

kubectl expose deployment web --port=80 --target-port=80
kubectl get service web

# forward localhost:8080 to the Service; open the URL, then Ctrl-C
kubectl port-forward service/web 8080:80
Checkpoint kubectl get service web shows a ClusterIP Service. With port-forward running, open http://localhost:8080 (in Cloud Shell, use the web preview on port 8080) and you get the quickstart welcome page — served by whichever pod port-forward picked when the tunnel opened. That pin belongs to the tunnel, not the Service: port-forward holds one pod for its whole session instead of routing each request the way the Service does for every other client. Delete that same pod now and the tunnel breaks — reload and you get a connection error, not the page. Run kubectl port-forward service/web 8080:80 again and it picks whichever pod is running now. The Service kept routing the entire time; only your one local shortcut had to be restarted.
Down

Tear it down — before the node bills overnight

Stop port-forward with Ctrl-C and use the same Bash session that holds $RG and $CLUSTER. Confirm that $RG is the unique lab group you created. AKS also creates a managed node-resource group. Record its name if the cluster exists, delete the lab group, then verify both are gone. Do not independently delete a managed group while the AKS deletion is still running.

Commands inside $(...) save their output in a variable. Check the printed identifier or the checkpoint before continuing.

printf 'Delete lab group: %s\n' "$RG"
az group show -n "$RG" --query '{name:name,resourceId:id}' -o table
NODE_RG=$(az aks show -n "$CLUSTER" -g "$RG" --query nodeResourceGroup -o tsv 2>/dev/null || true)
printf 'Managed node group: %s\n' "$NODE_RG"
az group delete -n "$RG" --yes
az group exists -n "$RG"             # expected: false
if [ -n "$NODE_RG" ]; then az group exists -n "$NODE_RG"; fi  # expected: false after cleanup
Checkpoint The lab group existence check should print false. If a managed group was found, its check should also print false. If either remains, allow time for AKS cleanup and inspect it in the Azure portal before taking further action. If creation failed before a managed group name was available, inspect the subscription's resource groups and cost view for leftovers. Do not assume that a submitted delete request has finished just because the command returned.
End

What you can now honestly claim

You created an AKS cluster, connected kubectl, deployed an app, and watched a Deployment replace a deleted pod. You also verified that the lab groups were removed. The practical lesson is precise: a controller replaces a pod when its Deployment still requests that replica and the cluster has the resources to run it. That does not guarantee a user saw no interruption; availability depends on replicas, readiness, and traffic routing.1

Footnotes
  1. A production-facing app may use a Service of type LoadBalancer or an ingress controller. These can create billable Azure networking resources and require security configuration. This lab uses port-forward to avoid exposing the example app publicly. Class 27c covers ingress. ↩
  2. The Free tier gives a managed control plane with no financially-backed uptime SLA — correct for a lab and fine for many workloads that tolerate the occasional control-plane blip. The Standard tier adds the SLA for a per-cluster hourly fee. Neither tier changes the node bill, which is separate and always present. Quote the current Azure pricing page before you promise anyone a number. ↩