kubectl and the API
Everything in Kubernetes is an object in a REST API, and every action — by you, by a controller, by the kubelet — is a request to one API server. kubectl is just a convenient client for that API. Internalizing this collapses a lot of apparent complexity: there is one interface, and everything else is a way of talking to it.
Once you see the cluster as an API of objects, the daily workflow becomes legible: you read objects, you declare the ones you want, and the system reconciles. The command-line ergonomics matter far less than the model underneath them.
The Object Model
Every Kubernetes object shares a shape. apiVersion and kind identify the type (for example apps/v1 and Deployment). metadata carries the name, namespace, labels, and annotations. spec is the desired state you set. status is the actual state the system reports — you read it, you do not write it. The gap between spec and status is exactly what the reconcile loop works to close.
apiVersion: apps/v1 # the type's group and version kind: Deployment # what kind of object this is metadata: name: web # who it is namespace: default spec: # desired state — you write this replicas: 3 status: # actual state — the system writes this readyReplicas: 3
Objects are grouped into API groups and versions. Core objects like Pods live in the original group (v1); others live in named groups like apps, batch, or networking.k8s.io, each versioned independently (v1, v1beta1). The version tells you how stable the contract is, and it is why upgrades sometimes require migrating a manifest from a removed beta version.
Talking to the API
A handful of kubectl verbs cover most work. get lists objects and describe shows a detailed, human-readable view including recent events. apply submits desired state from a file. logs and exec reach into running containers. explain prints the schema of any field. Each is just an HTTP call: get is a GET, apply is a PATCH, and so on.
Which cluster and identity a command uses comes from your kubeconfig — a file of clusters, users, and contexts that pair them. kubectl config current-context tells you where a command will land. This is not a detail: the most expensive kubectl mistakes are commands run against the wrong context.
| Verb | What it does |
|---|---|
get / describe | List objects / show details and recent events |
apply | Declaratively submit desired state from a manifest |
logs / exec | Read container output / run a command inside a container |
explain | Print the schema and docs for any field |
Imperative vs Declarative
You can change the cluster two ways. Imperative commands act now: kubectl create, scale, edit, delete. They are fine for exploration and emergencies. The declarative way is kubectl apply against manifests you keep in version control. apply performs a three-way merge — comparing your file, the live object, and the last-applied configuration — so it changes only what you changed and leaves fields managed by controllers alone.
For anything that outlives a debugging session, declarative wins. Manifests in Git are reviewable, reproducible, and the foundation of GitOps (covered later), where a controller continuously applies the repository to the cluster. Imperative edits, by contrast, leave no record and quietly drift from what anyone believes is deployed.
Discovering the API
The API is self-describing, which means you rarely need to leave the terminal to learn it. kubectl api-resources lists every kind the cluster knows, including ones added by extensions. kubectl api-versions lists the available group/versions. kubectl explain pod.spec.containers walks the schema field by field. On an unfamiliar cluster these three commands tell you what exists and how it is shaped faster than any documentation.
Extending the Surface
The set of kinds is not fixed. A Custom Resource Definition teaches the API server a new kind, which then behaves like any built-in object — you get, describe, and apply it the same way, and a custom controller reconciles it. This is the foundation of operators and the main way Kubernetes becomes a platform, covered in the extending-Kubernetes chapter. The point for now: the API you have been learning is the same surface that every tool, controller, and extension builds on.
<code>apply</code> — declarative, idempotent, three-way merge from a file in Git. The default for real workloads.
<code>edit</code> — opens the live object in an editor and patches it in place. Convenient, but the change exists only in the cluster — instant drift.
<code>create</code> — imperative one-shot; fails if the object already exists. Useful for quick experiments, not for managing state.
- Running a destructive command against the wrong context — always confirm
current-contextbefore touching production. - Using
kubectl editor ad-hocscaleon live objects so the cluster drifts from anything in Git. - Skipping
describeand the object's events when debugging, then guessing at causes the events already explain. - Confusing
specandstatus— trying to set status, or reading spec and assuming it reflects reality. - Relying on
createin automation, which fails on re-run, instead of the idempotentapply.
- Manage workloads declaratively: keep manifests in Git and
applythem, ideally through GitOps. - Name and verify contexts; make the production context visibly distinct to avoid fat-finger disasters.
- Reach for
kubectl explainandapi-resourcesinstead of memorizing fields. - Debug describe-first:
describethe object and read its events before changing anything. - Treat the API as the source of truth — every tool you add talks to the same surface, so keep changes flowing through it.
Knowledge Check
What is the difference between an object's spec and its status?
- spec is the desired state you set; status is the actual state the system reports and you read
- spec is a read-only field the system fills in, while status is the field you edit to make changes
- They are the same underlying field exposed under two interchangeable names
- spec applies only to Pods, while status applies only to Deployments and ReplicaSets
Why is kubectl apply preferred over kubectl edit for managing a Deployment?
- apply is declarative and idempotent from a file in Git, doing a three-way merge; edit changes the live object only and drifts from source
- edit is slower because it re-downloads the container image from the registry and restarts every single Pod in the Deployment before it saves the change
- apply works only on Pods, while edit is the only verb that can modify a Deployment or ReplicaSet
- edit cannot change the replica count, so scaling always has to go through apply instead
On an unfamiliar cluster, which command shows every object kind available, including those added by extensions?
- kubectl api-resources
- kubectl get pods --all-namespaces
- kubectl describe cluster
- kubectl logs --all
You got correct