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:

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:

  1. kubectl get pods -o wide — status, node, restarts at a glance
  2. kubectl describe pod <name> — spec + recent events
  3. kubectl logs <name> -p — logs from the previous crashed container
  4. kubectl exec -it <name> -- sh — poke inside if it stays up long enough
  5. kubectl get events --sort-by=.metadata.creationTimestamp — cluster context
  6. kubectl describe node <node> — pressure, taints, allocatable

This pair (describe + logs -p) covers ~80% of real-world failures.

Health checks — probe kinds

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:

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: