kubectl and the API
Topic 04

kubectl and the API

APITooling

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.

The shape of every object
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.

VerbWhat it does
get / describeList objects / show details and recent events
applyDeclaratively submit desired state from a manifest
logs / execRead container output / run a command inside a container
explainPrint 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.

apply vs edit vs create

<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.

Common Mistakes
  • Running a destructive command against the wrong context — always confirm current-context before touching production.
  • Using kubectl edit or ad-hoc scale on live objects so the cluster drifts from anything in Git.
  • Skipping describe and the object's events when debugging, then guessing at causes the events already explain.
  • Confusing spec and status — trying to set status, or reading spec and assuming it reflects reality.
  • Relying on create in automation, which fails on re-run, instead of the idempotent apply.
Best Practices
  • Manage workloads declaratively: keep manifests in Git and apply them, ideally through GitOps.
  • Name and verify contexts; make the production context visibly distinct to avoid fat-finger disasters.
  • Reach for kubectl explain and api-resources instead of memorizing fields.
  • Debug describe-first: describe the 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.
RelatedThe Kubernetes API — the single surface every controller, kubelet, and tool sharesCloud CLIs (aws/gcloud/az) — the imperative analog for cloud resourcesclient-go / client libraries — programmatic access to the same API

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