App Development Using Local Kubernetes (k8s) Cluster

App Development Using Local Kubernetes (k8s) Cluster

Photo Source: https://kind.sigs.k8s.io/

Use Case

The current cloud era is dominated heavily by Kubernetes (k8s). While the DevOps team manages App lifecycle (deploy, scale, upgrade, downgrade, etc.), the App Developer needs to build a similar environment locally. So before shipping the App, developer is required to ensure DevOps lifecyle is not impacted by any of the changes s/he makes.

So it is very much necessary for a Developer to have a local k8s cluster for testing. In short, this blog post will demonstrate following flow.

Diagram Source is here.

Assumption

  • You have docker, kubectl and KinD installed already.

  • You have go version go1.20.3 installed.

  • This complete setup is build on Ubuntu 22.04 release. If you are using another OS/platform, then you might need to modify the commands accordingly.

Let Start...

Prerequisite Setup

Clone the repo

[~]# git clone https://github.com/simplyatul/godevsetup.git
[~]# cd godevsetup

Create a local docker registry. We will use this registry to push the App's docker image.

[~/godevsetup]# ./local-docker-registry.sh 
974282a4a1c561017a93cc45caefcc23f9a2ed1924ec8e949f76850e4818ad89

[~/godevsetup]# docker ps -a
CONTAINER ID   IMAGE             COMMAND                  CREATED         STATUS         PORTS                                       NAMES
974282a4a1c5   registry:latest   "/entrypoint.sh /etc…"   4 seconds ago   Up 3 seconds   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   local-docker-registry

Now using KinD, create local k8s cluster with a master node and two worker nodes.

[~/godevsetup]# kind create cluster --config ./kind-local-k8s-cluster.yaml --name local-dev
Creating cluster "local-dev" ...
 ✓ Ensuring node image (kindest/node:v1.26.3) 🖼
 ✓ Preparing nodes 📦 📦 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
 ✓ Joining worker nodes 🚜 
Set kubectl context to "kind-local-dev"
You can now use your cluster with:

kubectl cluster-info --context kind-local-dev

Have a nice day! 👋

[~/godevsetup]# docker ps -a
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS                                           NAMES
82849b786a50   kindest/node:v1.26.3   "/usr/local/bin/entr…"   56 seconds ago   Up 53 seconds                                                   local-dev-worker2
0d1153c315bf   kindest/node:v1.26.3   "/usr/local/bin/entr…"   56 seconds ago   Up 53 seconds                                                   local-dev-worker
d16cbeb2102c   kindest/node:v1.26.3   "/usr/local/bin/entr…"   56 seconds ago   Up 53 seconds   0.0.0.0:80->80/tcp, 127.0.0.1:43821->6443/tcp   local-dev-control-plane
974282a4a1c5   registry:latest        "/entrypoint.sh /etc…"   10 minutes ago   Up 10 minutes   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp       local-docker-registry

See kind-local-k8s-cluster.yaml. We have made the KinD cluster ingress ready using node-labels: "ingress-ready=true".

Install the Nginix Controller. This is required so that our service can be reachable from outside the cluster.

[~/godevsetup]# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/kind/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created

Connect the kind network with the local docker registry

[~/godevsetup]# docker network connect kind local-docker-registry

Step 1 - App Development

I have written a simple App in Go language. You can do this in any language of your choice. The App exposes a ReST API and returns the hostname. Check the GitHub repo. You can run the App locally using go run main.go and then hit the following command from another terminal.

$ curl localhost:8080
Hostname: atul-lap

Once you complete the coding, commit it. Optionally, you can push it to the remote repo as well.

Step 2 - Conatainerize

Build the docker image

[~/godevsetup]# docker build -t localhost:5000/godevsetup:latest .

Run it locally and check once

[~/godevsetup]# docker run --rm --name godevsetup -p 8080:8080 localhost:5000/godevsetup:latest

# from another terminal
[~/godevsetup]# curl localhost:8080
Hostname: 064a6217790f

Step 3 - Push Image to Local Registry

Push the image to the local registry

[~/godevsetup]# docker push localhost:5000/godevsetup:latest

