04 · kubectl Basics¶
Not run against a live cluster
Command syntax and example output below follow documented kubectl
behavior; they were not executed against a live cluster in this
session. Actual output (Pod names, ages, IPs) will differ on your
machine.
kubectl is the command-line client that talks to the Kubernetes API
server. Nearly everything you do with Kubernetes goes through it (or a tool
built on the same API, like Helm or a CI pipeline).
General command shape¶
Examples:
kubectl get pods
kubectl get pod my-pod
kubectl describe deployment my-app
kubectl delete service my-service
kubectl apply -f manifest.yaml
The core verbs¶
| Verb | Purpose |
|---|---|
get |
List resources, or show one in brief |
describe |
Show detailed info + recent events for one resource |
create |
Imperatively create a resource (quick, one-off) |
apply |
Declaratively create/update from a YAML file (preferred) |
delete |
Remove a resource |
logs |
Print container logs |
exec |
Run a command inside a running container |
edit |
Open a resource in your editor and apply changes on save |
get: listing resources¶
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# web-7d8f9c6b7d-2xk9p 1/1 Running 0 3m
kubectl get pods -o wide # extra columns: node, IP
kubectl get pods --watch # stream changes live
kubectl get all # pods, services, deployments, etc. at once
kubectl get pods -n kube-system # in a specific namespace
kubectl get pods -A # across ALL namespaces
kubectl get pod web-7d8f9c6b7d-2xk9p -o yaml # full object as YAML
-o (--output) accepts wide, yaml, json, or a custom
-o jsonpath='{...}' for scripting.
describe: the debugging workhorse¶
Prints the full spec/status plus an Events section at the bottom — this Events list is usually the fastest way to diagnose why a Pod won't start (image pull errors, failed scheduling, crash loops, failed probes all show up there with timestamps).
logs: reading container output¶
kubectl logs web-7d8f9c6b7d-2xk9p # current logs
kubectl logs web-7d8f9c6b7d-2xk9p -f # follow (stream), like tail -f
kubectl logs web-7d8f9c6b7d-2xk9p --previous # logs from a crashed prior instance
kubectl logs web-7d8f9c6b7d-2xk9p -c sidecar # a specific container in a multi-container Pod
exec: getting a shell inside a container¶
-it allocates an interactive TTY and keeps stdin open — needed for an
interactive shell. Everything after -- is the command run inside the
container, not interpreted by kubectl itself.
apply vs create: declarative vs imperative¶
# Imperative: quick and one-off, but not repeatable/idempotent in the same way
kubectl create deployment web --image=nginx:alpine
# Declarative: write the desired state to a file, then apply it
kubectl apply -f deployment.yaml
kubectl apply -f is the standard, production-grade way to manage
resources: re-running it with an updated file updates the existing
resource to match (a three-way diff against the last-applied config), rather
than erroring because the resource already exists. This is what makes YAML
manifests checked into version control ("infrastructure as code") the
normal workflow — Module 09 covers manifest structure in depth.
kubectl apply -f deployment.yaml # apply one file
kubectl apply -f ./manifests/ # apply every file in a directory
kubectl diff -f deployment.yaml # preview what apply would change
delete¶
kubectl delete pod web-7d8f9c6b7d-2xk9p
kubectl delete -f deployment.yaml # delete everything defined in a file
kubectl delete deployment web --grace-period=0 --force # force-delete (use sparingly)
Namespaces and context¶
kubectl get pods -n staging # one-off namespace override
kubectl config set-context --current --namespace=staging # change the default
Module 09 covers namespaces themselves; for now, know that most kubectl
commands default to the default namespace unless you say otherwise.
Shortcuts worth knowing immediately¶
kubectl get po # "po" = pods
kubectl get svc # "svc" = services
kubectl get deploy # "deploy" = deployments
kubectl get cm # "cm" = configmaps
kubectl get ns # "ns" = namespaces
kubectl api-resources # full list of resource types and their shortnames
Worked example: full inspect-and-fix loop¶
A typical debugging session, chaining the verbs above:
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# web-6c9d8f5b6d-h8k2p 0/1 ImagePullBackOff 0 45s
kubectl describe pod web-6c9d8f5b6d-h8k2p
# ... Events:
# Warning Failed kubelet Failed to pull image "nginx:alpin":
# not found: manifest unknown
# Found it -- typo in the image tag. Fix the manifest, then:
kubectl apply -f deployment.yaml
kubectl get pods --watch
# NAME READY STATUS RESTARTS AGE
# web-7d8f9c6b7d-2xk9p 1/1 Running 0 8s
How It Actually Works¶
Every kubectl command is a thin REST/JSON client — understanding the
request it actually sends demystifies most "why didn't that work" moments:
get/describe/deleteare HTTP verbs against a REST resource path.kubectl get pods -n fooperformsGET /api/v1/namespaces/foo/pods;kubectl describedoes the equivalentGETon the object plus a separateGETfor related Events (/api/v1/namespaces/foo/events?fieldSelector=involvedObject.name=...) and stitches them together client-side — there is no single "describe" API, it's kubectl composing multiple calls into readable text.applycomputes a three-way merge patch, not an overwrite.kubectl applystores the last-applied configuration as JSON in thekubectl.kubernetes.io/last-applied-configurationannotation on the live object. On the nextapply, it diffs three things — the last-applied config, your new local file, and the current live object on the server — to figure out which fields you intentionally removed (present in last-applied, absent in new file) versus fields something else changed that you should leave alone (present in live object but never mentioned by you).create, by contrast, is a simplePOSTthat fails outright if the object already exists.logsandexecdon't go through etcd at all. These commands have the API server open a streaming connection (an upgraded HTTP/SPDY or WebSocket connection) directly to the kubelet on the node hosting the Pod, which in turn asks the container runtime (via CRI'sExec/Attachor the container's log file on disk) to stream the output back. This is whylogs/execfail with a distinct "error dialing backend" class of error when the node is unreachable, even thoughkubectl get podfor that same Pod succeeds (get only needs etcd/API server, not the node).--watchkeeps the same long-lived watch connection described in Module 01 rather than polling — this is why watched output appears event-by-event with no fixed delay, and why killing the network briefly causes kubectl to silently reconnect and resync via a freshList+Watchrather than losing events.
Exercise¶
Against your local cluster from Module 03: create a Deployment imperatively
(kubectl create deployment demo --image=nginx:alpine), then practice
get, describe, logs, and exec -it ... -- /bin/sh (try ls /, env,
and exit inside the shell) against the resulting Pod. Finally delete it
with kubectl delete deployment demo and confirm with kubectl get pods
that it's gone.