Contents
2 Chapter

Local Environment

Choose between minikube · kind · Docker Desktop k8s. Compare how each option works and the pros and cons of each, then install kubectl and bring up your first cluster with kind to check the nodes and system Pods — all in one pass.

In Chapter 1 What Kubernetes Is we saw the big picture of a cluster split into control plane and worker nodes. In this chapter we put that picture into practice. We compare the three ways to bring up a Kubernetes cluster on one laptop (minikube · kind · Docker Desktop k8s), install kubectl, then bring up our first cluster with kind to check the nodes and system Pods — all in one pass.

By the end of this chapter you’ll have one local cluster and the kubectl that lets you look into it. That state becomes the starting point for Chapter 3 kubectl and your first Pod onward.

Three options for local K8s #

All three tools do the same thing — “bring up one Kubernetes cluster locally” — but they differ in how they work and in their pros and cons.

ToolHow it worksMulti-nodeStart timeRecommended for
Docker Desktop k8sRuns single-node K8s on a VM inside Docker DesktopX (single node)mediumusers already using Docker Desktop on macOS · Windows
minikubeRuns the cluster on a VM or container driver of your choiceO (optional)mediumwhen you want to use addons (ingress, dashboard, etc.) richly
kindUses each docker container as a K8s nodeOfastlightweight, or when you want to define multi-node via YAML / CI

Spelling it out a bit more.

Docker Desktop k8s is the lowest-effort option on macOS and Windows. If you’re already using Docker Desktop, one checkbox in the Kubernetes tab of the settings menu brings up a cluster, and kubectl’s context is automatically set to docker-desktop. The downsides are that it’s macOS · Windows only (Docker Desktop for Linux offers the same option, but circumstances vary by environment), and since it’s K8s running inside the VM Docker Desktop already brings up, it uses a fair amount of memory and CPU.

minikube is among the oldest of the local K8s tools. Depending on the environment, one of several drivers — docker / hyperkit / kvm / virtualbox, etc. — is auto-selected or you can pick one. Its biggest strength is a rich set of addons. Commonly needed extras like ingress, metrics-server, dashboard, and registry can be turned on with a one-line command.

kind is a tool whose name is its description — Kubernetes IN Docker. Since it uses a docker container as a K8s node, it’s lighter and faster than minikube / Docker Desktop, which bring up a separate VM. You can define a multi-node cluster in a short YAML, which makes it great for mimicking an environment with the control plane and workers separated, and that’s why it’s used in K8s’s own CI.

Which to pick #

If you’re already using Docker Desktop on macOS · Windows, Docker Desktop k8s is the easiest to start with. One checkbox brings up a cluster, and it ties naturally into your usual docker setup. If you’re a Linux user, want to mimic a multi-node environment, or want to create and destroy clusters frequently, kind fits well. If you want to flip addons on and off with one-line commands and experiment with various things, minikube is a good choice too.

This book lays down kind as its main option. It’s light and fast, and you can create and destroy a cluster with one or two commands, which suits learning. That said, whichever you pick, the kubectl commands from Chapter 3 on are identical. Only the way the cluster is brought up differs; the K8s resources you handle on top of it are the same. Pick the one that fits your environment and follow along.

Installing kubectl #

Wherever you bring up the cluster, the client that talks to it is the same — kubectl. It’s the CLI that sends commands over HTTP to the K8s control plane’s API server (kube-apiserver).

macOS — Homebrew #

install kubectl (macOS)
brew install kubectl

Windows — winget or Chocolatey #

install kubectl (Windows)
winget install -e --id Kubernetes.kubectl
# or
choco install kubernetes-cli

Linux — official binary #

install kubectl (Linux)
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/

Depending on the distribution you can install via a package manager (apt, dnf, etc.) too, but with K8s it’s safer to keep the client and server versions within the compatible range. If you need the exact install procedure, see the install guide at kubernetes.io.

Once installed, check the client version.

verify install
kubectl version --client
example output
Client Version: v1.32.x
Kustomize Version: v5.x.x

At this point there’s no cluster yet, so the server version isn’t picked up. It appears once you bring up a cluster.

Bring up your first cluster with kind #

This is the main flow of the chapter. First install kind itself.

install kind (macOS)
brew install kind
install kind (Windows)
winget install -e --id Kubernetes.kind
# or
choco install kind
install kind (Linux)
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
chmod +x kind
sudo mv kind /usr/local/bin/

