Skip to content

Get a Cluster to Practice On

Assumes: you can use a terminal and have Docker (or Podman) installed. Nothing about Kubernetes yet.

Every other page on this site gives you kubectl commands. This page gets you a cluster to run them against, on your own machine, in about five minutes. Nothing here costs money and nothing touches a cloud account.

1. Install kubectl

kubectl is the command-line client that talks to a cluster's API server. Install it however your platform prefers:

# macOS
brew install kubectl

# Linux (x86_64)
curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

# Windows
winget install -e --id Kubernetes.kubectl

Verify:

kubectl version --client

2. Create a cluster with kind

kind ("Kubernetes in Docker") runs a real cluster inside Docker containers. It is the fastest option and the closest to a normal cluster.

# macOS
brew install kind

# Linux / anything with Go toolchain-free install
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind

kind create cluster --name learn

That last command downloads a node image, boots a single-node cluster, and writes the connection details into your kubeconfig (~/.kube/config) so kubectl finds it automatically.

Alternatives: minikube (minikube start) runs a VM or container-based cluster with add-ons like an Ingress controller and a dashboard, and k3d (k3d cluster create learn) runs the lightweight k3s distribution in Docker. Any of the three works for everything on this site.

3. Verify it

kubectl get nodes

You should see one node with status Ready:

NAME                  STATUS   ROLES           AGE   VERSION
learn-control-plane   Ready    control-plane   45s   v1.31.0

If it says NotFound or the connection is refused, the cluster did not come up - check that Docker is running and re-run kind create cluster.

4. Run something

Create a Deployment (three nginx pods, kept alive for you) and put a Service in front of it:

kubectl create deployment web --image=nginx:1.27 --replicas=3
kubectl expose deployment web --port=80 --target-port=80

Watch what Kubernetes actually did:

kubectl get deploy,rs,pods,svc -l app=web

You asked for three replicas, and the Deployment created a ReplicaSet, which created three Pods, which the scheduler placed on your node. You never told it to do any of that - that is the reconciliation model from the Overview working in front of you.

5. Reach it

The Service has a virtual IP that only exists inside the cluster, so forward a local port to it:

kubectl port-forward service/web 8080:80

Open http://localhost:8080 and you get the nginx welcome page. Press Ctrl+C to stop forwarding.

Now try the thing that makes Kubernetes click. In a second terminal:

kubectl delete pod -l app=web --wait=false
kubectl get pods -l app=web -w

You deleted all three pods. The ReplicaSet notices the gap between desired (3) and actual (0) and creates replacements immediately. You did not ask for that either.

Cleaning up

kubectl delete deployment web
kubectl delete service web

# or throw the whole cluster away
kind delete cluster --name learn

Deleting and recreating the cluster takes under a minute, so treat it as disposable. Breaking it is the point.

Check Yourself

You ran kubectl create deployment web --replicas=3 and the command returned instantly. Were three pods running at that moment?

Almost certainly not. The command only wrote a Deployment object to the API server. The Deployment controller then created a ReplicaSet, the ReplicaSet created Pods, the scheduler assigned them to a node, and the kubelet pulled the nginx image and started the containers - all after your command returned. Success from kubectl means "intent recorded", not "workload running".

Why did deleting all three pods not leave you with zero pods?

Nothing in Kubernetes remembers that you issued a delete. The ReplicaSet controller only ever compares desired state (3 replicas) against what it observes (0 pods) and creates the difference. Deleting pods does not change the desired state, so the gap is immediately refilled.

You closed the terminal running kubectl port-forward and localhost:8080 stopped working. Did something break in the cluster?

No. port-forward is a tunnel maintained by your local kubectl process, not a cluster feature. The Service and pods are untouched. A ClusterIP Service is only reachable from inside the cluster, which is why you needed the tunnel at all - Ingress and LoadBalancer Services are the real answers for outside traffic.

What would you expect kubectl get nodes to show on a kind cluster with one node, and why does that differ from production?

One node named <cluster>-control-plane with the control-plane role, running both the control plane and your workloads. Production clusters normally taint control-plane nodes so application pods never land on them, and add separate worker nodes. Single-node local clusters skip that so your pods have somewhere to run.


Beginner track - step 2 of 12. Next: Pods and Deployments. Back to the track overview.