Skip to content
CAMPUX Cloud Bootcamp Lab · Class 27 · AKS ← All labs
Hands-On Lab · Beginner
~35 min · Free control plane · Cloud Shell
AKS & kubectl · torn down
Class 27 · AKS Fundamentals

Your first cluster, and its first scar.

A Kubernetes cluster is easier to create than to understand. This lab creates one on the free control-plane tier, puts an app on it, then does the thing that makes Kubernetes finally click — deletes a running pod and watches the cluster quietly bring it back — before tearing the whole thing down for the price of a coffee.

Fig. 1 · What you build — kubectl, the free control plane, one paid node
kubectl talks to the free AKS control plane, which schedules pods onto one paid 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_B2s virtual machine pod · web pod · web desired state schedules onto the node is the only thing charging you
Why

Create is easy; the lesson is what heals it

Standing up a Kubernetes cluster takes one command and ten minutes, which is exactly why so many people have "created an AKS cluster" on their résumé and so few can explain what happened next. This lab is built around the moment that turns the first group into the second: you will deploy an app, delete one of its running pods by hand, and watch a controller notice the gap and recreate it — because you did not deploy a container, you deployed a statement that a container should exist, and the cluster spends its life defending that statement. Understand that, and every later feature — probes, autoscaling, upgrades — is the same idea again.

You will create a one-node cluster on the free control-plane tier, connect kubectl to it, run a Deployment, break it and watch it heal, reach it through a Service, then delete everything. Nothing here needs Docker on your machine and nothing here needs a credit card beyond the few cents the single node bills while it lives.

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

Before you begin — one-time setup

You need a free Azure account and the Azure CLI (az), signed in with az login. First time? The 15-minute Set up your machine page covers the account, the installs, and sign-in. Prefer zero installs? Run everything in Azure Cloud Shell (Bash) — it has az and kubectl preinstalled and is already signed in.

What it costs — read this before you start

The AKS control plane is free on the Free tier. The node is not: it is one Standard_B2s virtual machine that bills by the second it exists — cents per hour, not zero. Run the lab start-to-finish in one sitting and delete the resource group at the end and the whole thing costs well under a dollar. Walk away with the node running and it bills all week. Set a phone timer if you must step away.

Setup

One node, free control plane

Create a resource group and the smallest sensible cluster: a single-node system pool on the free tier, with a managed identity and monitoring left off to keep it lean.

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

RG="campux-lab-aks-rg"
az group create -n "$RG" -l eastus

# --tier free = free managed control plane, no uptime SLA (right for a lab)
# one small burstable node; managed identity for the cluster
az aks create -n campux-aks -g "$RG" \
  --tier free \
  --node-count 1 \
  --node-vm-size Standard_B2s \
  --enable-managed-identity \
  --generate-ssh-keys
Checkpoint After a few minutes az aks create returns a large JSON block with "provisioningState": "Succeeded". You now have a real Kubernetes control plane that Azure operates for free, attached to exactly one node that is quietly billing. That asymmetry — free brain, paid muscle — is the single most important cost fact about AKS.
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 campux-aks -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.

# 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 keeps the lab free; a real internet-facing app would use a LoadBalancer Service (a note below shows the one-line version, and why it costs a little more).

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 the Service chose. Delete a pod again while the page is up and reload: still served, because the Service routes to whichever replica is healthy. That indirection is why pods can be disposable.
Down

Tear it down — before the node bills overnight

One resource group holds the cluster and its node. Delete it, and delete the node's automatically-created infrastructure group too if it lingers.

az group delete -n campux-lab-aks-rg --yes --no-wait
az group exists -n campux-lab-aks-rg      # -> false once complete
Checkpoint The delete removes the cluster and, with it, the node that was the only thing billing. AKS also creates a second, auto-managed resource group (named like MC_campux-lab-aks-rg_campux-aks_eastus) for the node infrastructure; deleting the cluster's group tears that down too. Confirm both are gone in the portal if you want to be certain no VM is left running.
End

What you can now honestly claim

You created an AKS cluster, connected kubectl to its API server, deployed a Deployment, demonstrated the reconciliation loop by deleting a pod and watching it heal, exposed the app behind a Service, and tore the whole thing down for pocket change. More important than any of the commands: you can now explain, from having seen it, why deleting a pod is not an outage and why the cluster is a promise-keeper rather than a container-starter. That sentence, said in an interview with the confidence of someone who watched it happen, is worth more than a memorised kubectl cheatsheet.1

Footnotes
  1. A production-facing app would be exposed with a Service of type LoadBalancer (kubectl expose deployment web --type=LoadBalancer --port=80), which provisions a public Azure load balancer and a static IP — a small standing cost, which is why this lab uses port-forward instead. In front of many apps you would put an ingress controller rather than one load balancer per Service; that is the subject of Class 27c.
  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.