[~/godevsetup]# curl -X GET localhost:5000/v2/godevsetup/tags/list {"name":"godevsetup","tags":["latest"]}

Step 4 - Deploy on Local K8s Cluster

Check the containers running on k8s worker nodes before we deploy the App.

[~/godevsetup]# docker exec local-dev-worker crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
62e79b0d067d7       a329ae3c2c52f       About an hour ago   Running             kindnet-cni         0                   f4171cb3fcca7       kindnet-q9gcc
fb97952a78295       eb3079d47a23a       About an hour ago   Running             kube-proxy          0                   f586b0a23dd88       kube-proxy-64ng7

[~/godevsetup]# docker exec local-dev-worker2 crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
a62234bb3add4       a329ae3c2c52f       About an hour ago   Running             kindnet-cni         0                   6bbf88872fe5b       kindnet-jv67s
9f0f8a3a1eaa4       eb3079d47a23a       About an hour ago   Running             kube-proxy          0                   4aa4b67da1ff7       kube-proxy-fptm8

I have created the service.yaml which creates Deployment and Service k8s kinds for our App. Apply it using kubectl to deploy the App.

[~/godevsetup]# kubectl apply -f service.yaml
deployment.apps/godevsetup created
service/godevsetup created
ingress.networking.k8s.io/godevsetup-ingress created

[~/godevsetup]# kubectl get all
NAME                              READY   STATUS    RESTARTS   AGE
pod/godevsetup-575fcd74d5-9k6d9   1/1     Running   0          17s
pod/godevsetup-575fcd74d5-f6xz6   1/1     Running   0          17s
pod/godevsetup-575fcd74d5-x7dtt   1/1     Running   0          17s

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/godevsetup   ClusterIP   10.96.73.129   <none>        8080/TCP   17s
service/kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP    74m

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/godevsetup   3/3     3            3           17s

NAME                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/godevsetup-575fcd74d5   3         3         3       17s

You can also observe the App pods are created inside the worker nodes

[~/godevsetup]# docker exec local-dev-worker crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
778beb3eee995       7df119c4f1283       28 minutes ago      Running             godevsetup          0                   3e1d374e07eb5       godevsetup-575fcd74d5-9k6d9
2aaaaa94f5ec5       7df119c4f1283       28 minutes ago      Running             godevsetup          0                   f43aab8a1bb08       godevsetup-575fcd74d5-x7dtt
62e79b0d067d7       a329ae3c2c52f       2 hours ago         Running             kindnet-cni         0                   f4171cb3fcca7       kindnet-q9gcc
fb97952a78295       eb3079d47a23a       2 hours ago         Running             kube-proxy          0                   f586b0a23dd88       kube-proxy-64ng7

[~/godevsetup]# docker exec local-dev-worker2 crictl ps
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
5ff7d0313821e       7df119c4f1283       28 minutes ago      Running             godevsetup          0                   50dcd5384b9ce       godevsetup-575fcd74d5-f6xz6
a62234bb3add4       a329ae3c2c52f       2 hours ago         Running             kindnet-cni         0                   6bbf88872fe5b       kindnet-jv67s
9f0f8a3a1eaa4       eb3079d47a23a       2 hours ago         Running             kube-proxy          0                   4aa4b67da1ff7       kube-proxy-fptm8

Step 5 - Verify

Finally, verify the App's ReST endpoint.

[~/godevsetup]# curl localhost
Hostname: godevsetup-575fcd74d5-f6xz6
[~/godevsetup]# curl localhost
Hostname: godevsetup-575fcd74d5-9k6d9
[~/godevsetup]# curl localhost
Hostname: godevsetup-575fcd74d5-x7dtt

Step 6 - Enhance/Bug Fix the App

First, remove the App from the k8s cluster

[~/godevsetup]# kubectl delete -f service.yaml
deployment.apps "godevsetup" deleted
service "godevsetup" deleted
ingress.networking.k8s.io "godevsetup-ingress" deleted

And now repeat Steps 1-5.

Sign Off...

We are done. This blog covers how to set up a local k8s cluster using KinD. And how can we leverage it for our App deployment and testing.

Thank you for reading the post. Please comment on how you find this. Connect me on Twitter for getting updates on future blog posts.