kind uses the docker daemon. Docker Desktop or the docker engine must already be running.

Now create a cluster. For a single node it’s one command.

create cluster
kind create cluster
example output
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.32.x)
 ✓ Preparing nodes
 ✓ Writing configuration
 ✓ Starting control-plane
 ✓ Installing CNI
 ✓ Installing StorageClass
Set kubectl context to "kind-kind"
You can now use your cluster with:

kubectl cluster-info --context kind-kind

The last line is the key. kubectl’s context is automatically set to kind-kind. From now on kubectl commands go to this cluster.

How kind works #

We said kind uses a docker container as a K8s node. You can confirm that’s really the case from the docker side.

see the container kind brought up
docker ps
example output
CONTAINER ID   IMAGE                  COMMAND                  PORTS                       NAMES
abc123def456   kindest/node:v1.32.x   "/usr/local/bin/entr…"   127.0.0.1:xxxxx->6443/tcp   kind-control-plane

One container made from the kindest/node image is up, and the control plane and worker run together inside it. An arbitrary host port is mapped to the container’s 6443 (the default port of the K8s API server). kubectl sends commands to this port.

First commands — looking around nodes and system Pods #

The cluster is up, so let’s confirm that the components that were only named in Chapter 1 are actually running.

list nodes
kubectl get nodes
example output
NAME                 STATUS   ROLES           AGE   VERSION
kind-control-plane   Ready    control-plane   45s   v1.32.x

Since it’s a single-node cluster, only one line shows. This one node plays the control plane role and also takes workloads at the same time (a common setup in local environments).

Next, look at the Pods in all namespaces. K8s components often run as Pods on top of the cluster.

Pods in all namespaces
kubectl get pods -A
example output
NAMESPACE            NAME                                         READY   STATUS    RESTARTS   AGE
kube-system          coredns-xxxxxxxxxx-aaaaa                     1/1     Running   0          1m
kube-system          coredns-xxxxxxxxxx-bbbbb                     1/1     Running   0          1m
kube-system          etcd-kind-control-plane                      1/1     Running   0          1m
kube-system          kindnet-xxxxx                                1/1     Running   0          1m
kube-system          kube-apiserver-kind-control-plane            1/1     Running   0          1m
kube-system          kube-controller-manager-kind-control-plane   1/1     Running   0          1m
kube-system          kube-proxy-xxxxx                             1/1     Running   0          1m
kube-system          kube-scheduler-kind-control-plane            1/1     Running   0          1m
local-path-storage   local-path-provisioner-xxxxxxxxxx-yyyyy      1/1     Running   0          1m

The components only named in Chapter 1 show up directly — kube-apiserver, etcd, kube-scheduler, kube-controller-manager, kube-proxy, and the cluster DNS coredns. On top of that, kind’s own network plugin kindnet and the local-path-provisioner that mimics PersistentVolume in a local environment are running alongside.

The kube-system namespace is the space where the Pods K8s uses for its own operation gather. Apps you create going forward go to the default namespace (or a namespace you create yourself, Chapter 7 Namespace and labels).

Finally, check the cluster’s own meta information.

cluster info
kubectl cluster-info
example output
Kubernetes control plane is running at https://127.0.0.1:xxxxx
CoreDNS is running at https://127.0.0.1:xxxxx/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

The IP and port written in the first line of this output are the same place as the port mapping we saw in docker ps above. That is, kubectl is talking to the kube-apiserver inside the node container through the port docker exposed on the host.

kubeconfig — where kubectl sends its commands #

Which cluster kubectl sends commands to is decided by the kubeconfig file. The default path is as follows.

  • macOS / Linux — ~/.kube/config
  • Windows — %USERPROFILE%\.kube\config

This file holds three kinds of information.

ItemMeaning
clusterswhich clusters exist (API server address, CA certificate)
userswith what credentials you access those clusters (certificates, tokens, etc.)
contextsa bundle of “this cluster + this user + this namespace.” kubectl uses one context at a time

When kind creates a cluster, it automatically adds a context named kind-kind and sets the current context to it. Let’s check directly.

see contexts
kubectl config get-contexts
example output
CURRENT   NAME             CLUSTER          AUTHINFO         NAMESPACE
*         kind-kind        kind-kind        kind-kind
current context
kubectl config current-context
example output
kind-kind

