From Concepts to a Real Cluster
Day 1 covered the core object model — Pods, ReplicaSets, Deployments, and Services — but everything was created somewhere. That somewhere is a Kubernetes cluster, and a cluster has to run on actual compute: a laptop, a VM, a cloud instance, or bare metal.
Setting up a production-grade cluster by hand is a genuinely complex undertaking — provisioning multiple machines, wiring up the control plane, configuring networking between nodes, and more. For learning and local development, that complexity is unnecessary. This lesson uses Kind to get a real, spec-compliant Kubernetes cluster running in minutes, on top of a single AWS EC2 instance.
What is Kind?
Kind stands for Kubernetes IN Docker. It's a tool that creates Kubernetes clusters by running each cluster "node" as a Docker container rather than a separate VM or physical machine. The control plane and any worker nodes are just containers on the same Docker host — but from kubectl's point of view, it's a fully functional cluster with the same API server, etcd, scheduler, and controller manager as any other.
Why Kind?
Learning Kubernetes doesn't require ten servers and 50 GB of RAM. It requires one Docker installation:
One Docker Installation
↓
Kind
↓
Complete Kubernetes Cluster
Each "node" is just a container, so the architecture looks like this:
EC2 Instance
│
Docker Engine
│
├── Control Plane Container
│
└── Worker Node Container
Why Run Kind on EC2 Instead of Locally?
Kind runs perfectly well on a laptop. Running it on an EC2 instance instead has a few practical advantages for a classroom or team setting:
- Accessible anywhere — connect from any machine over SSH
- Same environment for everyone — no "works on my machine" differences in OS or Docker version
- Better performance — dedicated CPU/RAM instead of competing with a laptop's other processes
- Easy to share — a teammate or instructor can SSH into the same box
- Closer to production — you're working on Linux, over a network, the way real clusters are typically operated
- Useful DevOps practice — provisioning a cloud instance and hardening it is itself a skill worth reps
Prerequisites
You'll need an AWS account and an EC2 instance running Ubuntu 22.04 LTS with internet access. Recommended sizing:
| Setting | Value |
|---|---|
| OS | Ubuntu 22.04 LTS |
| Instance Type | Minimum 2 vCPU, 4 GB RAM (e.g. t3.medium) |
| Storage | 20 GB |
| Security Group | SSH (22), HTTP (80), HTTPS (443), NodePort range (30000–32767) if you plan to expose services externally |
Open the NodePort range (30000–32767) in the instance's security group up front. It's easy to forget until you kubectl expose something and can't reach it from outside — better to allow it now than to debug a "connection refused" later.
Once the instance is running, connect over SSH using your key pair:
ssh -i my-key.pem ubuntu@<EC2_PUBLIC_IP>A quick hostname confirms you're on the right box.
Step 1: Update Packages
Always refresh the package index before installing anything new:
sudo apt update
sudo apt upgrade -yStep 2: Install Docker
Kind needs Docker as its container runtime — each Kubernetes node it creates is a Docker container. Install it with the official convenience script:
curl -fsSL https://get.docker.com | sudo shEnable and start the service so it survives a reboot:
sudo systemctl enable docker
sudo systemctl start dockerVerify:
docker --version
# Docker version 28.x.xStep 3: Let the Current User Run Docker Without sudo
By default, every Docker command needs sudo. Add your user to the docker group instead:
sudo usermod -aG docker $USER
newgrp dockerConfirm it worked:
docker psIf that runs without sudo and without a permission error, you're set.
usermod -aG docker $USER doesn't take effect for your current shell session — group membership is read at login. newgrp docker works around that for the active session, but if you open a fresh SSH connection later and still see permission errors, log out and back in.
Step 4: Install kubectl
kubectl is the CLI that talks to the Kubernetes API server — it's how you, and every automation tool, actually operate a cluster:
Developer → kubectl → API Server → Kubernetes Cluster
Download the latest stable release, make it executable, and put it on your PATH:
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/Verify:
kubectl version --clientStep 5: Install Kind
Same three-step pattern — download, make executable, move into PATH:
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x kind
sudo mv kind /usr/local/bin/Verify:
kind version
# kind v0.30.xStep 6: Create the Cluster
The simplest way to create a cluster is a single command:
kind create cluster --name dev-clusterKind will create the cluster, install a CNI plugin for pod networking, and set up a default StorageClass. Once it reports ready, the architecture looks like this:
EC2 → Docker → Control Plane Container → Worker Container
If you need specific port mappings — for example, to reach a NodePort service from outside the container network — define them in a config file instead of using the defaults:
# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30080
hostPort: 30080
protocol: TCPkind create cluster --config kind-config.yamlStep 7: Verify the Cluster
First, confirm the underlying Docker containers exist — these are your Kubernetes nodes:
docker ps
# kind-control-plane
# kind-workerThen check the cluster through kubectl:
kubectl cluster-info
kubectl get nodesFinally, list every pod across all namespaces (-A means all namespaces) to see the control plane components — CoreDNS, etcd, the scheduler, the controller manager, and the API server — all running as pods inside your new cluster:
kubectl get pods -AEverything covered conceptually on Day 1 is now a running process you can inspect.
Step 8: Deploy and Expose an Application
With the cluster live, create a Deployment the same way you would on any Kubernetes cluster:
kubectl create deployment nginx --image=nginx
kubectl get deployment
kubectl get podsExpose it as a NodePort Service so it's reachable from outside the cluster:
kubectl expose deployment nginx --type=NodePort --port=80
kubectl get svcThe output shows a mapping like 80:30xxx/TCP — that high port on the node forwards to port 80 on the pod.
Under the hood, kubectl create deployment triggers the same chain from Day 1:
kubectl create deployment → API Server → Deployment → ReplicaSet → Pod → Container
Running kubectl get all shows the Deployment, ReplicaSet, Pod, and Service together — the full object graph from yesterday's lesson, now materialized on a real cluster.
Common Commands
kubectl get nodes # cluster nodes
kubectl get pods # pods in the current namespace
kubectl get deployment # deployments
kubectl get svc # services
kubectl get rs # replicasetsDeleting the Cluster
Kind clusters are disposable by design — tear one down and rebuild it in seconds:
kind delete cluster --name dev-clusterThis removes the control plane and worker containers along with everything running inside them.
Kind vs Minikube
Both are popular for local Kubernetes, but they take different approaches:
| Feature | Kind | Minikube |
|---|---|---|
| Runs inside Docker | Yes | No |
| Lightweight | Yes | Moderate |
| Startup speed | Fast | Moderate |
| Multi-node support | Yes | Limited |
| CI/CD friendly | Yes | No |
| Good for learning | Yes | Yes |
Kind vs Managed Kubernetes
Kind and services like Amazon EKS solve different problems:
| Kind | Amazon EKS | |
|---|---|---|
| Purpose | Local development | Production |
| Cost | Free | Paid |
| Topology | Single machine | Multi-node cluster |
| Underlying infra | Docker containers | Real cloud infrastructure |
| Best for | Learning | Enterprise workloads |
Kind is not intended for production use. Once workloads need to run reliably at scale, that's the job of a managed Kubernetes service — Amazon EKS, Google GKE, Azure AKS, or a self-managed cluster — not a Docker-based local tool.
Summary
AWS EC2 → Docker → Kind → Kubernetes Cluster → kubectl → Deployment → ReplicaSet → Pod → Container
A simple way to remember the stack: EC2 is the machine, Docker runs containers, Kind uses Docker to create a Kubernetes cluster, and kubectl is how you talk to that cluster's API server. With this environment running, the rest of Day 2 — Namespaces, Ingress, and the NGINX Ingress Controller — builds directly on top of it.