Skip to content

kubectl and Declarative YAML

kubectl is how you send desired state to a cluster and inspect what it is doing, and almost everything you send it is a YAML manifest built from the same four top-level fields.

kubectl basics: contexts, namespaces, and verbs

Section titled “kubectl basics: contexts, namespaces, and verbs”

Your kubeconfig can hold credentials for several clusters at once, grouped into contexts; switch which one your commands target with:

Terminal window
kubectl config use-context my-cluster
kubectl get pods -n my-namespace

The -n (or --namespace) flag scopes a command to one namespace — leaving it off targets whatever namespace your current context defaults to, usually default. Beyond that, a small set of verbs covers most of daily kubectl usage:

Terminal window
kubectl get pods
kubectl describe pod web-7d9f8c6b7-abcde
kubectl logs web-7d9f8c6b7-abcde
kubectl exec -it web-7d9f8c6b7-abcde -- sh

get lists objects, describe shows detailed status and recent events for one object (the first place to look when something is wrong), logs streams a container’s stdout/stderr, and exec runs a command inside a running container — handy for a quick shell to poke around.

Whatever kind of object you are creating — Pod, Deployment, Service, anything — its YAML has the same shape:

apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80

apiVersion and kind say which API group and object type you mean. metadata carries identity and bookkeeping — name, optional namespace, and the labels and annotations you attach yourself. spec is the desired state you author — everything above is spec for a Pod. Kubernetes adds a fifth field on its own, status, once the object exists: that one you never write, only read, since it reflects observed reality rather than what you asked for.

apply vs create, and labels as the gluing mechanism

Section titled “apply vs create, and labels as the gluing mechanism”

kubectl apply -f file.yaml is declarative: it diffs your file against the object’s last-applied configuration (tracked in an annotation) and patches only what changed, creating the object if it does not exist yet. That makes it safe to re-run the same command over and over, which is exactly what you want in scripts and CI. kubectl create -f file.yaml is imperative instead — it creates the object once and errors out if it already exists. This course uses apply throughout.

Terminal window
kubectl apply -f pod.yaml
kubectl create -f pod.yaml

Labels are arbitrary key-value pairs you attach to objects, like app: web in the Pod above. On their own they do nothing — their power comes from selectors, which other objects use to find the Pods labeled a certain way. A Service with selector: { app: web } routes traffic to every Pod carrying that label; a Deployment’s ReplicaSet uses a matchLabels selector to know which Pods are “its” Pods to keep at the desired replica count. This label-and-selector pattern is the fundamental way objects in Kubernetes find each other, and you will see it again in nearly every later module.

flowchart LR
  cmd["kubectl apply -f pod.yaml"] --> api["kube-apiserver"]
  api --> etcd["etcd"]
  api --> ctrl["Controllers notice the change and react"]
A declarative apply flows through the API server into etcd
What happens differently between kubectl apply and kubectl create when the target object already exists?
What does a label selector actually do?
Which four top-level fields does every Kubernetes object manifest have?
Which field on a live Kubernetes object is managed by the system and never something you author yourself?