Kubernetes, day one: pods, deployments, and why self-healing actually matters
Second infra-learning session this week, this time on Kubernetes — picked up right where Ansible left off, since K8s solves the same kind of problem ("keep this thing in the state I described") but for containers instead of servers.
Getting a cluster running
No separate install needed — Docker Desktop ships its own single-node Kubernetes cluster, off by default. Settings → Kubernetes → Enable Kubernetes → Apply & Restart, a few minutes, and kubectl get nodes showed one Ready control-plane node.
A Pod alone proves nothing
Started with the ad-hoc equivalent of docker run:
kubectl run web --image=nginx --port=80
Unlike Docker's -p, K8s doesn't expose a port just because you declared one — kubectl port-forward pod/web 8080:80 was needed to actually load nginx's welcome page at localhost:8080. Then, on purpose, killed it:
kubectl delete pod web
kubectl get pods
# No resources found in default namespace.
Gone. Nobody brought it back — a bare Pod behaves exactly like a bare Docker container: delete it, it's dead.
Deployment: the part that actually self-heals
Same idea, wrapped in a Deployment instead:
kubectl create deployment web --image=nginx --port=80
This time the Pod (web-7fdc5fd5c-cvl8h) wasn't created directly by hand — the Deployment created it, and kept watching it. Deleted that exact Pod and checked again within seconds:
kubectl delete pod web-7fdc5fd5c-cvl8h
kubectl get pods
# web-7fdc5fd5c-7jxwn 1/1 Running 0 4s
A new Pod, 4 seconds old, same ReplicaSet hash (7fdc5fd5c) but a new random suffix. Nobody ran a command — the Deployment noticed the actual count had dropped below the desired count and fixed it on its own. That's the whole idea: declare the end state once, K8s enforces it continuously, not just at the moment you typed the command.
Service: an address that outlives the Pod behind it
One more problem: every time a Pod gets recreated, its name and internal IP change. Anything depending on a specific Pod name breaks on every restart. A Service fixes that with a stable address that doesn't care which Pod is currently behind it:
kubectl expose deployment web --port=80 --target-port=80
kubectl port-forward service/web 8080:80
With that port-forward pointed at the Service instead of a Pod directly, killed the Pod behind it again from a second terminal (kubectl delete pod -l app=web) while the browser tab stayed open. Refreshed localhost:8080 mid-replacement — it still loaded, no interruption visible from the outside, even though the Pod serving that request had been swapped out underneath.
What I actually learned
- Docker Desktop already has a Kubernetes cluster available — no minikube/kind install needed to get started.
- A bare Pod is just a managed container; it has no more resilience than
docker runon its own. - Deployment is what actually gives you self-healing — it watches the desired replica count and recreates Pods that disappear, without being told to.
- Service exists specifically because Pod identity is disposable — it's the stable address other things should depend on, never the Pod name itself.
- Pod → Deployment → Service is the load-bearing trio almost everything else in Kubernetes builds on top of.
Next: taking this past Docker Desktop's single local node — probably k3s on the same free-tier EC2 box already doing Ansible duty, since a full kubeadm cluster wants more RAM than a t3.micro has to spare.