Once you’re handling several clusters at once (e.g., local kind, the company dev cluster, the company prod cluster), kubectl config get-contexts shows multiple entries. The command to pick one of them and make it the current context is use-context.

switch context
kubectl config use-context kind-kind

When you work back and forth between a company cluster and local, a habit of always being aware of the context so you don’t send commands to the wrong cluster is important. Tools that surface the current context in the shell prompt (kube-ps1, starship, etc.) are also widely used. This perspective is emphasized again in Part 4 EKS in Production (Chapter 21 EKS cluster setup).

If you bring it up with Docker Desktop k8s #

If you’re in an environment already using Docker Desktop, just turning on K8s in the settings menu brings up a cluster. The exact menu path varies slightly by Docker Desktop version, but generally it’s as follows.

  1. Run Docker Desktop → enter Settings (or Preferences)
  2. Select the Kubernetes tab in the left menu
  3. Check Enable Kubernetes → Apply & Restart

The first time you enable it, it takes a few minutes to pull the K8s component images. When done, kubectl’s context is added automatically.

switch to the Docker Desktop k8s context
kubectl config use-context docker-desktop
kubectl get nodes
example output
NAME             STATUS   ROLES           AGE   VERSION
docker-desktop   Ready    control-plane   2m    v1.32.x

The kubectl commands after this are completely identical to kind.

If you bring it up with minikube #

minikube is similar. First install the minikube CLI (official guide: minikube.sigs.k8s.io), then start the cluster.

start minikube cluster
minikube start

If you want to pick a driver explicitly, pass the --driver option. The default driver is auto-selected as docker / hyperkit / kvm, etc., depending on the environment.

start with an explicit driver
minikube start --driver=docker

Once minikube is up, the context is set to minikube.

verify
kubectl config use-context minikube
kubectl get nodes

One of minikube’s draws, addons, is handled with the minikube addons command. For example, to turn on the ingress controller, do this.

ingress addon
minikube addons enable ingress

Cleanup and teardown #

When you’re done learning or want to create a cluster fresh, it’s good to know how to tear things down cleanly too.

kind

delete kind cluster
kind delete cluster

If you never gave it a name, the cluster with the default name (kind) is deleted. If you ran several clusters at once, specify with --name.

Docker Desktop k8s

Check Disable Kubernetes in the Kubernetes tab of the settings. Or you can reset it with the Reset Kubernetes Cluster button on the same screen.

minikube

stop / delete minikube
minikube stop      # pause (can start again next time)
minikube delete    # delete completely

A big advantage of a local cluster is the lightness of being able to delete and recreate it anytime. When you decide something is tangled up, deleting it cleanly and bringing it up again is often faster.

Exercises #

  1. In light of your environment (OS, whether you use Docker Desktop, whether you intend to learn multi-node), decide in one line which of the three options (Docker Desktop k8s / minikube / kind) you’ll use as your main one, and write a paragraph about why, against the criteria in §“Which to pick.” Since in Part 4 EKS in Production you’ll end up talking to the EKS control plane with the same kubectl, also note how your local choice connects to the EKS flow.
  2. After bringing up a cluster with kind as in the body, organize into a table the roles of the 9 system Pods that appear in the kubectl get pods -A output, paired with the component descriptions in Chapter 1 §“The big picture of a K8s cluster.” kindnet and local-path-provisioner are components that don’t appear in Chapter 1; note which area (network / storage) they belong to.
  3. Check which contexts kubectl config get-contexts shows in your current environment. If there are two or more contexts (e.g., when kind-kind and docker-desktop are both picked up), switch between them with use-context and confirm directly how kubectl get nodes results differ. When you start handling a company cluster too, this habit becomes a safeguard.

In one line: local K8s has three options — Docker Desktop k8s · minikube · kind — and whichever you pick, the K8s resources you handle on top are identical. This book’s main option is kind. Which cluster kubectl sends commands to is decided by the context in kubeconfig.

Next chapter #

In Chapter 3 kubectl and your first Pod we cover the Pod, K8s’s smallest execution unit. We see both the imperative path of bringing one up with kubectl run and the declarative path of bringing one up with a YAML manifest, and the flow of looking into Pods with everyday commands like kubectl get / describe / logs / exec. It’s the real start of K8s.

X