Overview
Kubernetes takes the container model explained in Docker Fundamentals and adds a control plane that keeps workloads running, healthy, and reachable across a fleet of machines. You declare a desired state through the Kubernetes API; controllers continuously compare that declaration with the cluster's observed state and reconcile the difference. A Deployment does not "run a container" directly: it manages ReplicaSets, which manage disposable Pods. A Service does not own those Pods: it selects Ready Pods by label and gives that changing backend set a stable network identity.
That ownership chain is the essential mental model. When something fails, diagnose the layer that owns the symptom instead of randomly restarting resources: the Deployment for rollout state, the Pod and its Events for scheduling or container failures, logs for application behavior, EndpointSlices for Service backends, and nodes or metrics for resource pressure. The official Kubernetes learning path follows the same practical loop: deploy, inspect, expose, scale, update, and debug. This guide keeps those tasks together.
What this guide covers
This is the operational foundation: core objects, manifests, rollouts, probes, resource controls, and first-response troubleshooting. Continue into Kubernetes Security for RBAC, Pod Security Standards, and NetworkPolicy; GitOps Principles for production change management; and the Kubernetes OOMKilled troubleshooting guide for a complete memory incident runbook.
Quick Reference
| Object | Purpose | What to inspect first |
|---|---|---|
| Pod | Smallest deployable unit; one or more co-located containers | phase, container states, readiness, restarts, Events |
| Deployment | Declares replicas and rollout strategy for stateless Pods | available replicas, conditions, rollout history |
| StatefulSet | Gives stateful Pods stable identity and ordered lifecycle | replicas, persistent volumes, ordered rollout |
| DaemonSet | Runs a Pod on every eligible node | desired vs available Pods and node placement |
| Job / CronJob | Runs finite or scheduled work | completions, failed Pods, schedule and history |
| Service | Stable IP and DNS name for selected Ready Pods | selector, ports, EndpointSlices |
| ConfigMap / Secret | Supplies externalized configuration or sensitive values | mounted/injected keys and workload references |
| Namespace | Scopes names, policy, and access; not a security boundary alone | active namespace, quotas, policies |
The commands below are grouped by intent, not alphabetically. Confirm the active context and namespace before every production investigation: a correct command against the wrong cluster is still a dangerous command.
| Command | Description | Copy |
|---|---|---|
kubectl config current-context | Print the cluster context kubectl will use. | |
kubectl config get-contexts | List configured contexts, clusters, users, and default namespaces. | |
kubectl config use-context <name> | Switch to another configured context. | |
kubectl config set-context --current --namespace=<namespace> | Set the default namespace on the current context. | |
kubectl api-resources | List resource kinds and whether they are namespaced. | |
kubectl cluster-info | Show control-plane and core service addresses. |
| Command | Description | Copy |
|---|---|---|
kubectl apply -f deployment.yaml | Create or update resources declared in a manifest file. | |
kubectl diff -f deployment.yaml | Preview how a manifest differs from live cluster state. | |
kubectl get deployments | List Deployments and their ready/up-to-date/available replica counts. | |
kubectl describe deployment/<name> | Show rollout conditions, ReplicaSets, and related Events. | |
kubectl rollout status deployment/<name> | Watch a Deployment rollout until it finishes. | |
kubectl rollout history deployment/<name> | List recorded Deployment revisions. | |
kubectl rollout undo deployment/<name> | Roll a Deployment back to its previous revision. | |
kubectl scale deployment/<name> --replicas=5 | Change the desired replica count directly, without editing the manifest. | |
kubectl wait --for=condition=available deployment/<name> --timeout=120s | Block until a Deployment reports Available or the timeout expires. |
| Command | Description | Copy |
|---|---|---|
kubectl get pods -o wide | List Pods with readiness, restarts, Pod IPs, and assigned nodes. | |
kubectl get pods -l app=<label> | List only Pods matching a label selector. | |
kubectl describe pod <name> | Show container state, probes, resources, owner, node, and recent Events. | |
kubectl logs <pod> -c <container> -f | Stream logs for one container in a Pod. | |
kubectl logs <pod> -c <container> --previous | Read logs from the previous crashed container instance. | |
kubectl exec -it <pod> -c <container> -- sh | Open a shell when the application image contains one. | |
kubectl debug <pod> -it --image=busybox:1.36 | Add an ephemeral debug container when the app image lacks troubleshooting tools. |
| Command | Description | Copy |
|---|---|---|
kubectl get svc | List Services and their cluster IPs and ports. | |
kubectl describe svc <name> | Show a Service's selector, endpoints, and port mappings. | |
kubectl get endpointslices -l kubernetes.io/service-name=<service> | Show the Ready backend addresses discovered for a Service. | |
kubectl get pods -l <key>=<value> --show-labels | Verify that a Service selector matches the intended Pods. | |
kubectl port-forward svc/<name> 8080:80 | Forward a local port to a Service, for local debugging without exposing it publicly. | |
kubectl run -it --rm net-debug --restart=Never --image=busybox:1.36 -- sh | Start a temporary in-cluster shell for DNS and connectivity tests. |
| Command | Description | Copy |
|---|---|---|
kubectl get configmaps | List ConfigMaps in the current namespace. | |
kubectl get secrets | List Secrets in the current namespace (values are not shown in plaintext). | |
kubectl top pods --containers | Show live CPU and memory usage by container; requires the Metrics API. | |
kubectl top nodes | Show live CPU and memory usage by node; requires the Metrics API. | |
kubectl describe resourcequota | Show namespace resource quotas and current usage. | |
kubectl describe limitrange | Show namespace defaults and constraints for requests and limits. |
| Command | Description | Copy |
|---|---|---|
kubectl get events --sort-by=.metadata.creationTimestamp | List namespace Events in creation order to expose scheduling, pull, mount, and probe failures. | |
kubectl get pods -A | List Pods across every namespace. | |
kubectl get nodes -o wide | List node readiness, versions, internal addresses, and OS details. | |
kubectl describe node <name> | Show node conditions, allocatable resources, running Pods, and pressure Events. | |
kubectl get pod <name> -o yaml | Inspect the API server's complete live representation of a Pod. | |
kubectl auth can-i <verb> <resource> -n <namespace> | Check whether the current identity is authorized for an action. |
Syntax
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app.kubernetes.io/name: api
template:
metadata:
labels:
app.kubernetes.io/name: api
spec:
containers:
- name: api
image: registry.example.com/api:1.4.0
ports:
- name: http
containerPort: 8080
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
startupProbe:
httpGet:
path: /health/startup
port: http
periodSeconds: 5
failureThreshold: 30
readinessProbe:
httpGet:
path: /health/ready
port: http
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health/live
port: http
periodSeconds: 10
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app.kubernetes.io/name: api
ports:
- name: http
port: 80
targetPort: httpThe Deployment selector and Pod-template labels must match; the Service independently uses the same label to discover backends. Resource requests influence scheduling, while limits are runtime ceilings. Readiness removes an unhealthy Pod from Service traffic, liveness restarts a stuck container, and startup prevents those checks from interfering with slow initialization.
Examples
Deploy and inspect
# Confirm the target before changing anything
kubectl config current-context
kubectl config set-context --current --namespace=production
# Preview and apply the desired state
kubectl diff -f deployment.yaml
kubectl apply -f deployment.yaml
kubectl wait --for=condition=available deployment/api --timeout=120s
kubectl get pods -l app.kubernetes.io/name=api -o wide
kubectl get endpointslices -l kubernetes.io/service-name=apiUpdate and roll back
kubectl set image deployment/api api=registry.example.com/api:1.5.0
kubectl rollout status deployment/api
kubectl rollout history deployment/api
# If the new revision is unhealthy:
kubectl rollout undo deployment/api
kubectl rollout status deployment/api// Inside any Pod in the cluster, the Service's DNS name resolves
// automatically - no hardcoded Pod IPs, no service registry to run.
const response = await fetch('http://api.production.svc.cluster.local/health/ready')kubectl port-forward svc/api 8080:80Test the Service locally without creating a public LoadBalancer or Ingress. This narrows the problem to the Service and its backends before external routing is involved.
curl http://localhost:8080/health/ready
Troubleshooting Workflow
The official Kubernetes troubleshooting guides start with triage: decide whether the failure is in the workload, Pod, Service, or cluster. Preserve evidence before deleting or restarting anything; recreating a Pod can remove the Events and previous-container state that explain the failure.
Step 1: Confirm cluster, namespace, and desired state
I run kubectl config current-context, check the namespace, then inspect the owning Deployment with kubectl get deployment/<name> -o wide and kubectl rollout status deployment/<name>. A rollout problem is not automatically an application problem: the controller may be unable to create, schedule, pull, or ready the new Pods.
Step 2: Read Pod status, container state, and Events
I use kubectl get pods -o wide and kubectl describe pod <name>. The Pod phase is only a summary; the container State, Last State, Reason, readiness, restart count, and Events carry the diagnosis.
| Symptom | Usually means | Check next |
|---|---|---|
Pending | no suitable node, unbound storage, or admission/scheduling constraint | Pod Events, requests, taints, affinity, PVCs |
ImagePullBackOff | image name, tag, registry access, or pull credentials failed | Pod Events and imagePullSecrets |
CrashLoopBackOff | a container repeatedly exits and Kubernetes is delaying restarts | current/previous logs, exit code, command, config, probes |
Running but 0/1 Ready | process started but readiness is failing | probe Events, endpoint behavior, dependencies |
OOMKilled / exit 137 | memory limit or node-pressure investigation required | previous logs, limits, metrics, node conditions |
Step 3: Read the right logs
I use kubectl logs <pod> -c <container> for the current instance and add --previous after a restart. Always name the container in multi-container Pods. For memory failures, follow the complete Kubernetes OOMKilled troubleshooting guide before simply increasing the limit.
Step 4: Verify Service discovery from selector to backend
I compare the Service selector with Pod labels, then inspect its EndpointSlices. If no endpoints
appear, Kubernetes found no matching Ready Pods. If endpoints exist, test the Pod port, Service
IP, and DNS name from another Pod. I keep port (the Service port) distinct from targetPort (the
backend Pod port).
kubectl get svc api -o yaml
kubectl get pods -l app.kubernetes.io/name=api --show-labels
kubectl get endpointslices -l kubernetes.io/service-name=api
kubectl run -it --rm net-debug --restart=Never --image=busybox:1.36 -- sh
# From the temporary in-cluster shell:
nslookup api
wget -qO- http://api/health/readyStep 5: Check probes and resources
A readiness failure should stop traffic without restarting the container. A liveness failure restarts it; an overly strict liveness check can therefore create a cascading failure under load. I use a startup probe for slow initialization. I compare actual CPU/memory with requests and limits, and inspect node conditions when Pods are Pending, evicted, or repeatedly killed.
Step 6: Use a debug container when the image is minimal
Production images often omit shells and network tools. I use kubectl debug to add an ephemeral
troubleshooting container or create a modified copy of the Pod instead of weakening the production
image just to make incidents easier.
Escalate by layer
If Pods cannot be scheduled, investigate requests, storage, affinity, taints, and nodes. If Pods run but are not Ready, investigate the application and probes. If Pods are Ready but the Service has no endpoints, investigate selectors. If endpoints exist but traffic fails, investigate ports, DNS, NetworkPolicy, and the cluster network. This order prevents a networking symptom from turning into random changes across every layer.
Visual Diagram
Common Mistakes
- Editing a Pod directly (
kubectl edit pod) instead of the Deployment that manages it; the change is lost the moment that Pod is replaced. - Deleting a failing Pod before reading
describe, Events,Last State, andlogs --previous, destroying the best evidence and recreating the same failure. - Treating
Runningas equivalent to healthy; a Pod can be Running while containers restart or readiness remains false. - Checking only current logs after a restart, when the failure output belongs to the previous container instance.
- Debugging a broken Service without checking selectors and EndpointSlices, or confusing Service
portwith PodtargetPort. - Reusing one dependency-heavy endpoint for startup, readiness, and liveness, causing a dependency outage to restart every otherwise healthy application replica.
- Omitting resource requests or guessing them far below real usage, so the scheduler packs more workload onto a node than normal demand can support.
- Treating Secret values as encrypted merely because their YAML representation is base64; use access controls and an appropriate encryption or external-secret strategy.
Performance
- Resource requests drive scheduling capacity decisions, not current usage. Under-requesting can overpack nodes; over-requesting strands capacity and can leave otherwise runnable Pods Pending.
- CPU limits are enforced by throttling, while memory pressure cannot be handled the same way and may end in an OOM kill. Choose limits from measured behavior instead of copying one ratio across every workload.
- Rolling-update settings trade speed, spare capacity, and availability.
maxSurgeneeds temporary capacity for new Pods;maxUnavailabledecides how much serving capacity may disappear during the rollout. - Readiness design affects usable capacity. A probe that is too strict can remove healthy Pods during a traffic spike, increasing load on the remaining replicas and turning a small slowdown into a cascade.
- Horizontal autoscaling depends on meaningful requests and available metrics. Scaling cannot repair a memory leak, a broken dependency, or a Service with no endpoints.
Best Practices
- Confirm context and namespace before every change, and include
-n <namespace>in runbooks so commands are safe to copy under pressure. - I keep production configuration declarative and version-controlled. I use
kubectl diffbeforeapply, then let GitOps Principles guide continuous reconciliation and rollback. - I use stable, consistent labels for ownership and selection. A Deployment selector, Pod-template labels, and Service selector should be understandable as one relationship.
- Set resource requests from observed steady-state demand and choose limits from measured peaks and failure tolerance. I use the Kubernetes Resource Calculator to check replica totals.
- Give startup, readiness, and liveness separate jobs. A readiness failure controls traffic; a liveness failure should mean the process is genuinely unable to recover without a restart.
- Emit structured logs, workload metrics, and traces with correlation identifiers. Observability Fundamentals explains how to combine those signals during an incident.
- I use namespaces for scope and organization, then add RBAC, Pod Security admission, and NetworkPolicy from Kubernetes Security; namespaces alone are not a security boundary.
- Prefer minimal production images and use ephemeral debug containers when necessary, rather than shipping shells and troubleshooting packages in every application image.
Practice the whole lifecycle
I use the Kubernetes Manifest Generator to inspect a minimal Deployment and Service pair, then work through the production AKS GitOps project to apply deployment, observability, rollback, and troubleshooting in one system.