Color legend (used throughout)
1 ยท What is a container?
A container is just a normal Linux process running on your host kernel โ but the kernel lies to it about the world. It thinks it has its own filesystem, its own network interfaces, its own process tree. It sees almost nothing of the host.
The lies are produced by three kernel features: namespaces (what you can see), cgroups (how much you can use), and chroot/overlay filesystems (which files exist).
Virtual Machine (heavy)
Every VM ships its own kernel โ GBs of RAM, minutes to boot.
Container (light)
All containers share one kernel โ MBs of RAM, milliseconds to boot.
The container in our lab
YAML we wrote
containers: - name: proddetail image: python:3.12-alpine command: ["/bin/sh", "-c"] args: - touch /tmp/healthy /tmp/ready python3 -m http.server 3000 ports: - containerPort: 3000
What actually runs on the node
โ๏ธ PID 1:
python3 -m http.server 3000๐ listening on :3000
๐ own /tmp, own /proc, own network interface
2 ยท What is a Pod?
A Pod is a thin wrapper around one or more containers that share a network and a small set of disks. From outside the pod looks like one machine: one IP, one localhost, one DNS name.
99% of pods have exactly one container. The multi-container pattern is for tightly-coupled "sidecars" (log shipper, proxy) that need to share localhost with the main app.
Pod โ Container โ three differences that matter
| Container | Pod | |
|---|---|---|
| Scheduling unit | No โ runtimes don't move containers between hosts. | Yes โ Kubernetes schedules pods onto nodes. |
| IP address | No own IP โ would share the host's. | Gets its own cluster-internal IP (e.g. 10.244.0.14). |
| Lifecycle | Just a process โ when it crashes, it's gone. | Can hold multiple containers, restart them, share storage. |
3 ยท The full hierarchy
You almost never create a Pod directly. You write a Deployment, and three nested controllers do the work.
app=proddetail aliveWho's responsible for what?
| Level | Job | You write? |
|---|---|---|
| Deployment | Pod template + update strategy (rolling, recreate). Manages ReplicaSets. | Yes |
| ReplicaSet | Keep N pods of a specific template alive. Spawns replacements when pods die. | Auto-created |
| Pod | The actual unit that gets scheduled to a node. Hosts container(s). | Auto-created |
| Container | The running process. | Defined in pod template |
4 ยท Service โ the stable address
Pods are mortal. They die, restart, get new IPs constantly. So you can't just save a pod's IP and call it.
A Service is a permanent virtual IP and DNS name that load-balances to whichever pods match its selector right now.
virtual IP: 10.109.233.228:3000 ยท DNS: proddetail.workshop.svc.cluster.local
selector: app=proddetail
Labels and selectors โ the glue of Kubernetes
Almost every "X connects to Y" relationship in Kubernetes is implemented with labels. The Service has a selector; pods have labels; they match โ wired.
Service selector
spec: selector: app: proddetail
Pod labels
metadata: labels: app: proddetail version: v2
Endpoints: <none> and no traffic flows. Always check with kubectl get endpoints <svc>.
5 ยท Versioning & rolling updates โ the magic moment
Here's what your eyes saw earlier when we went from v1 โ v2 with zero downtime.
The setup
Before the change, the Deployment is pointing at ReplicaSet A (v1) with 3 pods. We change the pod template โ Deployment creates ReplicaSet B (v2) and orchestrates a controlled handoff.
The strategy knobs
What we wrote
strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0
What it means
- maxSurge: 1 โ at most 4 pods total during the rollout (3 desired + 1 extra).
- maxUnavailable: 0 โ never fewer than 3 Ready pods. Capacity never dips.
Frame-by-frame timeline
ReplicaSets after the rollout
| ReplicaSet | Pods | Role |
|---|---|---|
proddetail-6bb476c4b6 | 0 | Ancient (no probes). Kept for rollback. |
proddetail-784d8f4b99 | 0 | v1 with probes. Kept for rollback. |
proddetail-74f9b4b4b9 | 3 | Active. v2. |
One-command rollback
$ kubectl rollout undo deployment/proddetail -n workshop # Same algorithm โ just scales the OLD RS back up and NEW RS back down. # Service Endpoints update continuously. Zero downtime, both directions.
6 ยท Probes โ self-healing in two flavors
Liveness vs readiness โ the one-sentence distinction
| Liveness probe fails | Readiness probe fails | |
|---|---|---|
| What happens to the pod | ๐ช Container killed and restarted | ๐ง Pod stays alive, removed from Service Endpoints |
| RESTARTS counter | +1 | 0 |
| Pod name | same (container restarts inside same pod) | same |
| Use it for | Detecting deadlocks, stuck processes | Warming up, draining, dep outages |
Readiness probe โ visual state machine
Pod can flip back and forth indefinitely. Never gets killed.
Liveness probe โ visual state machine
After failureThreshold consecutive failures, kubelet kills and restarts the container.
What we actually demonstrated
Readiness fail
$ kubectl exec $POD -- rm /tmp/ready # 8s later: $ kubectl get endpoints proddetail NAME ENDPOINTS proddetail โ empty! $ kubectl get pod NAME READY STATUS RESTARTS proddetail-x 0/1 Running 0 โฒ not Ready โฒ but alive โฒ never killed
Liveness fail
$ kubectl exec $POD -- rm /tmp/healthy # ~12s later: $ kubectl get pod NAME READY STATUS RESTARTS proddetail-x 1/1 Running 1 (18s ago) โฒ killed and restarted $ kubectl get events ... Warning Unhealthy Liveness probe failed Normal Killing Container will be restarted
7 ยท What each kubectl command actually does
kubectl is just an HTTP client for the Kubernetes API server. Every command becomes a REST call to /api/v1/....
kubectl apply -f file.yaml
What you type
$ kubectl apply -f deployment.yaml
Behind the scenes
- YAML โ JSON
- HTTP PUT/PATCH to API server
- API server validates & writes to etcd
- Deployment controller wakes up, sees new desired state
- Creates/updates ReplicaSet
- ReplicaSet controller creates Pod objects
- Scheduler picks a node for each pod
- kubelet on that node pulls the image and starts the container
kubectl get / describe / exec โ what they really are
| Command | REST equivalent | What it returns |
|---|---|---|
kubectl get pods | GET /api/v1/namespaces/.../pods | Table view of pod list |
kubectl describe pod x | GET pod x + GET events?involvedObject=x | Verbose pod state + recent events |
kubectl exec -it pod -- bash | POST /pods/x/exec?command=bash (WebSocket) | Bidirectional tunnel to a process inside the container |
kubectl logs pod | GET /pods/x/log | The container's stdout/stderr stream |
kubectl rollout undo deploy/x | PATCH deployment with old pod-template-hash | Reverses the spec back to a previous revision |
The control loop โ why this all keeps working
Every Kubernetes controller is the same shape:
Forever. This is the loop. Pods die โ ReplicaSet notices โ spawns new pod. You change image โ Deployment notices โ rolling update. Everything in Kubernetes is one of these loops.