I wrote a value to Redis, deleted its pod, and it was gone. Did the same to a different Redis, deleted the pod, and it was still there. Same command, opposite result, and the difference explains what Kubernetes is actually doing.
The shift: describe, don't instruct
Every tool before this took commands that run once. Kubernetes takes a description of what should be true and keeps reality matching it. You say "I want two API pods," and the control plane makes and keeps that true.
The catch: you cannot fix things by hand. Delete a pod, it comes back, because the description still says two. To change what runs, change the description.
Self-healing, proven

Delete a pod and a replacement is scheduled before the old one finishes terminating.
~12 seconds to serving traffic. Nothing responded to the delete specifically, the control loop just saw 1 where it wanted 2.
The pieces
Pods wrap containers and are disposable. Deployments hold the replica count and manage pods.
Services give a stable address in front of changing pods, reached by DNS name (redis, not an IP). Labels and selectors are the glue, a Service fronts "anything labelled app: redis", and a label/selector mismatch is the classic beginner bug.
Autoscaling

CPU hit 92% under load, pods went 2 to 4 in seconds. Needs metrics-server, and needs pods to declare CPU requests or there's no baseline to measure.
The two Redis pods
The API is stateless, interchangeable, perfect for a Deployment. Redis holds data. A plain Deployment loses it on restart. A StatefulSet gives a stable identity (redis-store-0) and a PersistentVolumeClaim that outlives the pod, so the value survives.
Caveat: real replicated databases in Kubernetes are hard (leader election, failover). Most teams use a managed database.
Config, updates, isolation
ConfigMap injects settings as env vars. Rolling updates replace pods gradually, gated by the readiness probe, so a broken version stops the rollout instead of taking the service down.

Rollback adds a revision rather than deleting history, and history is finite. The manifest in git is the real source of truth.
By default every pod can reach every other pod. A NetworkPolicy fixes it:

Unrelated pod: timed out. API with the allowed label: connects. Same chained isolation as AWS security groups, by labels not IPs. The moment a policy selects a pod it goes deny-by-default, so forgetting to allow needed traffic breaks your own app.
Helm vs Kustomize
Helm: templated manifests with variables, versioned releases, one-command rollback, but the files aren't valid YAML alone. Kustomize: plain YAML with per-environment patches, no release history, built into kubectl. Helm for third-party software, Kustomize for your own apps. Many teams use both.
Debugging
STATUS gives the category, describe Events give the cause, logs only help once a container starts.

A deliberate bad-image break. Events name it in one line. Reading Events first is the habit beginners skip.
The one idea
Declare the desired state, a control loop keeps reality matching it. Self-healing, autoscaling, rolling updates, rollback, reboot recovery, all the same loop closing a gap.
Full build: https://github.com/vivianokose/nexaops-operations-lab/tree/main/10-kubernetes

Top comments (0)