6 Easy Steps To Deploy Web Application On Kubernetes

Learn how to deploy a React and Django web application on a Kubernetes cluster in 6 easy steps.

Vikas Gautam
Python in Plain English

--

Photo by Danial Igdery on Unsplash

Overview

This blog explains how to deploy your web application on a local Kubernetes cluster created using Docker For Desktop, Here our web application has frontend developed using React js, backend developed using Django, and any type of database (I am using MySQL). I will also tell you how to fix the CORS error which you will certainly face while accessing web application using a windows browser.

This blog looks lengthy so I am putting content here, you can check the part which you want.

Content

  1. Prerequisites
  2. Architecture Diagram & Idea
  3. Docker files
  4. Kubernetes Deployments
  5. Kubernetes Services
  6. Kubernetes Ingress Controller
  7. Frontend Axios API Changes and CORS error fix
  8. Backend Changes and CORS error fix
  9. Run All Yaml files
  10. Browse Web APP
  11. Conclusion & GitHub Repository link
  12. What’s Next

1. Prerequisites

1.1 ToDoList Web Application (branch master) https://github.com/vgautam99/ToDoList

1.2 Docker desktop https://www.docker.com/products/docker-desktop

1.3 Enable Kubernetes

1.4 Kubectl https://kubernetes.io/docs/tasks/tools/

To verify everything was done correctly, open a new cmd and enter kubectl. If you get a response from the kubectlcommands, then you are good to go.

2. Web application deployment on Kubernetes architecture

2.1 Let’s understand this architecture from right to left

Host DB: Since we are running Kubernetes on docker desktop so docker desktop has created a VM in our Windows/MC/Linux machine and this VM is the host where Kubernetes is running. We will store our data and logs inside this VM. We can choose to save data on AWS/Azure/GCP or any other cloud service provider too.

MySql: This is our database server that Django will use, you will see Mysql inside a circle which is inside a square, here the square is a Pod (smallest instance in Kubernetes same as a container in docker) and the circle is our docker container. So in simple terms, I have created a MySQL container using docker and put it inside a Kubernetes Pod. This is the same for Django and React.

Services: Services in Kubernetes allows to access a server running inside a pod, so here Mysql Service is created to access MySQL server running inside Mysql Pod. This is the same for Django and React.

Ingress: Ingress controller is used to access our web application from outside of the Kubernetes cluster (eg windows browser), Ingress also takes care of load balancing.

2.2 Idea

First, we will create docker images, which will run inside Kubernetes pods.

Then we will create deployments and these deployments will create a pod and run docker images.

Then to communicate with pods of each deployment we will create a service that will expose a port to access the pod.

Then we will create an Ingress controller which will allow us to access the web application from the browser.

At last, we will modify Axios APIs to fix the CORS error.

Now let’s create all the components of the architecture

3. Docker containers ( React and Django):

3.1 React Container: we will create a docker image inside react-todolist/react-todolist/Dockerfile

cd react-todolist/react-todolist
docker build -t frontend:v1 .

3.2 Django Container: we will create a docker image inside todolist/Dockerfile

cd todolist
docker build -t backend:v1 .

3.3 Now push both images in docker hub, if don’t have an account then create it on https://hub.docker.com/

docker image ls frontend:v1
docker login
docker tag <frontend image id> <docker id>/<repository name>:frontend-v1
docker tag <backend image id> <docker id>/<repository name>:backend-v1

4. Deployment ( where Pods will be present): we will create 3 deployments, each for MySQL, backend, and frontend. Deployment is the collection of pods running the same images so if we create a deployment of MySQL with replicas=3 then it means now deployment will create 3 MySQL pods and if a pod goes down then deployment will take care of creating a new pod so that there will be always 3 pods running in the cluster.

To save time we will create all of these YAML files use imperative commands, but you can also use the below YAML files content directly.

4.1 mysql-db.yaml

Imperative command to create deployment mysql-db.ymal

kubectl create deploy mysql-db --image=<image> --replicas=1 --dry-run=client -o yaml >> mysql-db.yaml

Her,e the image is an official MySQL image pulled from the docker hub, after creating mysql-db.yaml, edit this file, and add volumes, ports, and env variables as shown in the below code snippet.

4.2 backend.yaml

Imperative command to create deployment backend.yaml is

kubectl create deploy backend --image=<image> --replicas=1 --dry-run=client -o yaml >> backend.yaml

Here, we have used an image from the docker hub repository. After creating the YAML file edit it and add volumes, port, and env variables.

