Skip to main content
PProfileMatch

Kubernetes Architecture and Pods Explained for Beginners

DevOpsLesson 1 of 45 min read

What you'll learn

  • Name the control plane and node components and what each one does
  • Explain the declarative model and the reconciliation loop
  • Write, apply and inspect a Pod manifest
  • Describe how multiple containers in one Pod share networking and volumes

On this page

  1. The cluster: a brain and a fleet of workers
  2. Declarative state and the reconciliation loop
  3. The Pod: the smallest thing you can run
  4. Two containers, one Pod: sharing a volume
  5. Common mistakes and the symptoms they cause
  6. How this shows up in real projects and interviews

Try this on any practice cluster. Create a single Pod, then delete it. It vanishes and stays gone, which feels odd for a system whose reputation is keeping things running. Now do the same to a Pod owned by a Deployment and a replacement appears within seconds. The difference between those two outcomes is the entire architecture of Kubernetes in miniature, and understanding it will save you hours of confusion.

The cluster: a brain and a fleet of workers

A Kubernetes cluster has two halves. The control plane makes decisions. The worker nodes (machines, virtual or physical) run your containers. You never tell Kubernetes "start this container on that machine". You tell it what you want to be true, and the control plane works out how.

ComponentWhereJob
kube-apiserverControl planeThe front door. Every kubectl command, controller and kubelet talks to it over a REST API
etcdControl planeA consistent key-value store holding the cluster's desired and current state
kube-schedulerControl planePicks a node for each new Pod based on resource requests, constraints and spread rules
kube-controller-managerControl planeRuns controllers: loops that compare desired state with reality and act to close the gap
kubeletEvery nodeStarts and watches the containers for Pods assigned to its node, and reports status back
Container runtimeEvery nodeActually runs the containers (containerd or CRI-O are common)
kube-proxyEvery nodePrograms network rules so traffic to a Service reaches the right Pods

Managed offerings such as EKS, GKE and AKS run the control plane for you. You still see and reason about the same objects. Locally, tools like kind and minikube squeeze everything into one machine so you can practise.

Declarative state and the reconciliation loop

You submit a manifest describing desired state: "one Pod named tide-board running this image". The API server validates it and stores it in etcd. Controllers and the scheduler watch the API for changes. The scheduler notices an unassigned Pod and binds it to a node. That node's kubelet notices a Pod assigned to it and asks the runtime to start the containers. Throughout, controllers keep comparing what exists against what you asked for. This compare-and-correct cycle is called reconciliation, and it is why Kubernetes can heal itself: if reality drifts, a loop notices and fixes it.

Life of a kubectl apply
kubectl apply -f pod.yaml
    |
    v
 kube-apiserver --> validate --> store in etcd
    |
    +--> kube-scheduler: "Pod has no node" -> picks node-2
    |
    +--> kubelet on node-2: "new Pod for me"
            |
            +--> runtime pulls image, starts containers
            +--> kubelet reports status back to apiserver

The Pod: the smallest thing you can run

Kubernetes does not schedule bare containers. It schedules Pods, a wrapper around one or more containers that always land on the same node and share a network identity (one IP address, one set of ports) and optionally storage volumes. Most Pods hold a single container. A second container is justified only when the two are tightly coupled, such as a log shipper or a helper that refreshes a file the main container serves. Here is a minimal manifest for a harbour tide-board service.

pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: tide-board
  labels:
    app: tide-board
spec:
  containers:
    - name: web
      image: nginx:1.27-alpine
      ports:
        - containerPort: 80
Terminal
kubectl apply -f pod.yaml
kubectl get pods
kubectl describe pod tide-board
kubectl logs tide-board
kubectl delete pod tide-board

Output

pod/tide-board created

NAME         READY   STATUS    RESTARTS   AGE
tide-board   1/1     Running   0          20s

The READY 1/1 column means one of one containers passed its readiness check. describe is the command you will use most when debugging, since its Events section tells the story of what the scheduler and kubelet did. The AGE value and any generated names differ on your cluster.

Two containers, one Pod: sharing a volume

