KUBERNETES TUTORIAL k8s
You're connected to a real 3-node cluster that's already running a small app. We won't deploy anything today. We'll learn what a cluster is made of, read its state with `kubectl`, and watch Kubernetes heal itself.
What we're doing
When you hit Start, a real cluster is already up (k3d-firstlook: one control-plane node and two workers) running a shop app. You'll use kubectl to look around: which cluster you're on (contexts), what machines exist (nodes), what's running (pods), and where it's running. Then you'll delete a pod and watch the cluster replace it on its own.
Watch the video first, then run these as you read. Every command and flag is explained. kubectl is already set up for you here; nothing needs sudo.
The one idea: desired state
With Docker you give an order: "start this container." With Kubernetes you declare a wish: "keep 3 of these running, always." Kubernetes then runs a loop forever, comparing what you asked for against what's actually running, and fixing any gap. Kill a pod and it starts another, because two is not the three you asked for. That one loop is behind self-healing, scaling, and rollouts. Hold onto it; you'll see it live in Step 6.
Step 1: Which cluster am I on?
Always start here. A context is a saved bundle of "which cluster, which user, which namespace," under one name.
kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* k3d-firstlook k3d-firstlook admin@k3d-firstlook
staging staging staging-admin web
The * marks the context you're pointed at: k3d-firstlook, the real one. The staging entry is here to show what a second cluster looks like in the list (in a real job you'd have dev, staging, prod). You switch with:
kubectl config use-context k3d-firstlook # (you're already on it)
Habit that saves you: before running anything that changes a cluster, check the
*. Reading the wrong cluster is harmless; acting on the wrong one is not.
Step 2: What machines are in the cluster?
kubectl get nodes -o wide
NAME STATUS ROLES VERSION
k3d-firstlook-server-0 Ready control-plane,master v1.31.5+k3s1
k3d-firstlook-agent-0 Ready <none> v1.31.5+k3s1
k3d-firstlook-agent-1 Ready <none> v1.31.5+k3s1
kubectl get nodes lists the machines (nodes) in the cluster. Three, all Ready. The ROLES column is the thing to read:
server-0is the control-plane: the brain that stores the desired state and decides where things run.- the two
agentnodes are workers: where your containers actually run.
-o wide just adds extra columns. You talk to the control plane with kubectl; you never log into the nodes.
Step 3: What's actually running?
kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-... 1/1 Running 0 5m
kube-system local-path-provisioner-... 1/1 Running 0 5m
kube-system metrics-server-... 1/1 Running 0 5m
shop cache-... 1/1 Running 0 4m
shop web-... 1/1 Running 0 4m
shop web-... 1/1 Running 0 4m
shop web-... 1/1 Running 0 4m
A Pod is the smallest unit you run; it wraps your container. -A means all namespaces. Two groups:
kube-system: Kubernetes running its own internals (corednsis in-cluster DNS, the others are helpers). Ignore these day to day.shop: the actual app, acacheand threewebpods.
READY 1/1 and STATUS Running mean healthy. Those three web pods are the "I want 3" wish, alive in front of you.
A Namespace is just a folder that groups things.
-Ashows every namespace; later we use-n shopto look at just one.
Step 4: Where is the app running?
kubectl get pods -n shop -o wide
NAME READY STATUS NODE IP
web-... 1/1 Running k3d-firstlook-server-0 10.42.2.4
web-... 1/1 Running k3d-firstlook-agent-0 10.42.0.4
web-... 1/1 Running k3d-firstlook-agent-1 10.42.1.3
cache-... 1/1 Running k3d-firstlook-agent-1 10.42.1.4
-n shop narrows to that one namespace; -o wide adds the NODE column. Notice the three web pods are on three different nodes, one each. You never asked for that, the scheduler spread them so that one machine dying can't take out all your copies at once. That's orchestration doing its job, visible in one column.
Step 5: Zoom into one pod
get is the wide view; describe is the zoom. Copy one web pod name from above and:
kubectl describe pod -n shop <web-pod-name>
You get everything about that pod: its node, its image, and an Events list at the bottom that reads like a diary (scheduled, pulled image, started). When a pod misbehaves, that Events section is the first place you look. Right now it just tells the happy startup story.
Step 6: Watch it heal itself
The payoff. The wish is "3 web pods, always." Break it and watch.
In one terminal, start a live watch:
kubectl get pods -n shop -w
-w means watch: the list stays live and updates as things change. Leave it running. In a second terminal, delete one web pod (use a real name from Step 4):
kubectl delete pod -n shop <web-pod-name>
Flip back to the watching terminal. You'll see:
web-... 1/1 Terminating # the one you deleted
web-xyz 0/1 ContainerCreating # a brand new one, seconds later
web-xyz 1/1 Running # back to 3
You didn't restart anything. The Deployment saw two pods when it wanted three and created a replacement, in seconds, on its own. That's self-healing, the clearest reason Kubernetes exists. Stop the watch with Ctrl+C.
The objects you just met
| Object | One-liner |
|---|---|
| Pod | wraps your container; the smallest thing you run |
| ReplicaSet | keeps the right number of identical pods |
| Deployment | "keep N of these running, this version" (owns a ReplicaSet) |
| Service | one stable address in front of a set of pods |
| Namespace | a folder that groups objects (like shop) |
| Node | a machine in the cluster (control-plane or worker) |
The chain to remember: a Deployment owns a ReplicaSet, which keeps the right number of Pods, each running on a Node, with a Service as the front door.
Cheat sheet
kubectl config get-contexts # which cluster am I on? (* = current)
kubectl config use-context <name> # switch clusters
kubectl get nodes -o wide # the machines in the cluster
kubectl get pods -A # everything running, all namespaces
kubectl get pods -n shop -o wide # one namespace, with the NODE column
kubectl describe pod -n shop <name> # zoom into one pod + its events
kubectl get pods -n shop -w # live watch (Ctrl+C to stop)
kubectl delete pod -n shop <name> # delete one, and watch it come back
The one thing to remember: you declare the state you want, and Kubernetes works forever to keep reality matching it. Everything else (scheduling, self-healing, scaling, rollouts) is that one idea at work.
Next tutorial: we deploy our own app onto this cluster.
What's next
Start Kubernetes