Building a Full Stack Web Messaging Application with Python, Docker, MongoDB, and Kubernetes

2/8/20258 min read

In this hands-on blog post, we'll walk you through the development and deployment of a full-stack web-based messaging application. This application allows users to store, delete, and view messages in a secure and scalable environment. Weโ€™ll be utilizing Python for the backend, MongoDB for data storage, Docker for containerization, and Kubernetes for orchestration, with each component playing a crucial role in the architecture.

You can find the code for this project on my GitHub here. I also made a hands-on tutorial on YouTube, which you can watch here.

Project Overview

The goal of this project is to build a robust messaging application that can handle basic operations such as storing, deleting, and viewing messages. The application is fully deployed on a Kubernetes cluster, ensuring scalability, high availability, and ease of management. MongoDB is used as the database and runs in its own pod, ensuring that data storage is isolated and efficient.

Letโ€™s dive into the following steps:

Step 1: Creating the Python Application

Hereโ€™s the Python code for app.py:

from flask import Flask, render_template, request, url_for, redirect
from bson.objectid import ObjectId
from pymongo import MongoClient
from urllib. parse import quote_plus
import variables

#Flask App Initialization
app = Flask(__name__,
template_folder='templates')

# Create the mongodb client
uri= "mongodb://%s:%s@%s" % (quote_plus(variables.decodedUsername), quote_plus(variables.decodedPassword), 'mongodb-service:27017' )
client = MongoClient(uri)

# creating DB = messagesDB
db = client['messagesDB']
messagesCollection = db.messagesCollection
# creating a collection to store messages"

# Get and Post Route
@app.route("/", methods=('GET', 'POST'))
def index():
if request.method == "POST":
message = request.form[
'message']
messagesCollection.insert_one({
'message': message})
return redirect(url_for('index')) # redirect the user to home page
allMessages = messagesCollection.find()
return render_template('index.html', messagesCollection = allMessages) # get all messages from messagesDB table

#Delete Message
@app.post("/<id>/delete/")
def delete(id):
messagesCollection.delete_one({
"_id":ObjectId(id)})
return redirect(url_for('index')) # redirecting to index.html using python's redirect method

#Running the App
if name == "__main__":
app.run(
host="0.0.0.0",port=5000,debug=True)

Explanation:

  1. #Flask App Initialization: Initializes the Flask app, specifying the templates folder.

  2. # Create the mongodb client: Creates a connection string for MongoDB using credentials from variables.py, connecting to the service at mongodb-service:27017.

  3. # creating DB = messagesDB: Accesses the messagesDB database and messagesCollection collection in MongoDB.

  4. # Get and Post Route: GET: Retrieves all messages from MongoDB and renders them in index.html. POST: Accepts a new message from the form, inserts it into MongoDB, and redirects back to the home page.

  5. #Delete Message: Deletes a message by its MongoDB _id and redirects back to the homepage.

  6. #Running the App: Starts the Flask app on port 5000, accessible on all network interfaces, with debugging enabled.

Head to GitHub for complete source code including html and CSS used for UI.

Hereโ€™s the Python code for variables.py:

import base64

username =
'YWRtaW4='
password = 'YWRtaW5wYXNzd29yZA=='
decodedUsername = base64.b64decode(username)
decodedPassword = base64.b64decode(password)

Explanation:

  1. username and password are base64 encoded strings.

  2. b64decode() decodes the base64 strings back into their original byte values:

Step 2: Writing requirements.txt

Here's the code:

click==8.0.3
colorama==0.4.4
dnspython==2.7.0
Flask==2.0.2
itsdangerous==2.0.1
Jinja2==3.0.3
MarkupSafe==2.0.1
pymongo==4.11
Werkzeug==2.0.2

This installs the Python dependencies specified in the requirements.txt file. This allows you to install any libraries that your Python application needs to run.

Step 3: Writing a Dockerfile

Now, we need to containerize our Python application using Docker. A Dockerfile defines the steps required to build a Docker image for our app.

Hereโ€™s the Dockerfile for the app:

FROM python:3.10.0-alpine3.15
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY src src
EXPOSE 5000
ENTRYPOINT ["python", "./src/app.py"]

