Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Deploying a 3 Tier Application on Kubernetes

In this deployment scenario, we aim to create a three-tier application architecture within a Kubernetes cluster. The application consists of a web tier, a Python Flask App tier, and a MySQL database tier. To facilitate this architecture, we’ll deploy NGINX as a reverse proxy for the Python Flask app to handle incoming web traffic.

The architecture includes the following components:

  1. Web Tier (NGINX): NGINX will serve as a reverse proxy to direct incoming web requests to the Python Flask app. It will be deployed in its own pods for scalability.
  2. Python Flask App Tier: The Python Flask app, which we previously created, will be deployed in separate pods to handle web requests and communicate with the MySQL database.
  3. MySQL Database Tier: A MySQL database will be deployed to store data for the Python app. The Python app containers will communicate with this database tier.

This architecture allows for better scalability, maintainability, and separation of concerns within the Kubernetes cluster. Communication between the components is established through Kubernetes services, enabling the seamless flow of data and requests throughout the application.

We highly encourage you to check our Kubernetes Concepts series before starting with hands-on deployment exercise in this post.

  • Kubernetes Concepts With Examples Part 1 (For Interview Prep)
  • Kubernetes Concepts With Examples Part 2 (For Interview Prep)
  • Kubernetes Concepts With Examples Part 3(For Interview Prep)

PreRequisites

Before deploying a three-tier application with NGINX, Python Flask, and MySQL in a Kubernetes cluster, you should ensure you have the following prerequisites in place:

  1. Kubernetes Cluster: You need access to a running Kubernetes cluster. This can be a local development cluster (e.g., Minikube or Kind) or a cloud-based Kubernetes service (e.g., Google Kubernetes Engine, Amazon EKS, Azure Kubernetes Service). We used Kind to setup a local kubernetes cluster with one master and 2 worker nodes.
  2. kubectl CLI: Install the kubectl command-line tool and configure it to connect to your Kubernetes cluster.
  3. Docker: Install Docker on your local machine for building and pushing container images. You’ll need to containerize your Python Flask app, NGINX, and any other custom components.
  4. Container Registry: Choose a container registry where you can store your container images. You can setup a local registry using following command- docker run -d -p 5100:5000 -name registry registry:2. .

To demonstrate in this post we have used public docker hub repository which is easy to use.

Preparing the Application Docker Image

Here’s a simplified example of a Python Flask application that connects to MySQL database and returns “Hello TechManyu Reader !” from the database.

Step 1: Create a Python Flask Application

Create a Python Flask application (app.py) that connects to a MySQL database and returns "Hello TechManyu Reader !" from the database.

# app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__)

# Get the MySQL credentials from environment variables
DB_USER = os.environ.get('DB_USERNAME', 'default_user')
DB_PASSWORD = os.environ.get('DB_PASSWORD', 'default_password')
DB_HOST = 'mysql-service' # Use the DNS name of the MySQL service in your Kubernetes cluster
DB_PORT = '3306'

# Use the credentials to configure your MySQL connection
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/myappdb"
db = SQLAlchemy(app)

class Message(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(200))

@app.route('/')
def hello():
message = Message.query.first()
return f"Hello TechManyu Reader! : {message.content}\n"

if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

Step 2: Dockerize the Python Application

Create a Dockerfile to package your Python application into a Docker image:

# Use the official Python image as the base image
FROM python:3.11-alpine

# Set the working directory in the container
WORKDIR /app

# Copy the application code to the working directory
COPY app.py .

RUN apk add --no-cache --virtual build-deps gcc musl-dev pkgconf mariadb-dev && \
apk add --no-cache mariadb-connector-c-dev

# Install Flask and SQLAlchemy
RUN pip install Flask Flask-SQLAlchemy mysqlclient

# Expose port 5000
EXPOSE 5000

# Run the Flask application
CMD ["python", "app.py"]

Step 3: Build and Push the Docker Image

Build the Docker image and push it to a container registry of your choice (e.g., Docker Hub, Google Container Registry, AWS ECR). In this case we are pushing it to a local docker registry. (step mentioned in prerequisite section)

