00 - Setup: Local Cluster (kind/minikube)
Why this matters
Every exercise in this track needs a running Kubernetes cluster to point
kubectl at. Running one locally — instead of paying for a cloud cluster
before you understand the basics — means you can break things, delete the
whole cluster, and start over in seconds, for free. This module gets you
from "Docker only" to "a real (if small) Kubernetes cluster is running on
this machine."
Concepts
kubectl is the command-line client that talks to a Kubernetes
cluster's API — think of it as the docker CLI, but instead of talking to
the Docker daemon on one machine, it talks to a cluster's API server,
which might be managing dozens of machines. Every Kubernetes interaction
in this entire track goes through kubectl.
A local Kubernetes "cluster" is Kubernetes running on your own machine instead of on cloud VMs. You still get a real API server, a real scheduler, and real nodes — they're just simulated using containers (kind) or a single VM/container (minikube) instead of a fleet of cloud machines. It behaves the same way your production cluster will; it's just small.
kind ("Kubernetes IN Docker") runs each Kubernetes "node" as a Docker container. Since you already have Docker Desktop with WSL2 integration from the Docker track, kind is just another container workload to it — no extra virtualization layer, fast to start, fast to tear down and recreate. This is why we recommend it for a learning loop where you'll create and destroy clusters often.
┌───────────────────────────────────────────────────────────┐
│ Windows 11 host + WSL2 Ubuntu │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Docker Desktop engine │ │
│ │ ┌───────────────────────┐ ┌─────────────────────┐ │ │
│ │ │ "control-plane" node │ │ "worker" node │ │ │
│ │ │ = 1 Docker container│ │ = 1 Docker container│ │ │
│ │ │ kubelet, etcd, │ │ kubelet, │ │ │
│ │ │ apiserver, scheduler │ │ your workload pods │ │ │
│ │ └───────────────────────┘ └─────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ kubectl ─────► API server (inside the control-plane node)│
└───────────────────────────────────────────────────────────┘
minikube runs Kubernetes inside a single VM (or, in Docker-driver
mode, a single large container) and ships with an extensive addon system
(minikube addons enable ingress, a built-in dashboard, metrics-server
with one command, etc.). It's a good choice if you want a batteries-included
experience or need to demo a dashboard, but it's heavier and slower to
cycle than kind.
Which one we'll use: this track uses kind for every exercise. It's lighter, starts in seconds, and because each "node" is just a container, deleting and recreating a cluster is nearly free — you'll do that often while learning. Where a module needs an addon minikube would give you for free (an Ingress controller, metrics-server), we'll install it explicitly on kind so you understand what's actually happening.
Nodes: a Kubernetes cluster is made of a control plane (the
"brain" — decides what should run where) and worker nodes (where your
containers actually run). A default kind create cluster gives you one
node that plays both roles. You can ask for more nodes if you want to
simulate a multi-node cluster.
Default `kind create cluster` — 1 node, both roles:
┌────────────────────────────────┐
│ learning-control-plane │
│ ┌──────────┐ ┌────────────┐ │
│ │ control │+ │ worker │ │
│ │ plane │ │ role │ │
│ └──────────┘ └────────────┘ │
└────────────────────────────────┘
Multi-node (`--config kind-multi.yaml`):
┌───────────────┐ ┌──────────┐ ┌──────────┐
│ control-plane │ │ worker │ │ worker │
└───────────────┘ └──────────┘ └──────────┘
Command reference
| Command | What it does | Example |
|---|---|---|
kind create cluster |
Creates a kind cluster (default name kind) using the Docker driver |
kind create cluster --name learning |
kind create cluster --config <file> |
Creates a cluster with custom topology (multiple nodes, port mappings) | kind create cluster --config kind-config.yaml |
kind get clusters |
Lists all kind clusters on this machine | kind get clusters |
kind delete cluster |
Deletes a kind cluster | kind delete cluster --name learning |
kubectl cluster-info |
Shows the API server address and core service endpoints | kubectl cluster-info |
kubectl get nodes |
Lists the cluster's nodes and their status | kubectl get nodes -o wide |
kubectl config get-contexts |
Lists known clusters/contexts kubectl can talk to | kubectl config get-contexts |
kubectl config use-context <name> |
Switches which cluster kubectl targets | kubectl config use-context kind-learning |
kubectl version |
Shows client and server (cluster) Kubernetes versions | kubectl version |
minikube start |
Starts a minikube cluster (alternative to kind, covered for comparison) | minikube start --driver=docker |
minikube status |
Shows whether the minikube VM/container and cluster components are running | minikube status |
minikube delete |
Deletes the minikube cluster | minikube delete |
Hands-on exercises
These assume you're in a WSL2 Ubuntu terminal, Docker Desktop is running with WSL2 integration enabled (from the Docker track), and you have internet access to download binaries.
1. Install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
kubectl version --client
Expected: a client version prints (e.g. Client Version: v1.3x.y). No
server version yet — you have no cluster to talk to.
2. Install kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind
kind version
Expected: kind vX.Y.Z ... prints.
3. Confirm Docker is reachable from WSL2
docker ps
If this fails, go back to Docker Desktop settings and confirm WSL2 integration is enabled for your Ubuntu distro (this was set up in the Docker track) — kind needs a working Docker daemon to create its "node" containers.
4. Create your first cluster
kind create cluster --name learning
Expected output ends with something like:
Set kubectl context to "kind-learning"
You can now use your cluster with:
kubectl cluster-info --context kind-learning
kind automatically wrote a context into your kubeconfig and switched to it — no manual configuration needed.
5. Verify the cluster
kubectl cluster-info
kubectl get nodes
Expected:
NAME STATUS ROLES AGE VERSION
learning-control-plane Ready control-plane 1m v1.3x.y
One node, playing both control-plane and worker roles.
6. Look at what kind actually created
docker ps
Expected: you'll see a container named learning-control-plane — this
is your Kubernetes node. Kind's whole trick is running a full
Kubernetes node's worth of processes inside one Docker container.
7. Inspect contexts
kubectl config get-contexts
kubectl config current-context
Expected: a row for kind-learning, marked current (*) in the first
column.
8. Create a multi-node cluster with a config file
Multi-node clusters are useful later for understanding scheduling across nodes. Create the config:
# kind-multi.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
kind create cluster --name multi --config kind-multi.yaml
kubectl get nodes --context kind-multi
Expected: three nodes — one control-plane, two <none> (worker) roles.
9. Switch between clusters
kubectl config use-context kind-learning
kubectl get nodes
kubectl config use-context kind-multi
kubectl get nodes
Confirm each shows the right node count — this is how you'll manage multiple clusters (and later, real vs. test clusters) throughout your career.
10. Clean up the extra cluster
kind delete cluster --name multi
kind get clusters
Expected: only learning remains. Keep the learning cluster — the rest
of this track uses it.
11. (Optional comparison) Try minikube once
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube start --driver=docker
minikube status
kubectl get nodes
kubectl config get-contexts
Expected: minikube also registers its own context (minikube) and
switches kubectl to it. Notice kubectl get nodes now shows a
minikube-named node instead of the kind one — same kubectl, different
cluster underneath. When you're done comparing, tear it down and switch
back:
minikube delete
kubectl config use-context kind-learning
12. Diagnose and fix: "the cluster I expect isn't the one I'm talking to"
Someone (a very believable "friend") ran this and got confused:
kind create cluster --name debug-practice
kubectl config use-context kind-learning
kubectl get pods
# (works, but they expected to see resources they created against debug-practice)
Reproduce it yourself, then fix it:
kind create cluster --name debug-practice
kubectl create deployment nginx --image=nginx --context kind-debug-practice
kubectl config use-context kind-learning
kubectl get deployments
Expected: No resources found in default namespace. — the deployment
exists, but on kind-debug-practice, not kind-learning. Diagnose with:
kubectl config get-contexts
Find the * (current context) and confirm it doesn't match where you
created the resource. Fix by switching:
kubectl config use-context kind-debug-practice
kubectl get deployments
Expected: the nginx deployment now shows up. Clean up:
kind delete cluster --name debug-practice
kubectl config use-context kind-learning
This "wrong context" mistake is one of the most common real-world
Kubernetes errors — always check kubectl config current-context (or use
kubectl get nodes as a sanity check) before assuming a command failed
for some deeper reason.
Independent challenge
No YAML or commands given here — figure it out yourself using what you know from this module and earlier ones.
Task: A teammate needs a throwaway cluster named staging-sim that
mimics a small production topology: one control-plane node and three
worker nodes. Stand it up, prove all four nodes reached Ready, and then
— without deleting staging-sim — make sure your kubectl is pointed
back at your original learning cluster so you don't accidentally run
later exercises against the wrong place. Finally, confirm from the Docker
side (the container-runtime view you already know from the Docker track)
that the four nodes really are just containers on your host. Tear
staging-sim down when you're done.
Stuck? One hint
Multiple nodes come from a cluster config file listing one
role: control-plane and three role: worker entries; kubectl config use-context switches which cluster you target, and docker ps shows the
node containers.
Common mistakes & troubleshooting
- Docker not running / WSL2 integration off:
kind create clusterfails immediately with a connection error to the Docker daemon. Open Docker Desktop, Settings → Resources → WSL Integration, confirm your distro is enabled. - Forgetting which context is active: commands silently succeed or
return empty results against the wrong cluster. Always
kubectl config current-contextwhen something looks unexpectedly empty. - Port conflicts: if another process (or another kind cluster) is
already using a port kind wants to expose, cluster creation can fail.
kind delete cluster --name <name>and retry, or checkdocker psfor leftover containers from a previous failed attempt. - Confusing kind's "node" container with a real VM:
docker exec -it learning-control-plane bashgets you a shell inside the node container, which is a good debugging trick, but remember it's not where your workload containers run — those are separate containers created by the kubelet inside that node. - Running out of disk/memory with multiple clusters left around:
kind get clustersand delete ones you're not using — each is a set of running containers consuming resources.
Checkpoint quiz
Write down your answer to each question before expanding it — checking without attempting first is the single easiest way to fool yourself into thinking you've learned this.
- What relationship does
kubectlhave to a cluster, compared to whatdockerhas to the Docker daemon? - What is a kind "node," physically?
- Name one reason kind is recommended for this track over minikube, and one reason you might reach for minikube instead.
- What command tells you which cluster your next
kubectlcommand will hit? - If
kubectl get podsreturnsNo resources found, does that necessarily mean nothing is running anywhere? Why or why not? - How would you create a kind cluster with 2 worker nodes instead of the default 1 combined node?
Show answers
kubectlis a client to a cluster's API server (potentially many machines);docker(in the local-daemon sense) talks to one machine's Docker daemon directly. kubectl never touches nodes directly — every action goes through the API server.- A Docker container running the Kubernetes node processes (kubelet,
container runtime, etc.) — visible via
docker pson the host. - kind is faster to create/destroy and lighter, good for a fast learning loop; minikube bundles addons (dashboard, ingress, metrics-server via one command) which is convenient if you want those without installing them yourself.
kubectl config current-context(or inspect the*row inkubectl config get-contexts).- No — it only means nothing is running in the currently active context
(and, without
-A/--all-namespaces, only in the current namespace). The resources might exist in a different cluster/context or namespace. - Write a kind config YAML with one
role: control-planeentry and tworole: workerentries, thenkind create cluster --config <file>.
Further reading & sources
- Install Tools (kubectl, kind, minikube) - the official Kubernetes page for installing the CLI and a local cluster tool.
- kind Quick Start - the canonical guide for creating, using, and deleting kind clusters.
- kind Configuration reference - how to write the cluster config file used for multi-node topologies here.
- minikube Get Started - the minikube equivalent, for the optional comparison exercise.
- kubectl overview and cheat sheet - a fast reference for the commands and context switching you use throughout this track.
Next
01-kubernetes-architecture-and-concepts —
now that you have a cluster running, open the hood: what's actually
happening inside it when you run a kubectl command.