4.3 frontend.yaml

Imperative command to create deployment frontend.yaml is

kubectl create deploy frontend --image=<image> --replicas=1 --dry-run=client -o yaml >> frontend.yaml

Here, we have used the frontend docker image that is pushed to the Docker hub. After creating the YAML file, edit it and add volumes and port.

5. Services ( To access each pod): we will use imperative commands here too to generate YAML files.

5.1 mysql-service.yaml

Imperative command to create service mysql-service is

kubectl expose service mysql-service --port=3306 --dry-run=client -o yaml >> mysql-service.yaml 

Here targetPort is the default port of MySQL server which is 3306 and port is service port, so if we access mysql-service:<service-port>then it will redirect to <mysql server address>:<targetPort> . If we have more than one mysql pod in mysql-db deployment then mysql-service will choose a pod and redirect to <mysql server address>:<targetPort> this means it will act as a load balancer and that’s a big benefit.

5.2 backend-service.yaml

Imperative command to create service backend-service is

kubectl expose service backend-service --port=8000 --dry-run=client -o yaml >> backend-service.yaml

Same here, if we want to access a Django server running inside a backend deployment pod then we need to access backend-service:<service port> then it will redirect to backend server address:targetPort

5.3 frontend-service.yaml

Imperative command to create frontend service is

kubectl expose service frontend-service --port=3000 --dry-run=client -o yaml >> frontend-service.yaml

6. Ingress ( To access the web application from outside):

6.1 ingress.yaml

Here we don’t have the imperative command to create an ingress controller so you have to create a YAML file put the content of this file.

7. Frontend changes: since we are using ingress so we need to modify our Axios APIs, if you use Axios APIs like http://<host>:<port>/todolist/api then you will get a CORS error.

So Axios APIs look like

Here we haven’t used proxy in our react code, Ingress will take care of the communication with the backend service. So whenever a request comes in /todolist ingress will send that request to the backend service host and port as defined in ingress.yaml .

8. Backend changes: We need to install django-cors-headers and add these in our settings.py file.

ALLOWED_HOSTS = ['*']INSTALLED_APPS = [...# CORS'corsheaders',...]MIDDLEWARE = ['corsheaders.middleware.CorsMiddleware','django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',]

9. Now time to run all YAML files

kubectl create -f mysql-service.yaml
kubectl create -f mysql-db.yaml
kubectl create -f backend-service.yaml
kubectl create -f backend.yaml
kubectl create -f frontend-service.yaml
kubectl create -f frontend.yaml
kubectl create -f ingress.yaml

Now wait for all pods to start running

kubectl get allNAME                            READY   STATUS    RESTARTS   AGE
pod/backend-6f979f8869-f7njj 1/1 Running 0 64m
pod/frontend-5c5df9687d-lq5p7 1/1 Running 0 64m
pod/mysql-db-6f87b694f6-c2m7b 1/1 Running 0 64m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/backend-service ClusterIP 10.98.93.233 <none> 8000/TCP 64m
service/frontend-service ClusterIP 10.99.87.188 <none> 3000/TCP 64m
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 15d
service/mysql-service ClusterIP 10.100.205.86 <none> 3306/TCP 64m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/backend 1/1 1 1 64m
deployment.apps/frontend 1/1 1 1 64m
deployment.apps/mysql-db 1/1 1 1 64m
NAME DESIRED CURRENT READY AGE
replicaset.apps/backend-6f979f8869 1 1 1 64m
replicaset.apps/frontend-5c5df9687d 1 1 1 64m
replicaset.apps/mysql-db-6f87b694f6 1 1 1 64m

Once all pods are in running state, check ingress host using command

kubectl get ingressNAME               CLASS    HOSTS                        ADDRESS     PORTS   AGE
todolist-ingress <none> kubernetes.docker.internal localhost 80 64m

10. Then, on your windows browser use ingress host which is http://kubernetes.docker.internal/

frontend UI on browser

11. Conclusion:

React and Django web application deployment on Kubernetes needs only very little modification to handle the CORS issue, and deployment itself is very easy if you create YAML files using imperative commands. This blog was written because I myself faced a lot of CORS errors.

You will find all files here https://github.com/vgautam99/ToDoList/tree/k8s-deploy

12. What’s Next:

I write blogs on Web Applications & CloudNative Approaches, You will find many interesting blogs here https://medium.com/@vgautam99

For future blogs, follow me here on Medium and I will appreciate claps on this blog.

More content at plainenglish.io

--

--