You already know how to build and run a container (that's the Docker course). This chapter shows
you the problem that appears the moment your app becomes real — and the beautifully simple idea
Kubernetes uses to solve it. By the end you'll know what a cluster is, what lives inside it, and
exactly what happens between typing kubectl apply and your app serving traffic.
Picture QuickBites, a small food-delivery startup (you'll follow them through this whole course). Their app is containerized and business is good: an API, a frontend, a database, a payment worker — about 40 containers spread across 10 servers. No Kubernetes yet. Just Docker, SSH, and a very tired engineer on call.
Every one of those questions has a manual answer: an SSH session, a script, a checklist, a human. Manual answers don't scale, don't run at 2 a.m., and don't survive the day your best engineer goes on holiday. Kubernetes is the machine that answers all of them automatically.
Here is the mental shift that makes Kubernetes click. Everything you've done so far —
docker run, docker restart, SSH — is imperative:
you issue commands, one by one, and you are responsible for what happens next.
Kubernetes flips it: you declare the end state you want, in a YAML file,
and the cluster works forever to make reality match it.
"Start a container on server-2. Now one on server-5. server-2's crashed — restart it. server-8 died — start its containers on server-3…"
If you stop giving orders, everything stops being managed.
"There should always be 3 copies of my API, built from
image api:v2, each getting 256 MB of memory."
The cluster enforces this 24/7 — even while you sleep.
That declaration is just a small YAML file. This is a real one — three copies of an API, forever:
apiVersion: apps/v1 kind: Deployment # "keep N copies of this container running" metadata: name: api spec: replicas: 3 # ← the desired state: ALWAYS three selector: matchLabels: { app: api } template: metadata: labels: { app: api } spec: containers: - name: api image: quickbites/api:v2 # the container image to run
replicas: 3 is the temperature dial, and its controllers run that
compare-and-fix loop (the reconciliation loop) every few seconds, forever.
In Kubernetes your container runs inside a Pod — the smallest deployable unit,
basically a thin wrapper that gives your container an IP address and a home on some server.
The demo below is a tiny cluster running replicas: 3.
Click any pod to kill it (or use the chaos button) and watch the reconciliation
loop do the 2 a.m. work for you.
So what machinery makes that loop happen? Every Kubernetes cluster splits into two roles: a control plane (the brain — it decides) and worker nodes (the muscle — they run your actual containers).
kube-apiserver is the only door — kubectl, nodes and controllers all talk through it. etcd remembers everything. The scheduler places pods; the controller-manager runs the thermostat loops. In managed clouds (EKS, GKE, AKS) this whole layer is run for you.
Any Linux machine can join as a worker. Its kubelet takes orders ("run this pod"), tells containerd to pull images and start containers, and reports health back. kube-proxy wires the network so pods can reach each other across nodes.
The unit everything is measured in. A pod wraps one (usually) container, gets its own IP, lives on one node, and is deliberately disposable — pods are cattle, not pets. Higher-level objects like Deployments (next chapter) exist precisely because pods are allowed to die.
Let's connect everything with the one flow you'll repeat hundreds of times. You edit your YAML
to image: quickbites/api:v3 and run kubectl apply -f deployment.yaml.
Six things happen, in order:
kubectl sends your YAML to the kube-apiserver over HTTPS. It validates the spec and checks you're allowed to do this.
"api should be 3 × quickbites/api:v3" is now recorded. Nothing has run yet — Kubernetes has simply accepted the goal.
The Deployment controller compares: desired v3, actual v2 — and starts a rolling update, creating new v3 pods before killing old v2 ones, so there's zero downtime.
Each new pod is assigned to the healthiest node with room — CPU, memory and other rules considered in milliseconds.
The chosen node's kubelet sees its assignment, containerd pulls quickbites/api:v3 from the registry and starts the container.
The pod reports Ready and starts receiving traffic. From this second on, the reconciliation loop guards it: crash it, or even lose the whole node, and the cluster rebuilds toward your declared state. That is the entire magic of Kubernetes.
$ kubectl apply -f deployment.yaml deployment.apps/api configured $ kubectl get pods NAME READY STATUS RESTARTS AGE api-6d5f8-x2p4q 0/1 ContainerCreating 0 2s ← new v3 coming up api-59b7c-8kfns 1/1 Running 0 3d ← old v2 still serving api-59b7c-mw9zt 1/1 Running 0 3d $ kubectl get pods # a few seconds later… api-6d5f8-x2p4q 1/1 Running 0 9s api-6d5f8-tr7vb 1/1 Running 0 6s api-6d5f8-qq21d 1/1 Running 0 4s ← rollout complete, zero downtime
replicas: 3 → replicas: 10 is a one-line change; it can even scale automatically on CPU load (chapter 12).replicas: 3 and one pod crashes at 2 a.m. What happens?kubectl
is really just a friendly HTTPS client for it.