What Kubernetes is
The problem it solves, in plain terms.
You wrote an app and packaged it into a container. A container is your program bundled together with everything it needs to run, sealed up so it behaves the same on any machine. Running one copy on your laptop is a single command. The hard part shows up when one copy isn't enough. Real traffic means running many copies across many machines, restarting the ones that crash, shifting work off a server that dies, and rolling out new versions without going dark. By hand, that's a job that never sleeps. Kubernetes does it for you.
The one idea that makes it click
Start with a thermostat on the wall. You don't flip the heater on and off all day. You set a target, say 21 degrees, and the thermostat holds the room there. Gets cold, it turns the heat on. Warm enough, it stops. You set the goal, and the device handles the fussing. Kubernetes runs on that same idea. You don't feed it a list of steps to perform. You tell it the end state you want, like "keep three copies of my app running," and it works around the clock to make reality match. A copy dies? It starts a fresh one, without being asked. It's just keeping the promise you set.
Here's another way to see it. Old-school automation is like turn-by-turn driving directions: turn left here, straight for two miles, right at the light. Close one road and the whole plan falls apart. Kubernetes is more like a map app on your phone: you hand it the destination, and it works out the route, reroutes around a jam, and still gets you there. You care about where you're going; the tool sweats the turns.
People call this a container orchestrator, which is a fancy way of saying it runs, connects, heals, and updates lots of containers for you. You'll also see the name written K8s: the letter K, then eight letters, then s. It grew out of Google's experience running containers at huge scale, and it behaves the same everywhere. The same app description runs on your laptop, in a company data center, or on Amazon, Google, or Microsoft's cloud, so you aren't locked to one vendor. Learn it once and the skills carry over wherever you work.
Where your app actually runs
A busy restaurant kitchen runs on a split of labor. The head chef calls out orders and tracks what's done; the line cooks do the actual cooking. Kubernetes is built the same way, and it doesn't live on a single computer. It ties a group of computers together so they act like one large machine, and that group is called a cluster. Each machine in it is a node, just the word for one computer in the group. Most nodes are workers, the line cooks: they run your app. One machine is the control plane, the head chef. Think of it as the brain: it makes the decisions and remembers what you asked for. You rarely log into a worker yourself; you talk to the brain, and it hands work to the workers.
Talking to the cluster with kubectl
You talk to that brain with a command-line tool called kubectl. A command-line tool is a program you drive by typing text commands instead of clicking buttons. People say "kube-control" or "kube-cuttle," and no one really agrees. Every kubectl command is a request you send to the cluster from your own machine. A good first one lists the machines the cluster is made of.
$ kubectl get nodesNAME STATUS ROLES AGE VERSIONkind-control-plane Ready control-plane 9m v1.31.0kind-worker Ready <none> 8m v1.31.0kind-worker2 Ready <none> 8m v1.31.0
Kubernetes is not a fancy way to run one container on your laptop. It is a control loop that keeps a whole fleet of containers matching the state you declared. You say "three copies of this app, behind this address, with this config." The cluster compares live reality to that wish and nudges things until they match. That is why operators talk about desired state instead of a checklist of start scripts.
On-call, this mental model matters more than memorizing every YAML field. When something is wrong, ask what the desired state says and what the live objects actually look like. The gap between those two views is almost always where the incident lives. Imagine an attacker already has a foothold in one pod on your production cluster: the same desired-state machinery that heals crashes will also recreate anything you delete unless you change the declaration that keeps recreating it.
Prefer reading the API with kubectl get and kubectl describe over guessing from a dashboard screenshot. The API is the source of truth the controllers read. A dashboard is a delayed sketch of that truth.
Try this
Talk to a real (or kind/minikube) cluster and prove you can see nodes and create a throwaway pod. You want Ready nodes and a Running pod so the "desired state" idea stops being abstract.
$ kubectl get nodesNAME STATUS ROLES AGE VERSIONkind-control-plane Ready control-plane 12d v1.30.0kind-worker Ready <none> 12d v1.30.0$ kubectl run demo --image=nginx:1.27 --restart=Neverpod/demo created$ kubectl get pods demo -o wideNAME READY STATUS RESTARTS AGE IP NODEdemo 1/1 Running 0 8s 10.244.1.7 kind-worker$ kubectl delete pod demopod "demo" deleted
Takeaway
Kubernetes is a desired-state engine: you declare what should be true, controllers close the gap. Verify with kubectl against the API, not against a hunch — and remember that healing will recreate whatever your declaration still asks for.
Run something right now
Enough describing it. Let's make Kubernetes run something for real. The command below starts nginx, a small and popular free web server (a program that answers requests coming from web browsers), as a quick throwaway pod named hello. A pod is the smallest unit Kubernetes runs, and it usually holds a single container: your app. When you press enter, the cluster finds a node with room, pulls down the nginx software, and starts it. A few seconds later it's serving.
$ kubectl run hello --image=nginxpod/hello created$ kubectl get podsNAME READY STATUS RESTARTS AGEhello 1/1 Running 0 12s
That one-liner is great for a quick poke around. For real work, you write the state you want into a file and apply that file. Then it lives in version control, a system that tracks every change to your files over time, so a teammate can review it and you can run it again exactly the same way later. These files are written in YAML, a plain-text format that describes settings as simple labels, values, and lists. Here's the same web server written out in full, as a pod named web.
apiVersion: v1kind: Podmetadata:name: weblabels:app: webspec:containers:- name: webimage: nginx:1.27ports:- containerPort: 80
$ kubectl apply -f pod.yamlpod/web created$ kubectl apply -f pod.yamlpod/web unchanged
Run apply a second time and Kubernetes says unchanged. It compared your file to what's already running, found no difference, and did nothing. That's the thermostat from earlier, in one word: you describe what you want, and Kubernetes only acts on the gap between that and reality.
When a pod won't start
Pods don't always come up clean, and learning to read a failure is half the job. The most common first stumble is a typo in the image tag: ask for one that doesn't exist and the pod gets stuck. kubectl get pods flags the trouble in the STATUS column, and kubectl describe pod explains why at the bottom, under a heading called Events.
$ kubectl run oops --image=nginx:1.99pod/oops created$ kubectl get podsNAME READY STATUS RESTARTS AGEoops 0/1 ImagePullBackOff 0 45s$ kubectl describe pod oops...Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 45s default-scheduler Successfully assigned default/oops to kind-workerNormal Pulling 18s (x3 over 45s) kubelet Pulling image "nginx:1.99"Warning Failed 17s (x3 over 44s) kubelet Failed to pull image "nginx:1.99": manifest not foundWarning Failed 17s (x3 over 44s) kubelet Error: ErrImagePullNormal BackOff 4s (x4 over 44s) kubelet Back-off pulling image "nginx:1.99"
Read the Events from top to bottom like a short story. The scheduler picked a node, the kubelet on that node tried to pull nginx:1.99, and there is no such tag, so it keeps backing off and retrying. That status, ImagePullBackOff, almost always means a wrong image name or tag. Fix it to a real one like nginx:1.27 and the same pod comes up Running.