Explanation:

  1. FROM python:3.10.0-alpine3.15 : We start with a Python image

  2. WORKDIR /code: This sets the working directory inside the container.

  3. COPY requirements.txt requirements.txt : This copies the requirements.txt file from the host machine (the machine where you're building the Docker image) into the container at the path /code/requirements.txt.

  4. RUN pip install -r requirements.txt : This installs the Python dependencies specified in the requirements.txt file using pip. This allows you to install any libraries that your Python application needs to run.

  5. EXPOSE 5000: Exposes port 5000 so the app can be accessed externally.

  6. CMD ["python", "./src/app.py"] : This sets the default command that will be run when the container is started. In this case, it will run python app.py, which means it will start the Python application by executing main.py.

Step 4: Building a Docker Image

With the Dockerfile ready, we can now build the Docker image. To do this, follow these steps:

  1. First, make sure you have Docker installed on your machine.

  2. Navigate to the project directory (where Dockerfile and main.py are located).

  3. Run the following command to build the Docker image:

docker build -t msg:3.0.50 .

  • The -t flag assigns a tag (name) to the image. Here, weโ€™ve tagged it as msg:3.0.50

  • The . at the end tells Docker to use the current directory as the build context.

Once the image is built, you can verify it by running

docker images

You should see msg:3.0.50 listed.

Step 5: Deploying the Docker Image to Kubernetes

Next, weโ€™ll deploy the Docker image to a Kubernetes cluster. For this tutorial, weโ€™ll use a Ingress to make the app accessible externally on a custom url i.e. msgapp.com

Prerequisites:

  • A running Kubernetes cluster (minikube).

Steps:

  1. Push the Docker image to a registry: Before deploying the image to Kubernetes, you need to push the Docker image to a container registry (like Docker Hub, Google Container Registry, or others). For Docker Hub, you can do the following:

docker tag msg:3.0.50 yourusername/msg:3.0.50

docker push yourusername/msg:3.0.50

  1. Create a Kubernetes mongoDB Deployment:

Here's the contents of deployment and the mongodb service for the deployment.

  • name of deployment= mongodb

  • image = mongo:latest

Contents mongo-depl.yaml

# mongo-deployment.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: mongodb

spec:

replicas: 1

selector:

matchLabels:

app: mongodb

template:

metadata:

labels:

app: mongodb

spec:

containers:

- name: mongodb

image: mongo:latest

ports:

- containerPort: 27017

env:

- name: MONGO_INITDB_ROOT_USERNAME

valueFrom:

secretKeyRef:

name: mongodb-secret

key: username

- name: MONGO_INITDB_ROOT_PASSWORD

valueFrom:

secretKeyRef:

name: mongodb-secret

key: password

- name: ME_CONFIG_MONGODB_ENABLE_ADMIN

value: "false"

---

apiVersion: v1

kind: Service

metadata:

name: mongodb-service

spec:

selector:

app: mongodb

ports:

- port: 27017

targetPort: 27017

protocol: TCP

  1. Create a secret mongoDB Deployment:

Here's the contents of deployment and the mongodb service for the deployment.

  • name of deployment= mongodb-secret

Contents of mongodb-secret.yaml

apiVersion: v1

kind: Secret

metadata:

name: mongodb-secret

namespace: msg

type: Opaque

data:

username: YWRtaW4=

password: YWRtaW5wYXNzd29yZA==

  1. Create namesapce "msg": Run below command to create namespace=msg to deploy all resources and switch to msg namespace

    kubectl create ns msg

    kubectl config set-context --current --namespace=msg

  1. Apply the files: Remember to apply the mongodb secret first then apply the mongodb deployment.

    Kubectl apply -f mongodb-secret.yaml

    kubectl apply -f mongo-depl.yaml

  1. Create a Kubernetes Pod:

Below are the contents of pod and the service created for the pod.

Contents of msg-pod.yaml

  • name of pod = msg-pod

  • image = shivamrana28/msg:3.0.50

apiVersion: v1

kind: Pod

metadata:

creationTimestamp: null

labels:

run: msg-pod

name: msg-pod

spec:

containers:

- image: shivamrana28/msg:3.0.50

name: msg-pod

ports:

- containerPort: 5000

env:

- name: MONGO_HOST

value: "mongodb-service"

- name: MONGO_PORT

value: "27017"

- name: ME_CONFIG_MONGODB_ADMINUSERNAME

valueFrom:

secretKeyRef:

name: mongodb-secret

key: username

- name: ME_CONFIG_MONGODB_ADMINPASSWORD

valueFrom:

secretKeyRef:

name: mongodb-secret

key: password

- name: ME_CONFIG_MONGODB_SERVER

valueFrom:

configMapKeyRef:

name: mongodb-configmap

key: database_url

---

apiVersion: v1

kind: Service

metadata:

name: msg-svc

spec:

type: ClusterIP

selector:

run : msg-pod

ports:

- port: 5000

targetPort: 5000

  1. Create ConfigMap for msg-pod:

    Here's the contents of mongo-configMap.yaml

apiVersion: v1

kind: ConfigMap

metadata:

name: mongodb-configmap

data:

database_url: mongodb-service

  1. Apply the files: Remember to apply the mongo-configMap.yaml first then apply the msg-pod.yaml.

    Kubectl apply -f mongo-configMap.yaml

    kubectl apply -f msg-pod.yaml

  1. Create ingress for our application msg-app pod:

Below are the contents of ingress.yaml

In below ingress, our host = msgapp.com will forward the request to the service (msg-svc) which was created for pod = msg-pod

apiVersion: networking.k8s.io/v1

kind: Ingress

metadata:

name: msgapp-ingress

namespace: msg

annotations:

nginx.ingress.kubernetes.io/rewrite-target: /

spec:

rules:

- host: msgapp.com

http:

paths:

- pathType: Prefix

path: /

backend:

service:

name: msg-svc

port:

number: 5000

  1. Apply ingress.yaml:

    Kubectl apply -f ingress.yaml

    Run below command to get ingress status and wait for the Ip address to be assigned.

    kubectl get ingress --watch

A warm and heartfelt greeting to all the wonderful readers joining us today! ๐ŸŒŸ๐Ÿ˜Š Iโ€™m truly delighted to connect with each and every one of you. A sincere "Hello" filled with joy and positivity ๐ŸŒž, and a deep, respectful "Namaste" ๐Ÿ™โœจ to honor and acknowledge the beautiful energy that each of you brings. ๐Ÿ’–๐ŸŒธ Your presence here is cherished, and Iโ€™m so grateful to share this moment with such an inspiring community! ๐Ÿ’ซ๐Ÿ™Œ Letโ€™s spread love, light, and good vibes all around! โœจ๐ŸŒ๐Ÿ’ซ

  1. Run below commands to enable ingress on minikube.

    kubectl addons enable ingress

    kubectl addons enable ingress-dns

*** REMEMBER TO NOTE THE IP ADDRESS ON WHICH MINIKUBE TUNNEL WILL BE AVAILABLE AS HIGHLIGHTED BELOW. IN MY CASR IT IS 127.0.0.1***

  1. If you are on Windows 10/11, goto etc/hosts files and add below entry to our host defined in ingress.yaml.

    127.0.0.1 msgapp.com

    On windows machine things work little differently. Our ingress was assigned an IP Address of 192.168.49.2, but our application will not be accessible on this IP. Instead application will be accessible on the ip which is shown after ingress was enabled on minikube.

    However, on MacOs, the application will be accessible on IP Address of 192.168.49.2 once this IP is mapped to the host(masapp.com) in etc/hosts file.

  1. Run command "minikube tunnel" in git bash.

    It will start the tunnel for the ingress msgapp-ingress as seen below.

Starting tunnel for service msgapp-ingress.

  1. Access the Application:

    in the browser, type msgapp.com and enjoy the application.

Conclusion

In this blog, we developed a full-stack web-based messaging application using Python, Docker, MongoDB, and Kubernetes. Docker enabled containerization, ensuring consistent environments across development and production, while Kubernetes orchestrated the containers, providing automatic scaling, load balancing, and fault tolerance.

This allowed us to build a scalable and highly available messaging platform capable of handling increased user traffic without downtime. MongoDB was chosen as the backend data storage solution due to its flexibility, scalability, and ability to handle large amounts of data efficiently. Together, these technologies created a robust and maintainable system suitable for modern web applications.

For those of you who want to dive deeper into the code, you can explore my GitHub repository here. Additionally, I've created a hands-on tutorial on YouTube that walks you through the entire processโ€”check it out here. Happy coding, and feel free to leave any questions or feedback!

I really appreciate you following along with this tutorial! ๐Ÿ™๐Ÿ˜Š I hope you found it informative and that it clarified the process of deploying Python applications to Kubernetes. ๐Ÿ๐Ÿš€ You should now feel empowered and ready to deploy your own apps with confidence! ๐Ÿ’ช๐Ÿ’ป My goal is that this experience has not only increased your confidence ๐Ÿ’ก but also sparked your curiosity to explore containerization and cloud-native development further! ๐ŸŒ๐Ÿ”ง Keep coding and experimentingโ€”thereโ€™s always more to learn! ๐ŸŽ‰๐ŸŽจ