Skip to main content
mastering ckad certified kubernetes application developer

Kind Installation and Cluster Configuration

8 min read Chapter 5 of 87
Summary

Step-by-step installation of Docker, kubectl, and Kind. Detailed...

Step-by-step installation of Docker, kubectl, and Kind. Detailed walkthrough of a 3-node kind-cluster.yaml with port mappings, kubeadmConfigPatches, and node labels. Covers cluster lifecycle, image loading, and verification.

Kind Installation and Cluster Configuration

A working local cluster is the foundation of every exercise in this book. This section takes you from zero to a fully operational 3-node Kubernetes cluster running inside Docker containers, with ingress ports mapped to your host machine.

Installing Docker

Kind runs Kubernetes nodes as Docker containers, so Docker is the first dependency. If Docker is already installed and running (docker ps returns without error), skip ahead to kubectl.

On Ubuntu/Debian:

# Remove old versions
sudo apt-get remove docker docker-engine docker.io containerd runc 2>/dev/null

# Install prerequisites
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg

# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin

# Allow your user to run Docker without sudo
sudo usermod -aG docker $USER
newgrp docker

On macOS, install Docker Desktop from https://docs.docker.com/desktop/install/mac-install/. On Windows, install Docker Desktop with WSL2 backend enabled.

Verify the installation:

docker run --rm hello-world

You should see “Hello from Docker!” confirming that the Docker daemon is running and your user has permission to interact with it.

Installing kubectl

kubectl is the command-line tool for interacting with Kubernetes clusters. The CKAD exam provides kubectl pre-installed, but you need it locally to practice.

# Download the latest stable release
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

# Install it
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

# Verify
kubectl version --client

Expected output (version numbers will vary):

Client Version: v1.30.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3

Exam relevance: The exam environment provides a specific kubectl version. Always check kubectl version --client at the start of the exam to know what flags and features are available.

Installing Kind

Kind is a single Go binary. Installation is one command:

