Kubernetes Essentials
A quick reference for the kubectl commands and concepts from the Kubernetes Essentials training. By Konrad Mosoń. Commands up front; concepts, tables and YAML reference below.
Contexts & namespaces
kubectl config get-contexts
kubectl config current-context
kubectl config use-context <ctx>
Pin a default namespace to the active context:
kubectl config set-context --current \
--namespace=my-namespace
Use a different kubeconfig without editing the default one:
export KUBECONFIG=~/.kube/config-another
kubectl get pods
kubectl get — inspect
kubectl get pods
kubectl get pods -n kube-system
kubectl get pods -A # all namespaces
kubectl get pods -o wide # +IP, +node
kubectl get pod <name> -o yaml # full spec
kubectl get pods -w # --watch: stream changes
kubectl get pods -l app=myapp # filter by label
kubectl get pods \
--field-selector spec.nodeName=node-03
describe & events
kubectl describe pod <name>
kubectl describe node <name>
kubectl describe pvc <name>
The bottom of describe shows Events — the single most useful
thing when a pod is stuck.
Cluster-wide event stream, newest last:
kubectl get events \
--sort-by=.metadata.creationTimestamp
Apply & delete
kubectl apply -f deployment.yaml
kubectl apply --server-side -f app.yaml
kubectl delete -f deployment.yaml
kubectl delete pod <name>
kubectl delete pod -l app=myapp # by label
apply is declarative and keeps history; prefer it over create/replace
for anything that lives in Git. Deleting a Deployment cascades to its
ReplicaSets and Pods — same for StatefulSets, DaemonSets, Jobs.
exec & logs
kubectl exec -it <pod> -- /bin/sh
kubectl exec <pod> -c <container> -- ls /app
If /bin/bash is missing, fall back to /bin/sh, then busybox.
Distroless images may have neither — use an ephemeral debug container
(kubectl debug) instead.
kubectl logs <pod>
kubectl logs <pod> -c <container>
kubectl logs <pod> -p # previous crash
kubectl logs <pod> -f # follow
kubectl logs <pod> --tail=100
port-forward
Reach a pod or service from localhost without exposing it:
kubectl port-forward pod/<name> 8080:80
kubectl port-forward svc/<name> 5432:5432
kubectl port-forward deploy/<name> 3000:3000
Great for hitting an internal dashboard, a DB, or an API while debugging — no Ingress, no LoadBalancer.
PVCs — manage
kubectl get pvc
kubectl describe pvc <name>
kubectl get pv # cross-check binding
kubectl get volumeattachments -A # multi-attach debug
Resize (StorageClass must have allowVolumeExpansion: true):
kubectl patch pvc <name> -p \
'{"spec":{"resources":{"requests":{"storage":"5Gi"}}}}'
Some drivers need the pod restarted to grow the filesystem.
Delete — outcome depends on the PV's persistentVolumeReclaimPolicy:
Delete— backing volume is destroyed (data gone).Retain— PV stays asReleased; reclaim manually.
kubectl delete pvc <name>
Pod status cheats
| Status | Means |
|---|---|
Running |
up, container(s) started |
Pending |
not yet scheduled (no node fits, image pulling) |
ContainerCreating |
scheduled, pulling/mounting |
CrashLoopBackOff |
container keeps exiting |
ImagePullBackOff |
image can't be pulled (typo, auth, registry down) |
OOMKilled |
killed by kernel — exceeded memory limit |
RunContainerError |
runtime refused to start the container |
Terminating |
being deleted (waiting for grace period) |
Troubleshooting flow
When a pod misbehaves, walk this list — top to bottom:
kubectl get pods -o wide— status, node, restarts at a glancekubectl describe pod <name>— spec + recent eventskubectl logs <name> -p— logs from the previous crashed containerkubectl exec -it <name> -- sh— poke inside if it stays up long enoughkubectl get events --sort-by=.metadata.creationTimestamp— cluster contextkubectl describe node <node>— pressure, taints, allocatable
This pair (describe + logs -p) covers ~80% of real-world failures.
Health checks — probe kinds
- Liveness — container is wedged; restart it.
- Readiness — not ready for traffic; remove from Service endpoints.
- Startup — slow boot; disables liveness/readiness until it passes.
Startup probes save slow-booting apps (JVMs, migrations) from being killed mid-init by an over-eager liveness probe.
Health checks — executors
HTTP — runs inside the pod's network namespace:
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
TCP — runs from the node, not the pod:
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
Exec — arbitrary command, must exit 0:
readinessProbe:
exec:
command: [cat, /tmp/healthy]
initialDelaySeconds: 5
periodSeconds: 5
Probe tuning options
| Option | Default | Meaning |
|---|---|---|
initialDelaySeconds |
0 |
wait before first probe (ignored if < periodSeconds) |
periodSeconds |
10 |
how often to probe (min 1) |
timeoutSeconds |
1 |
probe timeout (min 1) |
successThreshold |
1 |
consecutive OKs to flip back to healthy (must be 1 for liveness/startup) |
failureThreshold |
3 |
consecutive fails before unhealthy |
terminationGracePeriodSeconds |
pod's | grace before force-kill on liveness fail |
Requests vs limits
| Field | What it does | When it bites |
|---|---|---|
requests |
scheduler reservation | bad fit → Pending |
limits |
runtime cap (kubelet) | CPU → throttle, memory → OOMKilled |
The scheduler only looks at requests. Overcommit (sum of limits > node capacity) is allowed as long as requests fit. CPU limits throttle silently; memory limits kill loudly.
resources:
requests: { cpu: "250m", memory: "128Mi" }
limits: { cpu: "1", memory: "512Mi" }
For predictable latency-sensitive workloads, set
memory request == memory limit (Guaranteed QoS class).
Patch a live Deployment:
kubectl set resources deployment myapp \
--requests=cpu=250m,memory=256Mi \
--limits=cpu=500m,memory=512Mi
OOMKilled — quick diagnosis
State: Terminated
Reason: OOMKilled
Triggered when a container's RSS crosses its memory limit.
Common causes: leak, traffic spike, JVM heap > limit, off-heap
buffers ignored.
Reproduce on demand:
apiVersion: v1
kind: Pod
metadata: { name: oom-demo }
spec:
containers:
- name: hog
image: polinux/stress
command: ["stress", "--vm", "1",
"--vm-bytes", "512M", "--vm-hang", "1"]
resources:
limits: { memory: "200Mi" }
Then: kubectl describe pod oom-demo.
Client- vs server-side apply
| Client-side | --server-side |
|
|---|---|---|
| Merge runs in | kubectl (local) |
API server |
| Field ownership | not tracked | managedFields |
| Conflicts | basic | fine-grained |
| Annotation | last-applied-configuration |
none |
| Best for | quick manual ops | GitOps, controllers |
Use server-side when multiple actors (CI, operators, humans) edit the same resource — the API server enforces who owns which field.
PVCs — what they are
A PVC is a pod's request for storage; the cluster binds it to a matching PV. The pod refers to the PVC, never the PV directly.
spec:
containers:
- image: nginx:latest
volumeMounts:
- { name: data, mountPath: /usr/share/nginx/html }
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc
Statuses:
Bound— linked to a PV, ready to mount.Pending— no PV fits (size, access mode, storageClass).Lost— the bound PV vanished; manual cleanup likely.
StatefulSet volume templates
StatefulSets mint a PVC per replica using volumeClaimTemplates:
volumeClaimTemplates:
- metadata: { name: data }
spec:
accessModes: [ ReadWriteOnce ]
storageClassName: fast-ssd
resources:
requests:
storage: 5Gi
PVCs survive pod deletion; deleting the StatefulSet does not delete the PVCs — that's a deliberate safety net for stateful data.
Access modes:
RWO— one node read/write (most block storage)RWX— many nodes read/write (NFS, CephFS)ROX— many nodes read-only