The API object model
Why everything is a resource, what the control loop actually does, and how to read any CRD you meet later without waiting for someone to document it.
Kubernetes has one idea in it, repeated. You write down what you want, a controller compares that to what exists, and it acts to close the gap. Learn the shape of that loop and every new resource you meet — from a Deployment to a CiliumNetworkPolicy to something your colleague invented last week — is recognisable on sight.
Spec is a wish. Status is a measurement.
Nearly every object has the same four parts: apiVersion and kind to say what it is, metadata to name it, spec for what you want, and status for what is true. You write spec. Controllers write status. Confusing the two is the most common beginner mistake, and the reason editing status by hand does nothing useful.
kubectl get deploy my-app -o jsonpath='{.spec.replicas}' # what you asked for
kubectl get deploy my-app -o jsonpath='{.status.readyReplicas}' # what you have
The loop, in one paragraph
A controller watches a resource type. When one changes, it is put on a work queue. The controller reads the current world, computes the difference from spec, and takes one step towards closing it — then it does that again, forever. It must be safe to run the same reconcile twice, because it will be. Nothing in the system assumes a message is delivered exactly once.
This is why Kubernetes recovers from almost anything you do to it, and also why it sometimes does nothing at all and gives you no error: the controller may simply not be watching the thing you changed.
Read the API, not the blog post
The cluster documents itself, including every CRD installed on it. Three commands cover most of what you would otherwise go searching for:
kubectl api-resources # everything this cluster knows about
kubectl explain deployment.spec.strategy --recursive
kubectl get crd # what has been added beyond core Kubernetes
kubectl explain reads the OpenAPI schema out of the API server, so it is correct for your cluster at your version, which a search result is not. It also works on custom resources the moment someone installs them.Ownership is how deletion works
A Deployment does not manage pods. It manages a ReplicaSet, which manages pods, and each child carries an ownerReferences entry pointing at its parent. Delete the parent and garbage collection removes the children.
kubectl get rs -l app=my-app -o jsonpath='{.items[*].metadata.ownerReferences[*].kind}'
# orphan the children instead of deleting them - occasionally what you want in an incident
kubectl delete deploy my-app --cascade=orphan
Labels do the wiring
There are no pointers in a Kubernetes manifest. A Service finds pods because its selector matches their labels, and nothing validates that the match succeeds. A Service with a typo in its selector is a perfectly valid object with zero endpoints.
# the real question when a Service returns nothing
kubectl get endpointslices -l kubernetes.io/service-name=my-svc
What to actually do with this
- Pick any resource in your cluster and read its
statusalongside itsspec. Notice which fields you never wrote. - Run
kubectl explainon something you thought you knew —pod.spec.securityContextis a good one. - Break a Service selector on purpose and follow it down to the EndpointSlice.
Something wrong or out of date? Open an issue — corrections are welcome and get credited.