Containers in the same Pod share a network namespace, so they reach each other on localhost, and they can mount the same volume. The next manifest runs a small writer that refreshes a timestamp file every 30 seconds, and an nginx container that serves that directory read-only. The emptyDir volume lives as long as the Pod does.

tide-board-pair.yaml
apiVersion: v1
kind: Pod
metadata:
  name: tide-board-pair
spec:
  volumes:
    - name: board
      emptyDir: {}
  containers:
    - name: writer
      image: busybox:1.36
      command: ["sh", "-c", "while true; do date +%H:%M > /data/updated.txt; sleep 30; done"]
      volumeMounts:
        - name: board
          mountPath: /data
    - name: web
      image: nginx:1.27-alpine
      ports:
        - containerPort: 80
      volumeMounts:
        - name: board
          mountPath: /usr/share/nginx/html
          readOnly: true
Terminal
kubectl apply -f tide-board-pair.yaml
kubectl port-forward pod/tide-board-pair 8080:80 &
curl http://localhost:8080/updated.txt
kubectl logs tide-board-pair -c web

Output

14:05

The time printed is simply the current time on your node. Note -c web: when a Pod has several containers you must say which one's logs you want. port-forward tunnels a local port to the Pod and is a debugging tool, not a way to expose an app.

Common mistakes and the symptoms they cause

  • Treating Pods as permanent. Symptom: you hand-edit a running Pod, it is rescheduled, and your change is gone; Pod IP addresses you hard-coded stop working. Fix: change the manifest, and talk to Services, never to Pod IPs.
  • Wrong `apiVersion` or `kind`. Symptom: error: unable to recognize "pod.yaml": no matches for kind followed by a kind and version. Fix: check kubectl api-resources and kubectl explain pod.
  • YAML indentation errors. Symptom: error converting YAML to JSON with a line number, or unknown field warnings for misplaced keys. Fix: use spaces, align list items, and run kubectl apply --dry-run=client -f file.yaml.
  • Looking in the wrong namespace. Symptom: No resources found in default namespace although you just deployed. Fix: add -n <namespace> or -A.
  • Forgetting `-c` on multi-container Pods. Symptom: logs show a message about choosing a container, or you read the wrong container's output.

How this shows up in real projects and interviews

Interviewers love "what happens when you run kubectl apply?" because it exposes whether you know the API server, etcd, scheduler and kubelet roles. Expect follow-ups such as "why would you put two containers in one Pod?" (tight coupling, shared lifecycle, shared network) and "what happens if a node dies?" On a team you will rarely write bare Pods, but you will read Pod specs constantly, because every higher-level object contains one inside its template.

Try it yourself

On a local cluster, apply the single-container Pod above and use kubectl get, describe and logs on it. Then change the image tag in the manifest to a version that does not exist, apply it as a new Pod name, and read the Events section of describe to see what the kubelet reports.

Show a hint

Use kubectl get pod <name> -o wide to see which node it landed on, and kubectl explain pod.spec.containers to browse available fields.

Key takeaways

  • The control plane (API server, etcd, scheduler, controllers) decides; kubelets on nodes execute.
  • You declare desired state and controllers continuously reconcile reality toward it.
  • A Pod is one or more containers sharing a network identity and optional volumes, scheduled together on one node.
  • Bare Pods are not replaced when they die; use higher-level controllers for real workloads.
  • kubectl describe and its Events section are your first stop when something is wrong.

Test yourself

A trick from this lesson's ride: Each component has one verb: API stores, scheduler picks, kubelet runs. Four more tricks like it wait along the route, each followed by a question.

Ride this lesson

Frequently asked questions

What is a Pod in Kubernetes?

A Pod is the smallest deployable unit in Kubernetes: a group of one or more containers that share a network identity and optional storage volumes and always run together on the same node. Most Pods contain a single application container.

What is the difference between the Kubernetes control plane and worker nodes?

The control plane (API server, etcd, scheduler, controller manager) stores cluster state and decides what should run where. Worker nodes run the actual containers through the kubelet and a container runtime, and report their status back to the control plane.

Why does my Kubernetes Pod not come back after I delete it?

A Pod created directly has no controller watching it, so nothing recreates it. Pods managed by a Deployment, StatefulSet or similar controller are replaced automatically because the controller sees the actual count dropped below the desired count.