Declarative Objects and Reconciliation
Every object you create in Kubernetes - whether a Pod, a Deployment or anything else - is stored and represented as structured data with four key parts. apiVersion and kind say which type of object this is and which version of its schema to use. metadata carries its name, namespace and the labels/annotations discussed below. spec is where you write your desired state - the part you author. The server then adds a status section itself, recording what is actually observed to be happening, plus bookkeeping fields like resourceVersion (a change counter) and UID (a permanent unique identifier that survives even if the name is reused later). The distinction between spec and status is fundamental: spec is your intent, status is the controller's observation, and reconciliation is the ongoing process of making status catch up to spec. Beyond that core shape, labels are key-value tags used to select and group objects (for example, every Pod belonging to one application might share app: checkout), annotations hold non-identifying metadata that tools or humans attach for context (they are not used for selection), ownerReferences record which object created and is responsible for cleaning up another object (this is what lets Kubernetes garbage-collect a Deployment's old Pods automatically), and finalizers are a way for a controller to say "wait, let me clean up external resources before you actually delete this." YAML is simply the file format you type this data in - the real, durable contract is the API object itself and its lifecycle inside the cluster, not the text file that happened to create it.
Analogy: Kubernetes is a thermostat, not a remote control. You declare the temperature; independent controllers measure reality and keep acting. Debugging means finding which sensor, rule or actuator prevents convergence.
Read the object as evidence
kubectl get RESOURCE NAME -n NAMESPACE -o yaml
kubectl describe RESOURCE NAME -n NAMESPACE
kubectl get events -n NAMESPACE --sort-by=.metadata.creationTimestamp
kubectl explain RESOURCE.spec
Do not memorize these as a ritual. The first command exposes desired and observed state, the second connects conditions and events, the third supplies a timeline, and the fourth checks the server's schema. Compare what the controller was asked to do with what it reports doing. Then test the narrowest hypothesis.
Scenario: An engineer repeatedly runs one-off imperative commands likekubectl createandkubectl editdirectly against the cluster to fix things quickly. Nobody can review what changed, there is no history, and when the team later tries to rebuild an identical staging environment from scratch, it comes out subtly different because the actual source of truth only ever lived inside the cluster's memory. Committing manifests to Git and applying them with server-side apply (which tracks who owns each field) gives you reviewable, reproducible intent instead of undocumented tribal knowledge.
Inspect object identity
Compare metadata.generation with a controller condition's observedGeneration; a mismatch means the controller has not processed the latest intent. Use kubectl get pod NAME -o jsonpath='{.metadata.ownerReferences}' to walk ownership and kubectl get ... --show-managed-fields when field managers fight. Deletion with a timestamp but no disappearance usually means a finalizer is waiting. Read the responsible controller before removing finalizers, because forced removal can leak cloud resources or corrupt cleanup.
Scenario: GitOps repeatedly restores a replica count after an engineer scales manually. Neither side is broken: two field owners express competing intent. Choose one owner and make the change at its source.
Tip: Labels are queryable identity; annotations are metadata. Putting a release timestamp in a selector creates a new identity boundary instead of merely recording a release.
Production reasoning
Ask four questions: Who owns this object? What dependency must become ready next? Which controller reports the blocking condition? What evidence would disprove my current theory? This prevents symptom-driven changes. Record the context, namespace, object generation, image digest and recent rollout before mutation; a recreated Pod may erase the evidence you needed.
Warning: Running is not the same as ready, healthy, durable or correct. Kubernetes status is layered. Confirm the application-level outcome as well as the object state.
Goal: Put this model into practice in the Kubernetes lab labels-selectors. Open/labs/kubernetes, chooselabels-selectors, predict the failure path before changing anything, then use the simulator'scheckcommand to validate the finished state.
Deliberate practice
Before the lab, write the expected object relationship and the first three commands you will run. Afterward, explain why the fix converged and name one tempting change that would only mask the symptom. Repeat using an explicit namespace and a structured output format. This prediction-observation-explanation loop is what turns command familiarity into production judgment.
45-minute investigation
- Map (5 min): draw the owner-to-child chain and mark every namespace, selector, identity and dependency involved.
- Predict (5 min): write one expected status condition, one likely event and one log or metric signal before opening the lab.
- Observe (10 min): collect YAML, describe output and ordered events. Do not mutate state. Record which observation disproves your first theory.
- Repair (15 min): make the smallest declarative correction, watch the responsible controller converge, and verify the user-facing path rather than stopping at
Running. - Stress (10 min): change one relevant constraint - replica count, label, readiness, resource value or placement rule - predict the outcome, observe it, then restore the known-good declaration.
Tip: Keep a short incident note with symptom, evidence, hypothesis, change, result. Across four sections this produces a reusable runbook instead of a pile of remembered commands.