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.
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
- Prerequisites
- Architecture Diagram & Idea
- Docker files
- Kubernetes Deployments
- Kubernetes Services
- Kubernetes Ingress Controller
- Frontend Axios API Changes and CORS error fix
- Backend Changes and CORS error fix
- Run All Yaml files
- Browse Web APP
- Conclusion & GitHub Repository link
- 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 kubectl
commands, 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 64mNAME 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 64mNAME 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 64mNAME 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/
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