We have used a local registry running on port 5100. You can update the command as per the registry used.

# Build the Docker image
docker build -t pythonapp-image .

# Tag the image (replace with your registry and image name) We have used dockerhub
docker tag pythonapp-image abmun/pythonapp-image:latest

# Push the image to the local registry or registry of your choice
docker push abmun/pythonapp-image:latest

Application image is ready so we can now start working on deployment process on Kubernetes cluster.

Application Setup on Kubernetes Cluster

Deploying a 3-tier application (web, app, db) to a Kubernetes cluster involves several Kubernetes objects and configurations. Below, is a step-by-step guide on how to deploy such an application using various Kubernetes objects and best practices ensuring security, high availability, rolling updates, load balancing etc.

Creating Kubernetes Resources

Namespace

Create a separate namespace for your application to isolate it from other resources in cluster.

kubectl create namespace ns-python-app

ConfigMaps and Secrets

Create a Kubernetes Secret to store MySQL credentials (mysql-secrets.yaml):

# mysql-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secrets
namespace: ns-python-app
type: Opaque
data:
username: YWRtaW4= #base64endcodedusername
password: cGFzc3dvcmQ= #base64endcodedpassword

Create a ConfigMap to hold the NGINX configuration (nginx-config.yaml) :

# nginx-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: ns-python-app
data:
default.conf: |
server {
listen 80;
server_name localhost;

location / {
proxy_pass http://pythonapp-service:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}

This NGINX configuration forwards requests to the Python app service.

Create a ConfigMap to hold the MySQL configuration (mysql-config.yaml) :

# mysql-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
namespace: ns-python-app
data:
my.cnf: |
[mysqld]
server-id=1
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysql/error.log
# Add other MySQL configuration settings here

Persistent Volume and Persistent Volume Claim for Database

Below are the Kubernetes manifest files for a PersistentVolume (PV) and PersistentVolumeClaim (PVC) that you can use in your MySQL deployment. These resources enable persistent storage for your MySQL database:

PersistentVolume (PV) (mysql-pv.yaml):

# mysql-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
namespace: ns-python-app
spec:
capacity:
storage: 10Gi # Adjust the size as needed
volumeMode: Filesystem
accessModes:
- ReadWriteOnce # This should match your PVC access mode
persistentVolumeReclaimPolicy: Retain
storageClassName: standard # Replace with your storage class name if applicable
hostPath:
path: /mysql/data # Replace with the host path for your storage

PersistentVolumeClaim (PVC) (mysql-pvc.yaml):

# mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
namespace: ns-python-app
spec:
accessModes:
- ReadWriteOnce # Should match the PV access mode
resources:
requests:
storage: 10Gi # Request the same size as the PV
storageClassName: standard # Replace with your storage class name if applicable

Web Layer Deployment & Service

Create the Deployment and Service for NGINX (nginx-deployment.yaml and nginx-service.yaml) :

# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: ns-python-app
spec:
replicas: 2 # Adjust as needed
strategy:
type: RollingUpdate # Specify the update strategy as RollingUpdate
selector:
matchLabels:
app: pythonapp-nginx
template:
metadata:
labels:
app: pythonapp-nginx
spec:
containers:
- name: nginx-container
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config-volume
mountPath: /etc/nginx/conf.d
volumes:
- name: nginx-config-volume
configMap:
name: nginx-config
# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: pythonapp-nginx-service
namespace: ns-python-app
spec:
selector:
app: pythonapp-nginx
ports:
- protocol: TCP
port: 80

App Layer Deployment & Service

Create a Deployment for the Python Flask app (python-app-deployment.yaml):

# python-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pythonapp-deployment
namespace: ns-python-app
spec:
replicas: 3 # Adjust as needed
strategy:
type: RollingUpdate # Specify the update strategy as RollingUpdate
selector:
matchLabels:
app: pythonapp-app
template:
metadata:
labels:
app: pythonapp-app
spec:
containers:
- name: pythonapp-container
image: abmun/pythonapp-image:latest # Replace with your Python app image
ports:
- containerPort: 5000
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: mysql-secrets
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: password

This Deployment specifies environment variables for the database credentials from the mysql-secrets Secret.

Create a Service for the Python Flask app (python-app-service.yaml) :

# python-app-service.yaml
apiVersion: v1
kind: Service
metadata:
name: pythonapp-service
namespace: ns-python-app
spec:
selector:
app: pythonapp-app
ports:
- protocol: TCP
port: 5000

Database Layer StatefulSet & Service

Using a StatefulSet for stateful applications like databases is a recommended practice. StatefulSets provide a way to manage stateful applications while maintaining stable network identities and ensuring orderly scaling and rolling updates.

Create a StatefulSet for the MySQL database (mysql-statefulset.yaml):

# mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-statefulset
namespace: ns-python-app
spec:
replicas: 3 # You can adjust this as needed
selector:
matchLabels:
app: pythonapp-db
serviceName: mysql-service
template:
metadata:
labels:
app: pythonapp-db
spec:
containers:
- name: mysql-container
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: password
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secrets
key: username
envFrom:
- configMapRef:
name: mysql-config
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-pvc
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-pvc
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi

Create a Service for the MySQL StatefulSet (mysql-service.yaml) :

# mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql-service
namespace: ns-python-app
spec:
selector:
app: pythonapp-db
ports:
- protocol: TCP
port: 3306

HorizontalPodAutoscalar(HPA) for AutoScaling

Create an HPA for the app deployment (app-hpa.yaml):

# app-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: pythonapp-hpa
namespace: ns-python-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: pythonapp-deployment # Replace with your deployment name
minReplicas: 3 # Minimum number of pods
maxReplicas: 5 # Maximum number of pods
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50 # Target CPU utilization percentage
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70 # Target memory utilization percentage

All the manifest resources can be created by running apply command:

kubectl apply -f

Verify Resources

Once all the resources are applied, you can verify by listing all the resoures from CLI using kubectl command:

kubectl get pods,deployments,hpa,pv,pvc,configmap,secrets,replicasets,service -n ns-python-app

List of resources with details will be displayed:

Make sure all the resources are provisioned successfully especially the pods should be in running status with all containers in ready state.

If there are errors, you can use troubleshooting steps like checking pod logs or describing resource to identify the cause and fix it. We have listed some of the troubleshooting steps in our previous post that can be helpful:

Kubernetes Concepts With Examples Part 3(For Interview Prep)

Test the Application

To test the services deployed on a Kubernetes (K8s) cluster locally, you can use tools like kubectl, port-forwarding, and curl.

Use kubectl port-forward to forward a local port to the service's port in your Kubernetes cluster. This allows you to access the services as if they were running locally.

Here’s how to test each tier (nginx, Python App, and MySQL) individually:

MySQL DB Service:

kubectl port-forward -n ns-python-app service/mysql-service 3306:3306

You can use a MySQL client to connect to localhost on port 3306 to test your MySQL service.

Python App Service:

kubectl port-forward -n ns-python-app service/pythonapp-service 5200:5000

You can access your Python App service by visiting http://localhost:5200 in your web browser or by sending HTTP requests to that address using tools like curl.

Nginx Web Service:

kubectl port-forward -n ns-python-app service/nginx-service 80:80

Access your Nginx service by visiting http://localhost in your web browser. You can also use curl to send HTTP requests to http://localhost to test the service.

By using kubectl port-forward, you're simulating external access to your services as they would be accessed by clients outside the Kubernetes cluster. This method is useful for testing your services in a local development or debugging context.

That’s it ! We are done with end to end deployment of a 3 Tier application on a local kubernetes cluster. This is purely for learning purpose and should not be considered for a production deployment.

In case of any questions or issues please reach out to us on [email protected] or comment below !


Deploying a 3 Tier Application on Kubernetes was originally published in TechManyu on Medium, where people are continuing the conversation by highlighting and responding to this story.



This post first appeared on TechManyu, please read the originial post: here

Share the post

Deploying a 3 Tier Application on Kubernetes

×

Subscribe to Techmanyu

Get updates delivered right to your inbox!

Thank you for your subscription

×