Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive · Kubernetes

A Local Cluster in a Minute with kind

Run a real multi-node Kubernetes cluster inside Docker for learning and CI, with port mappings for ingress, local image loading, and clean teardown.

3 min read

  • kind 0.10
  • Kubernetes 1.20 node image
  • kubectl 1.21
  • Docker 20.10

You do not need cloud servers to learn Kubernetes. kind (Kubernetes in Docker) runs each cluster node as a Docker container and starts a conformant cluster in about a minute. It is also widely used in CI to test manifests and Helm charts on every commit.

Versions in use on this date. kind 0.10, Docker 20.10, and kubectl 1.21 (released 8 April 2021). kind publishes node images per Kubernetes version; this post pins the 1.20 image, which kind 0.10 supports, and uses the newer kubectl, which is allowed because kubectl may be one minor version ahead of or behind the cluster. Check the kind release notes for the exact image tag and digest of each release, and pin it.

What kind creates

flowchart TB
  DEV[Your laptop with Docker] --> KC[kind CLI]
  KC --> C1
  subgraph DOCKER[Docker containers acting as nodes]
    C1[dev-control-plane<br/>API server etcd scheduler]
    W1[dev-worker]
    W2[dev-worker2]
  end
  KC -->|writes kubeconfig| KUBE[~/.kube/config context kind-dev]
  KUBECTL[kubectl] --> KUBE
  KUBECTL -->|6443 mapped to localhost| C1
  C1 --- W1
  C1 --- W2
  HP[localhost 80 and 443] -->|extraPortMappings| C1
  IMG[Local docker images] -->|kind load docker-image| DOCKER

Each node container runs systemd, containerd and kubelet, so what you learn matches a real cluster. The CNI is a simple built-in (kindnet) by default.

Install

# kind (Linux amd64); check the releases page for the latest 0.10.x
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.10.0/kind-linux-amd64
chmod +x kind && sudo mv kind /usr/local/bin/
# kubectl
curl -LO "https://dl.k8s.io/release/v1.21.0/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kind version; kubectl version --client --short; docker info --format '{{.ServerVersion}}'

Give Docker enough resources: 4 CPUs and 6 to 8 GB RAM is comfortable for a three-node cluster; on Docker Desktop, set this in preferences.

A cluster you can actually use

kind-config.yaml:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: dev
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - { containerPort: 80,  hostPort: 80,  protocol: TCP }
      - { containerPort: 443, hostPort: 443, protocol: TCP }
  - role: worker
  - role: worker
kind create cluster --config kind-config.yaml
kubectl cluster-info --context kind-dev
kubectl get nodes -o wide
docker ps --format '{{.Names}}\t{{.Image}}'

The ingress-ready label and port mappings prepare the control-plane node to host an ingress controller reachable on localhost (covered in the ingress post). Delete and recreate clusters freely; they are disposable.

Run something

kubectl create deployment hello --image=nginx:1.20 --replicas=2
kubectl expose deployment hello --port=80
kubectl get pods -o wide                         # spread across the workers
kubectl port-forward svc/hello 8080:80 &
curl -I localhost:8080

Your own images without a registry

kind nodes cannot see your host’s Docker images, so load them:

docker build -t myapp:dev .
kind load docker-image myapp:dev --name dev
kubectl create deployment myapp --image=myapp:dev
kubectl set image deployment/myapp myapp=myapp:dev

Use a tag other than latest, or set imagePullPolicy: IfNotPresent; with latest Kubernetes tries to pull from a registry and fails.

Useful commands

kind get clusters
kind export logs ./kind-logs --name dev       # debug bundle
kubectl config get-contexts; kubectl config use-context kind-dev
kind delete cluster --name dev

Use in CI

# GitHub Actions
- uses: engineerd/setup-kind@v0.5.0
  with: { version: v0.10.0 }
- run: |
    kubectl apply -f k8s/
    kubectl wait --for=condition=Available deployment/myapp --timeout=120s

Tests then run against a real API server: manifest validation, Helm installs, controller behaviour.

Limits

  • Not for production or performance testing; everything shares your machine.
  • LoadBalancer services stay <pending> without an add-on such as MetalLB; use NodePort, port-forward, or ingress.
  • Persistent data lives inside node containers and vanishes when the cluster is deleted.
  • kind exercises Kubernetes itself, not your cloud’s load balancers, IAM integration or storage classes.

For comparison: Minikube and k3d offer similar local clusters; kind’s strengths are speed, multi-node support and CI use.