1. Why Kubernetes at All
Docker Compose runs containers on a single machine — if that machine dies, every container on it dies with it. Kubernetes exists to answer a harder question: given a fleet of machines and a set of workloads, how do you keep those workloads running, correctly placed, and self-healing, without a human manually SSH-ing anywhere?
# You DECLARE desired state:
"I want 3 replicas of my-app running, each with these resource limits"
# Kubernetes CONTINUOUSLY RECONCILES reality toward that declaration:
- A node crashes, taking one replica down -> Kubernetes notices, schedules a
replacement on a healthy node, automatically.
- You update the image tag -> Kubernetes rolls out the change gradually,
replica by replica, without downtime.
- A Pod's health check starts failing -> Kubernetes restarts or replaces it.
# You never imperatively say "start this container on that machine."
# You declare an outcome; a set of control loops keeps making it true.
Every concept in the next eleven weeks — Deployments, Services, HPAs, GitOps — is a variation on this same idea: describe what you want, and let a controller loop keep reality matching it.
A single server running Docker Compose is genuinely the right answer for a lot of small, real projects. Kubernetes' power comes with real operational complexity — it earns its keep once you have multiple services, need genuine high availability, or are scaling a team, not by default on day one.
2. The Control Plane
The control plane is the cluster's "brain" — usually running on dedicated nodes, making global decisions about the cluster (scheduling, responding to events) without running any of your actual application workloads.
kube-apiserver # the front door. EVERYTHING talks to the cluster through
# this REST API — kubectl, controllers, even other control
# plane components. It's the only component that talks to etcd.
etcd # a distributed key-value store — the cluster's single source
# of truth. Every object's desired state lives here.
kube-scheduler # watches for newly-created Pods with no node assigned yet,
# and decides which node they should run on based on
# resource availability, constraints and affinity rules.
kube-controller-manager # runs the actual reconciliation loops — the "controllers"
# that notice drift between desired and actual state and
# act to correct it (e.g. the Deployment controller).
Notice the pattern: the API server is the only component that reads or writes
etcd directly. Every other component — the scheduler, the controllers,
even kubectl on your laptop — only ever talks to the API server, which
is what makes the whole system's access control and auditing tractable.
If etcd is lost with no backup, the cluster forgets everything it was ever told to run — every Deployment, Service and Secret definition. Production clusters treat etcd backups with the same seriousness as a production database backup, because that's exactly what it is.
3. Node Components
Every node — a worker machine, physical or virtual — runs a small set of agents that let the control plane actually run workloads on it:
kubelet # the primary agent on every node. Watches the API server for Pods
# assigned to its node, and makes sure their containers are running
# and healthy — talking to the container runtime to do so.
container runtime # containerd (or another CRI-compliant runtime) — actually pulls
# images and starts/stops containers, same as in Week 1's Docker
# architecture, just driven by kubelet instead of the Docker CLI.
kube-proxy # maintains network rules on the node so traffic to a Service's
# virtual IP gets routed to one of the correct backing Pods,
# wherever they're actually running.
Notice kubelet doesn't get told "start container X" directly by a human — it watches the API server for Pods scheduled to its node and reconciles locally, exactly the same declarative pattern as everything else in Kubernetes, just running per-node instead of cluster-wide.
kubectl apply -f deployment.yaml
-> kube-apiserver validates & writes desired state to etcd
-> kube-scheduler notices unscheduled Pods, assigns them to nodes
-> kubelet (on the assigned node) notices new Pods for it, tells containerd
to pull the image and start the container
-> kube-proxy updates routing rules so Services can reach the new Pod
Kubernetes talks to nodes through the CRI (Container Runtime Interface) — containerd, CRI-O or others all work. Modern Kubernetes doesn't require "Docker" installed anywhere in the cluster; it needs a CRI-compliant runtime, which is often containerd running underneath, the same one from Week 1's architecture diagram.
4. Your First Local Cluster
You don't need a cloud account to learn Kubernetes — Minikube and kind (Kubernetes IN Docker) both run a real, fully-functional cluster locally.
# Option A: Minikube
minikube start
kubectl get nodes
# Option B: kind
kind create cluster --name learning
kubectl cluster-info
kubectl get nodes # list every node in the cluster
kubectl get pods -A # list every Pod in every namespace ("-A" = all namespaces)
kubectl cluster-info # confirm you're actually talking to the cluster
kubectl version # client and server (control plane) version
kubectl config current-context # which cluster kubectl is currently pointed at
That last command matters more than it looks — kubectl can be pointed
at any cluster you have credentials for, local or cloud. Always confirm your current
context before running anything, especially once you're managing more than one
cluster.
Every remaining week in this course runs against this local cluster — there's no need for a cloud account until you choose to deploy for real. The same manifests you write locally are exactly what you'd apply to a managed cloud cluster (EKS, GKE, AKS) later.
5. Hands-on Exercise
Stand up a local cluster and explore it entirely with kubectl
Get comfortable navigating a cluster before you ever write a manifest — you'll be using these exact commands constantly for the rest of this course.
Part 1 — Set up:
- Install Minikube or kind, and
kubectl, and start a local cluster. - Run
kubectl get nodes -o wide— note how many nodes you have and their status. - Run
kubectl get pods -Aand identify at least three system Pods already running in thekube-systemnamespace — these are Kubernetes' own control-plane and networking components running as regular Pods on your cluster. - Pick one of those system Pods and run
kubectl describe pod <name> -n kube-system— skim the output for the Pod's status, container image, and recent Events.
Kubernetes runs many of its own components (like kube-proxy and CoreDNS) as Pods inside the cluster itself — this is a great, real illustration that Kubernetes largely "eats its own dog food" rather than being magic infrastructure outside the Pod model.
Part 2 — Trace a component to its role:
- Run an imperative test Pod (a shortcut you'll retire once Week 8 introduces manifests):
kubectl run test-nginx --image=nginx. - Immediately run
kubectl get podsrepeatedly (or add--watch) and observe the Pod moving through statuses — Pending, ContainerCreating, Running. - Run
kubectl describe pod test-nginxand find the "Node" field — this tells you which specific node the scheduler placed it on, and the Events section at the bottom shows the actual sequence: scheduled, image pulled, container started. - Delete it:
kubectl delete pod test-nginx. Write one paragraph, in your own words, tracing exactly which control-plane and node components were involved from the moment you rankubectl runto the Pod reaching Running status.
The Events section at the bottom of kubectl describe output is one of the most useful debugging tools in all of Kubernetes — get in the habit of checking it first whenever something isn't behaving as expected, in this exercise and every week after.
6. Knowledge Check
Four quick questions. Expand each to check your answer.
Q1
What's the ONE component that reads and writes directly to etcd?
What's the ONE component that reads and writes directly to etcd?
The kube-apiserver. Every other component — kubectl, the scheduler, controllers, even other control-plane pieces — interacts with cluster state exclusively through the API server, never touching etcd directly. This centralizes access control and auditing.
Q2
What decides WHICH node a new Pod gets placed on?
What decides WHICH node a new Pod gets placed on?
The kube-scheduler. It watches for Pods that have been created but not yet assigned a node, and picks a suitable one based on available resources, constraints and affinity/anti-affinity rules — the Pod itself never specifies its node directly under normal use.
Q3
What is the kubelet's job on a node?
What is the kubelet's job on a node?
It watches the API server for Pods assigned to its own node, and works with the local container runtime to make sure those Pods' containers are actually running and healthy — reconciling the node's real state toward what's been declared, the same declarative pattern used cluster-wide.
Q4
Does a modern Kubernetes cluster require Docker specifically to be installed on its nodes?
Does a modern Kubernetes cluster require Docker specifically to be installed on its nodes?
No. Kubernetes talks to nodes through the CRI (Container Runtime Interface), which any compliant runtime can implement — containerd and CRI-O are both common, and often don't involve the Docker daemon at all. Images built with docker build still work fine, since they're OCI-compliant regardless of which runtime eventually runs them.