Cloud Tech
DevOpsAdvanced

Kubernetes Fundamentals

Learn Kubernetes through Pods, Deployments, Services, probes, resources, rollouts, and an official-docs-based troubleshooting workflow.

Reviewed Jul 28, 2026Victor Nwoke13 min read

Written and maintained by Victor Nwoke. Technical behavior is reviewed against the primary references listed on this page.

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

ObjectPurposeWhat to inspect first
PodSmallest deployable unit; one or more co-located containersphase, container states, readiness, restarts, Events
DeploymentDeclares replicas and rollout strategy for stateless Podsavailable replicas, conditions, rollout history
StatefulSetGives stateful Pods stable identity and ordered lifecyclereplicas, persistent volumes, ordered rollout
DaemonSetRuns a Pod on every eligible nodedesired vs available Pods and node placement
Job / CronJobRuns finite or scheduled workcompletions, failed Pods, schedule and history
ServiceStable IP and DNS name for selected Ready Podsselector, ports, EndpointSlices
ConfigMap / SecretSupplies externalized configuration or sensitive valuesmounted/injected keys and workload references
NamespaceScopes names, policy, and access; not a security boundary aloneactive 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.

CommandDescriptionCopy
kubectl config current-contextPrint the cluster context kubectl will use.
kubectl config get-contextsList 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-resourcesList resource kinds and whether they are namespaced.
kubectl cluster-infoShow control-plane and core service addresses.

Syntax

yaml
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: http

The 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

bash
# 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=api

Update and roll back

bash
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
javascript
// 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:80

Test 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.

SymptomUsually meansCheck next
Pendingno suitable node, unbound storage, or admission/scheduling constraintPod Events, requests, taints, affinity, PVCs
ImagePullBackOffimage name, tag, registry access, or pull credentials failedPod Events and imagePullSecrets
CrashLoopBackOffa container repeatedly exits and Kubernetes is delaying restartscurrent/previous logs, exit code, command, config, probes
Running but 0/1 Readyprocess started but readiness is failingprobe Events, endpoint behavior, dependencies
OOMKilled / exit 137memory limit or node-pressure investigation requiredprevious 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).

bash
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/ready

Step 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, and logs --previous, destroying the best evidence and recreating the same failure.
  • Treating Running as 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 port with Pod targetPort.
  • 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. maxSurge needs temporary capacity for new Pods; maxUnavailable decides 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 diff before apply, 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.

Interview questions

What is the difference between a Pod, a Deployment, and a Service?
A Pod is the smallest deployable unit, one or more containers that share network and storage, scheduled together onto a node. Pods are ephemeral and disposable; Kubernetes recreates them freely and their IPs change every time. A Deployment manages a set of identical Pods (a ReplicaSet under the hood), handling rolling updates, rollbacks, and keeping the desired replica count running even as individual Pods die. A Service gives that changing set of Pods a stable network identity, a fixed virtual IP and DNS name, so other things in the cluster don't need to track individual Pod IPs, which change constantly.
What does the Kubernetes control loop actually do?
Every Kubernetes controller (Deployment, ReplicaSet, etc.) runs a reconciliation loop: it continuously compares the desired state (what you declared in a manifest, stored in etcd) against the observed actual state of the cluster, and takes action to close any gap. If you declared 3 replicas and only 2 Pods are running, the ReplicaSet controller creates one more. This is the same declarative, converge-toward-desired-state model as Terraform, but running continuously and automatically rather than on-demand.
Why can't you rely on a Pod's IP address for service discovery?
Pods are ephemeral by design, Kubernetes kills and recreates them constantly (failed health checks, node drains, rolling deployments, autoscaling), and every new Pod gets a brand-new IP address. Hardcoding or caching a Pod IP breaks the moment that Pod is replaced. A Service solves this by providing a stable virtual IP and DNS name that always routes to whichever Pods currently match its label selector, regardless of how many times the underlying Pods have been replaced.
What is the difference between readiness, liveness, and startup probes?
Readiness decides whether a Pod should receive Service traffic; a failed readiness probe removes the Pod from eligible backends without restarting it. Liveness decides whether a stuck container should be restarted. A startup probe protects slow-starting containers by delaying readiness and liveness checks until startup succeeds. Reusing one strict check for all three can create restart loops or route traffic too early.
How would you troubleshoot a Service that exists but returns no response?
Work from the application outward: confirm the selected Pods are Ready and serving on the expected container port, compare the Service selector with Pod labels, inspect EndpointSlices to verify Kubernetes discovered backends, confirm port and targetPort, then test Service DNS and IP from inside the cluster. An empty EndpointSlice usually points to a selector/readiness mismatch; healthy endpoints with failed DNS or routing move the investigation to cluster networking.

References

ShareXLinkedInReddit
Was this page helpful?
Suggest an improvement