# For Linux amd64
[ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.24.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

# Verify
kind version

For macOS with Homebrew:

brew install kind

For other platforms or the latest version, check https://kind.sigs.k8s.io/docs/user/quick-start/#installation.

Understanding What Kind Does

When you run kind create cluster, Kind does the following:

  1. Pulls a node image. This is a Docker image (kindest/node:v1.30.0) that contains kubelet, kubeadm, a container runtime (containerd), and all Kubernetes system components.
  2. Starts Docker containers. Each “node” in your cluster is a Docker container running this image. The container runs systemd, which starts kubelet, which runs Kubernetes system Pods.
  3. Bootstraps the cluster with kubeadm. Inside the containers, kubeadm init (on the control-plane) and kubeadm join (on workers) execute the same bootstrap process used on real machines.
  4. Writes a kubeconfig. Kind generates a kubeconfig file and merges it into your ~/.kube/config, setting it as the current context.

The result: kubectl commands you run on your host talk to the Kubernetes API server running inside the control-plane container. From Kubernetes’ perspective, each container is a node. From Docker’s perspective, they are containers. This duality is what makes Kind lightweight — no virtual machines, no hypervisors, no cloud bills.

The Cluster Configuration File

A default kind create cluster gives you a single-node cluster. For CKAD prep, you need multiple nodes. Create a file called kind-cluster.yaml:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
      - containerPort: 443
        hostPort: 443
  - role: worker
  - role: worker

Let’s break this down field by field.

kind: Cluster and apiVersion

This is a Kind-specific resource, not a Kubernetes resource. The apiVersion: kind.x-k8s.io/v1alpha4 tells Kind which configuration schema to use. This has nothing to do with the Kubernetes API version running inside the cluster.

nodes

An array defining the cluster topology. Each entry creates one Docker container that acts as a Kubernetes node. This configuration creates three nodes:

  • One control-plane node running the API server, etcd, controller-manager, and scheduler
  • Two worker nodes where your application Pods will be scheduled

kubeadmConfigPatches

This injects configuration into the kubeadm init process on the control-plane node. The patch adds a label ingress-ready=true to the control-plane node. This label is used by the NGINX Ingress Controller’s DaemonSet to know which node should receive external traffic.

The kubeletExtraArgs field passes arguments directly to the kubelet process on the node. The node-labels argument sets labels on the node at registration time — before any workloads are scheduled.

extraPortMappings

This maps ports from the Docker container to your host machine. When traffic arrives at localhost:80 on your host, Docker forwards it to port 80 on the control-plane container. This is how you access Ingress resources from your browser or curl.

Without port mappings, services inside the cluster are unreachable from your host unless you use kubectl port-forward. Port mappings give you a more realistic networking setup that mirrors production ingress flows.

Creating the Cluster

With kind-cluster.yaml saved, create the cluster:

kind create cluster --config kind-cluster.yaml --name ckad-lab

Kind will:

  1. Pull the kindest/node image (first time only, ~900MB)
  2. Create three Docker containers
  3. Bootstrap the control-plane with kubeadm init
  4. Join the two worker nodes with kubeadm join
  5. Install the CNI (kindnet) for Pod networking
  6. Write the kubeconfig to ~/.kube/config

Expected output:

Creating cluster "ckad-lab" ...
 ✓ Ensuring node image (kindest/node:v1.30.0) 🖼
 ✓ Preparing nodes 📦 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-ckad-lab"
You can now use your cluster with:

kubectl cluster-info --context kind-ckad-lab

Verifying the Cluster

Run the following commands to confirm everything is working:

# Check the current context
kubectl config current-context
# Expected: kind-ckad-lab

# List all nodes
kubectl get nodes

Expected output:

NAME                     STATUS   ROLES           AGE   VERSION
ckad-lab-control-plane   Ready    control-plane   45s   v1.30.0
ckad-lab-worker          Ready    <none>          30s   v1.30.0
ckad-lab-worker2         Ready    <none>          30s   v1.30.0

All three nodes should show STATUS: Ready. If a node shows NotReady, wait 30 seconds and check again — the kubelet may still be initializing.

Check system Pods:

kubectl get pods -n kube-system

You should see Pods for coredns, etcd, kube-apiserver, kube-controller-manager, kube-proxy, kube-scheduler, and kindnet. All should be Running or Completed.

Verify the control-plane node has the ingress label:

kubectl get nodes --show-labels | grep ingress-ready

You should see ingress-ready=true in the labels of the control-plane node.

Loading Docker Images into Kind

Kind clusters run inside Docker and have their own container image registry that is separate from your host’s Docker daemon. When you build a Docker image locally, it exists in your host’s Docker image cache — but Kind’s nodes cannot see it. You need to explicitly load images into Kind.

# Build an image on your host
docker build -t my-app:v1 .

# Load it into the Kind cluster
kind load docker-image my-app:v1 --name ckad-lab

After loading, any Pod in the cluster can use image: my-app:v1 with imagePullPolicy: Never or imagePullPolicy: IfNotPresent. Without loading the image first, the Pod will fail with ErrImagePull because Kind’s nodes cannot pull from your host’s Docker daemon.

Tip: When testing with locally-built images, always set imagePullPolicy: IfNotPresent in your Pod spec. The default pull policy for latest tags is Always, which will try to reach a remote registry and fail.

Verify an image was loaded:

docker exec -it ckad-lab-control-plane crictl images | grep my-app

This runs crictl (the container runtime CLI) inside the control-plane container to list its local images.

Resetting the Cluster

When you want a clean slate — maybe you broke networking, filled up etcd, or want to test a fresh setup:

# Delete the cluster
kind delete cluster --name ckad-lab

# Recreate it
kind create cluster --config kind-cluster.yaml --name ckad-lab

The entire cycle takes under 60 seconds. Keep your kind-cluster.yaml file in a consistent location (the book uses ~/ckad-lab/kind-cluster.yaml) so recreation is always one command away.

Exam strategy: In the real exam, you do not control cluster creation. But the habit of destroying and rebuilding builds confidence — you learn that Kubernetes state is reproducible from manifests. If you store your YAML files, you can recreate any workload in seconds. That mindset is critical when you are under time pressure.

Multiple Clusters

Kind supports running multiple clusters simultaneously. Each cluster is a separate set of Docker containers with its own kubeconfig context:

# Create a second cluster
kind create cluster --name ckad-practice

# List clusters
kind get clusters

# Switch between clusters
kubectl config use-context kind-ckad-lab
kubectl config use-context kind-ckad-practice

This is useful for simulating multi-cluster exam scenarios, though for most CKAD prep a single 3-node cluster is sufficient.

Summary

You now have three tools installed (Docker, kubectl, Kind) and a 3-node Kubernetes cluster running locally. The cluster has a control-plane node with ingress port mappings and two worker nodes — enough to practice node selectors, affinity rules, taints, tolerations, and multi-replica deployments. You know how to create, verify, load images into, and destroy clusters. The next section configures your shell for maximum speed.