Monday, December 26, 2022

Using kind for Local Development

 



Overview

In this post we will review a method to use kind for local development, for example on the personal laptop. Kind stands for Kubernetes IN Docker, and uses docker containers to simulate kubernetes nodes. It is a great tool to replace bare metal kubernetes,which until recently was relatively easy to install, but then with the CRI changes in kubernetes got very complicated.

The steps below explain how to install kind cluster with NGINX based ingress. Then we show how to simplify the communication with the services deployed on the kubernetes cluster.


Install The Kind Binary

Kind is a standalone binary, we just download it:


curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64
chmod +x ./kind
sudo mv ./kind /usr/local/bin/kind


Create Kind Based Kubernetes Cluster

To install a kubernetes cluster, we use the following:


cat <<EOF | kind create cluster --config=-
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
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
EOF


This enables binding of ports 80 and 443 on the local machine, which will later be used for communication from the local development machine to the services running on the kubernetes cluster.


Deploy NGINX Ingress

Now we deploy NGINX based ingress, which is the most popular kubernetes ingress.


kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

sleep 5

kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=90s


Load Docker Images

In most cases we build the application docker images locally on the development machine, and hence to make these available for the kind based kubernetes we need to load them, for example:

kind load docker-image ${DockerTag}

This command should be integrated as part of the docker images build scripts on the local machine.


List Loaded Docker Images

While this is not a part of the regular build and test process, I've found it very useful command - to view the images that are already loaded to the kind:


docker exec -it $(kind get clusters | head -1)-control-plane crictl images


Communication With The Services

Great! We've deployed our services on the kubernetes cluster, and everything works, but hey? wait... How do we access these services? The best method would be a combination of local development kind dedicated ingress with hosts file update.

Let assume we have kubernetes services named my-service-1 and my-service-2. First we create an ingress to route the incoming requests to the services. Notice that this ingress should be deployed only as part of the development environment, and not on the production. This could be easily accomplish using helm flags. The ingress would be:


apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-kind
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: my-service-1
http:
paths:
- pathType: ImplementationSpecific
backend:
service:
name: my-service-1
port:
number: 80
- host: my-service-2
http:
paths:
- pathType: ImplementationSpecific
backend:
service:
name: my-service-2
port:
number: 80


Notice that we use the host name to select the target service to access. But how do we get the my-service-1 and my-service-2 to reach the kind based kubernetes ingress? Simple - we update the hosts file:

127.0.0.1 my-service-1
127.0.0.1 my-service-2


By setting the IP for these services to the localhost, we get to the binding of the cluster that we've previously configured as part of the cluster creation.

Final Note

Kind is a great and easy tool for local development on a kubernetes cluster, but there are some downsides. First, we need to wait for the docker images to be loaded into the kind cluster. This might last ~20 seconds, and while it is relatively short time, it is a bit annoying. Second, using ingress we can route only to HTTP/S based services. TCP based services (as Redis) cannot be addressed using this method.

No comments:

Post a Comment