Honeypot Live Stats — 212 connections today from 30 unique IPs (1823 total, 90 unique IPs all-time)
Miles Huynh
· Reading time: 4 minutes · Admin

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 run on 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.