Run Kubernetes clusters locally using kind

How often have you created a new Kubernetes cluster to ‘test something new’? How often have you created a new Kubernetes cluster to start learning about new functionalities? In my case, I do this pretty frequently. I very frequently spin up (and down) Azure Kubernetes clusters.

This approach comes with two downsides however: it takes about 5 minutes to spin up a cluster and clusters carry a cost. Although Microsoft pays for my Azure usage, I still need to be mindful of costs.

There are however different ways to create clusters that don’t carry a cost, and that could create clusters faster. One of those solutions is minikube. This is a VM based-solution that creates virtual machines to run nodes of a Kubernetes cluster locally.

Another solution is to run kind (Kubernetes in Docker), which will be the focus of this blog. By running kind, you won’t run Kubernetes nodes as a virtual machine, but you’ll run them as container. The biggest benefit of this approach is that it’ll be even easier and more lightweight to quickly create and destroy a Kubernetes cluster.

For the purpose of this blog, I’ll be installing kind on my WSL2 setup. The end goal is to deploy a web app and expose that web app to the host system. To make sure I’m capturing all steps, I created a brand new WSL environment running Ubuntu 18.04 for WSL2, with nothing installed.

Prerequisites

kind has 2 prerequisites: go (11+) and Docker. It’s also going to be useful to have kubectl installed. We can install these the following way:

sudo apt-get update
sudo apt-get install docker.io golang -y
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl

With that out of the way, you’ll have to start the Docker daemon. If you’re running this in WSL I highly recommend checking out this post I wrote last week about automatically starting the Docker daemon in WSL. If you’re not running on WSL, you can start the Docker daemon using the following command:

sudo systemctl start docker

Installing kind

Installing kind is as simple as getting kubectl:

curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.8.1/kind-$(uname)-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind

Setting up a first cluster in kind

To setup a cluster in kind, you use the following command:

kind create cluster
Creating our first kind cluster

This will create the cluster in Docker. It automatically stores the context to connect to it on the kube-config file. This means you can immediately interact with the cluster and run:

kubectl get nodes

Which will show you a single node:

Our single node kind cluster

Running a sample app on kind

To test things out, I’ll be running a sample app on kind. As an example I’ll pick the app we used in the 7th chapter of the Hands-on Kubernetes on Azure book. This is a good demo app since it uses configmaps, deployments and a service to connect to it. The code for this sample is available on Github.

git clone https://github.com/PacktPublishing/Hands-On-Kubernetes-on-Azure---Second-Edition.git
cd Hands-On-Kubernetes-on-Azure---Second-Edition/Chapter07

From that directory, we’ll first create the three configmaps:

kubectl create configmap server1 --from-file=index1.html
kubectl create configmap server2 --from-file=index2.html
kubectl create configmap healthy --from-file=healthy.html

And then create two deployments:

kubectl create -f webdeploy1.yaml
kubectl create -f webdeploy2.yaml

This will cause the deployment and pods to be created. It takes a couple seconds for them to become live, since the images need to be downloaded. Please note, since we’re running a single-node cluster, these pods will be run on the master node.

Running the pods

Next, we’ll need to deploy the actual service that load balances between server1 and server2. The demo app was built to run on AKS, with service type LoadBalancer. This is not available on kind, since there is no external load balancer to provision.

To solve this, we can change our service to be a service of type NodePort. That will solve the load balancer issue, but it won’t make traffic available from the host yet. To make traffic available from the host, we’ll have to map a port from our cluster to our host machine. And that’s what we’ll do next.

Making a service available in kind

In this section we’ll destroy our current cluster and create a new (multi-node) cluster that exposes port 80. Since we’ll be using a service of type NodePort, we’ll need to assign a high port number (between 30,000-32,767). We’ll use that port number for the NodePort, but have kind expose it on port 80 to our host.

To destroy our demo cluster, we can execute the following command:

kind delete cluster

Next, we’ll create a custom cluster in kind. We’ll create a cluster with a single master node, 2 worker nodes and we’ll expose port 80 on the nodes.

To do this, we’ll create a kind cluster configuration file, which is a YAML file. The following YAML should do the trick:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
  extraPortMappings:
  - containerPort: 32345
    hostPort: 80
    listenAddress: "127.0.0.1"
- role: worker

We can then create a new cluster using the following command:

kind create cluster --config kind.yaml

And then we need to recreate our application.

kubectl create configmap server1 --from-file=index1.html
kubectl create configmap server2 --from-file=index2.html
kubectl create configmap healthy --from-file=healthy.html

kubectl create -f webdeploy1.yaml
kubectl create -f webdeploy2.yaml

With the application deployed, we can also deploy our service. We’ll need to adapt the webservice.yaml file to allow for our NodePort service:

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web-server
  ports:
  - protocol: TCP
    port: 80
    nodePort: 32345
  type: NodePort

Which we can then create using:

kubectl create -f webservice.yaml

And that will make our service available on localhost:80.

Exposing the service to our host system.

Which is exactly what I wanted to achieve.

Summary

In this post we looked into kind, a way to quickly run a Kubernetes cluster on a single machine. We deployed a single-node kind cluster and deployed an application to it. Afterwards, we created a new cluster, that allowed us to expose networking services.

I hope this helps you in cases where you quickly want/need a Kubernetes cluster to test something, without having to wait for an AKS cluster (and without having to pay for the resources).

Leave a Reply