By the end you'll have
- A three-node Kubernetes cluster running on your laptop, for free
- Seen the control plane and worker nodes from Lesson 1 with your own eyes
- Deleted a pod and watched Kubernetes replace it
What you need
You'll use kind, short for "Kubernetes in Docker". It runs each node of the cluster as a Docker container on your laptop. It's what many Kubernetes projects use for testing, it starts in about a minute, and you can delete it just as fast. It's not for production; it's perfect for learning.
You need three things:
- Docker, running. Docker Desktop on macOS or Windows, or Docker Engine on Linux.
- kubectl (say "cube control"), the command-line tool you use to talk to any Kubernetes cluster.
- kind itself.
1. Install kind and kubectl
On macOS with Homebrew:
brew install kind kubectl
On Windows with Chocolatey:
choco install kind kubernetes-cli
On Linux, follow the download steps in the kind quick start (opens in a new tab).
Check that both work:
kind version
kubectl version --client
2. Describe the cluster you want
Remember desired state from Lesson 1? Creating the cluster works the same way. Save this as kind-learn.yaml:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane # the brain
- role: worker # a machine that runs your apps
- role: worker # and another one
3. Create it
kind create cluster --name learn --config kind-learn.yaml
This takes a minute or two the first time, while Docker downloads the node image. When it finishes, kind also points kubectl at the new cluster for you.
kind create cluster --name learn. You get a single node that is both control plane and worker. Everything below still works; you'll just see one node instead of three.4. Look at your nodes
kubectl get nodes
NAME STATUS ROLES AGE VERSION
learn-control-plane Ready control-plane 60s v1.xx.x
learn-worker Ready <none> 40s v1.xx.x
learn-worker2 Ready <none> 40s v1.xx.x
Three machines, all Ready. Your version numbers will differ, and that's fine. A ROLES value of <none> just means "an ordinary worker".
Here's what you just built:
5. Find the control plane
The control plane is made of programs, and in your kind cluster they run as pods in a namespace called kube-system. (A namespace is a folder for grouping things; Lesson 10 covers them.)
kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-xxxxxxxxxx-xxxxx 1/1 Running 0 2m
coredns-xxxxxxxxxx-xxxxx 1/1 Running 0 2m
etcd-learn-control-plane 1/1 Running 0 2m
kindnet-xxxxx 1/1 Running 0 2m
kube-apiserver-learn-control-plane 1/1 Running 0 2m
kube-controller-manager-learn-control-plane 1/1 Running 0 2m
kube-proxy-xxxxx 1/1 Running 0 2m
kube-scheduler-learn-control-plane 1/1 Running 0 2m
Your list is a little longer (some pods run once per node) and the random letters will differ.
What each part does
Lesson 1 gave you one line per part. Here's the fuller picture, including what happens when each one stops, which is the question that matters when something breaks (and a favorite in interviews).
The control plane
- API server (
kube-apiserver): the front door. Every request goes through it: yours from kubectl, the scheduler's, the kubelets'. It checks who's asking and whether they're allowed, then records the change. Nothing else talks to etcd directly. If it stops: your apps keep running, and each kubelet still restarts crashed containers on its own node, but nothing can be changed, scheduled or replaced, and kubectl stops answering. - etcd (
etcd-learn-control-plane): the memory. A small database that holds everything the cluster knows: your desired state and the current status of every object. If it stops: the same as losing the API server. If its data is lost with no backup, the cluster forgets everything you've asked it to run, which is why managed providers back it up for you. - Scheduler (
kube-scheduler): the seating planner. When a new pod needs a home, it checks which nodes have enough room (CPU and memory, Lesson 9) and picks one. It only decides; it doesn't start anything. If it stops: running pods are fine, but new pods wait inPendingwith nowhere to go. - Controller manager (
kube-controller-manager): the thermostats. Many small loops, one for each kind of thing, each comparing what you asked for with what exists. The one for Deployments is what replaces the pod you delete in step 6. If it stops: a container that crashes still gets restarted by its kubelet, but a pod that's deleted, or lost with a failed node, is never replaced.
On every node
- Kubelet: the caretaker. An agent on each machine. It asks the API server what should run on its node, tells the container runtime to start it, restarts containers that crash, and reports back. It's the "reports back" arrow in the diagram. If it stops: the node is marked
NotReady, and after a few minutes its pods are recreated on other nodes. - Container runtime (containerd): the engine. The software that actually runs containers. Kubernetes never runs a container itself; it asks the runtime to.
- kube-proxy (
kube-proxy-xxxxx): the switchboard. It sets up the network rules that send traffic to the right pods. Lesson 8 covers it.
Two of these, the kubelet and the container runtime, aren't in your pod list. They run directly on each node, underneath Kubernetes, because something has to be there to start the pods in the first place.
The rest of the list is helpers: coredns is the cluster's phone book, turning names into addresses, and kindnet is kind's network plugin, which gives every pod its own address.
6. Watch it heal
Now try the idea from Lesson 1 for real. Ask for two copies of a web server:
kubectl create deployment hello --image=nginx:1.27 --replicas=2
kubectl get pods -o wide
The NODE column shows the scheduler spread your two pods across the workers. Copy the name of one pod, then delete it:
kubectl delete pod <paste-a-pod-name-here>
kubectl get pods
There are still two pods. One of them is brand new: look at its AGE. You told Kubernetes "two copies", one disappeared, and a controller noticed the difference and fixed it. You didn't restart anything. (Lesson 5 explains Deployments properly.)
Also notice that neither pod landed on learn-control-plane. That node has a taint, a "keep out" sign that tells the scheduler not to put ordinary apps there, so the brain of the cluster keeps its resources for itself. (On the one-node cluster, kind removes that taint so your apps have somewhere to run, and everything lands on that one node.)
Optional: choose the node yourself
Normally the scheduler picks. You can narrow its choice by giving a node a label and asking for that label. Label one worker:
kubectl label node learn-worker2 disk=ssd
Save this as pinned.yaml (Lesson 3 explains pod files properly):
apiVersion: v1
kind: Pod
metadata:
name: pinned
spec:
nodeSelector:
disk: ssd # only run on nodes with this label
containers:
- name: web
image: nginx:1.27
kubectl apply -f pinned.yaml
kubectl get pod pinned -o wide
The NODE column says learn-worker2, every time. On the one-node cluster, label learn-control-plane instead.
In real clusters you'd use this when some machines are different, like nodes with fast disks or graphics cards (GPUs). Lesson 9 covers the other ways: pinning a pod to one exact node, softer "preferred" rules, and taints.
Which cluster am I talking to?
kubectl can know about many clusters at once, and it sends commands to whichever one is current. Get into the habit of checking before you delete anything:
kubectl config current-context
For this lesson it should say kind-learn. If you ever have a work or production cluster on the same laptop, this one command is what keeps a practice delete from landing in the wrong place.
On a managed cluster, the control plane disappears
Your kind cluster shows everything, because you run all of it. A managed cluster from a cloud provider (EKS, AKS, GKE, DigitalOcean and others) looks different, because the provider runs the control plane for you, out of sight. If you have access to one, at work or on a free trial, you can see this for yourself:
kubectl get nodes
kubectl get pods -n kube-system
kubectl cluster-info
get nodeslists only worker nodes. There's no control-plane node.get pods -n kube-systemshows networking and DNS pods, but nokube-apiserver,etcd,kube-schedulerorkube-controller-manager.cluster-infostill shows an API server address, but it belongs to the provider, not to any machine you can see.
That's what "managed Kubernetes" means: you rent the workers, and the provider keeps the brain running, patched and backed up. Everything you type with kubectl works the same either way. Run kubectl config current-context first, so you know which cluster you're looking at.
Every major cloud offers managed Kubernetes the same way, and everything in this track works on all of them. If you're curious how they compare, see Managed Kubernetes compared.
Clean up
When you're done, delete the whole cluster. It's just Docker containers, so this takes seconds:
kind delete cluster --name learn
You can create a fresh one any time with the command from step 3. In Lesson 3 you will, to look closely at pods.
Check yourself
What is kind?
Which control plane component picks a node for each new pod?
You deleted one of your two hello pods and a new one appeared. What made that happen?
Why does etcd matter so much?
On a managed cluster like EKS or DOKS, why is there no kube-apiserver pod?
Which command should you run before deleting anything, to make sure you're on the right cluster?
Key terms
- Control plane: the part of the cluster that decides what runs where and keeps checking it.
- Worker node: a machine that runs your pods.
- kubectl: the command-line tool for talking to a cluster's API server.
- etcd: the database where the cluster stores its desired state.
- Kubelet: the agent on each node that starts and watches its pods.
- Context: kubectl's name for "which cluster, as which user".
kubectl config current-contextshows the active one.
Go deeper
- The kind quick start (opens in a new tab) covers more options, including other ways to install.
- Kubernetes components (opens in a new tab) in the official docs describes every part of the control plane and nodes.