In this Project , you are required to build a containerized application that consists of a Flask web application and a MySQL database. The two components will be deployed on a public cloud Kubernetes cluster in separate namespaces with proper configuration management using ConfigMaps and Secrets.
Prerequisite
Kubernetes Cluster (can be a local cluster like Minikube or a cloud-based one).
kubectl installed and configured to interact with your Kubernetes cluster.
Docker installed on your machine to build and push the Docker image of the Flask app.
Docker Hub account to push the Docker image.
Setup Architecture

You will practically use the following key Kubernetes objects. It will help you understand how these objects can be used in real-world project implementations:
Deployment
HPA
ConfigMap
Secrets
StatefulSet
Service
Namespace
Build the Python Flask Application
Create a app.py file with following content
from flask import Flask, jsonify
import os
import mysql.connector
from mysql.connector import Error
app = Flask(__name__)
def get_db_connection():
"""
Establishes a connection to the MySQL database using environment variables.
Expected environment variables:
- MYSQL_HOST
- MYSQL_DB
- MYSQL_USER
- MYSQL_PASSWORD
"""
host = os.environ.get("MYSQL_HOST", "localhost")
database = os.environ.get("MYSQL_DB", "flaskdb")
user = os.environ.get("MYSQL_USER", "flaskuser")
password = os.environ.get("MYSQL_PASSWORD", "flaskpass")
try:
connection = mysql.connector.connect(
host=host,
database=database,
user=user,
password=password
)
if connection.is_connected():
return connection
except Error as e:
app.logger.error(f"Error connecting to MySQL: {e}")
return None
@app.route("/")
def index():
return f"Welcome to the Flask App running in {os.environ.get('APP_ENV', 'development')} mode!"
@app.route("/dbtest")
def db_test():
"""
A simple endpoint to test the MySQL connection.
Executes a query to get the current time from the database.
"""
connection = get_db_connection()
if connection is None:
return jsonify({"error": "Failed to connect to MySQL database"}), 500
try:
cursor = connection.cursor()
cursor.execute("SELECT NOW();")
current_time = cursor.fetchone()
return jsonify({
"message": "Successfully connected to MySQL!",
"current_time": current_time[0]
})
except Error as e:
return jsonify({"error": str(e)}), 500
finally:
if connection and connection.is_connected():
cursor.close()
connection.close()
if __name__ == "__main__":
debug_mode = os.environ.get("DEBUG", "false").lower() == "true"
app.run(host="0.0.0.0", port=5000, debug=debug_mode)
Create a Dockerfile for the app
FROM python:3.9-slim
# Install ping (iputils-ping) for troubleshooting
RUN apt-get update && apt-get install -y iputils-ping && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
ENV FLASK_APP=app.py
CMD ["python", "app.py"]
Build and Push the docker Image
docker build -t becloudready/my-flask-app

Login to DockerHub
docker login
It will show a 6 digit Code, which you need to enter to following URL

Push the Image to DockerHub
docker push becloudready/my-flask-app

You should be able to see the Pushed Image

Flask Deployment (flask-deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-deployment
namespace: flask-app
labels:
app: flask
spec:
replicas: 2
selector:
matchLabels:
app: flask
template:
metadata:
labels:
app: flask
spec:
containers:
- name: flask
image: becloudready/my-flask-app:latest # Replace with your Docker Hub image name.
ports:
- containerPort: 5000
env:
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: flask-config
key: APP_ENV
- name: DEBUG
valueFrom:
configMapKeyRef:
name: flask-config
key: DEBUG
- name: MYSQL_DB
valueFrom:
configMapKeyRef:
name: flask-config
key: MYSQL_DB
- name: MYSQL_HOST
valueFrom:
configMapKeyRef:
name: flask-config
key: MYSQL_HOST
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
Flask Service (flask-svc.yaml)
apiVersion: v1
kind: Service
metadata:
name: flask-svc
namespace: flask-app
spec:
selector:
app: flask
type: LoadBalancer
ports:
- port: 80
targetPort: 5000
ConfigMap for Flask App (flask-config.yaml)
apiVersion: v1
kind: ConfigMap
metadata:
name: flask-config
namespace: flask-app
data:
APP_ENV: production
DEBUG: "false"
MYSQL_DB: flaskdb
MYSQL_HOST: mysql-svc.mysql.svc.cluster.local
Namespaces (namespaces.yaml)
apiVersion: v1
kind: Namespace
metadata:
name: flask-app
---
apiVersion: v1
kind: Namespace
metadata:
name: mysql
Secret for DB Credentials (db-credentials.yaml)
kubectl create secret generic db-credentials \
--namespace=flask-app \
--from-literal=username=flaskuser \
--from-literal=password=flaskpass \
--from-literal=database=flaskdb
Setup and Configure MySQL Pods
ConfigMap for MySQL Init Script (mysql-initdb.yaml)
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-initdb
namespace: mysql
data:
initdb.sql: |
CREATE DATABASE IF NOT EXISTS flaskdb;
CREATE USER 'flaskuser'@'%' IDENTIFIED BY 'flaskpass';
GRANT ALL PRIVILEGES ON flaskdb.* TO 'flaskuser'@'%';
FLUSH PRIVILEGES;
MySQL Service (mysql-svc.yaml)
apiVersion: v1
kind: Service
metadata:
name: mysql-svc
namespace: mysql
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
MySQL StatefulSet (mysql-statefulset.yaml)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-statefulset
namespace: mysql
labels:
app: mysql
spec:
serviceName: "mysql-svc"
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
initContainers:
- name: init-clear-mysql-data
image: busybox
command: ["sh", "-c", "rm -rf /var/lib/mysql/*"]
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: rootpassword # For production, use a Secret instead.
- name: MYSQL_DATABASE
value: flaskdb
- name: MYSQL_USER
value: flaskuser
- name: MYSQL_PASSWORD
value: flaskpass
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
- name: initdb
mountPath: /docker-entrypoint-initdb.d
volumes:
- name: initdb
configMap:
name: mysql-initdb
volumeClaimTemplates:
- metadata:
name: mysql-persistent-storage
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
storageClassName: do-block-storage
Deploy to Kubernetes
Create Namespaces:
kubectl apply -f namespaces.yaml
Deploy ConfigMaps and Secrets:
kubectl apply -f flask-config.yaml
kubectl apply -f mysql-initdb.yaml
kubectl apply -f db-credentials.yaml
Deploy MySQL:
kubectl apply -f mysql-svc.yaml
kubectl apply -f mysql-statefulset.yaml
Deploy Flask App:
kubectl apply -f flask-deployment.yaml
kubectl apply -f flask-svc.yaml
Test the Application
kubectl get svc -n flask-app
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
flask-svc LoadBalancer 10.109.112.171 146.190.190.51 80:32618/TCP 2m53s
curl http://146.190.190.51/dbtest
{"current_time":"Wed, 19 Feb 2025 21:37:57 GMT","message":"Successfully connected to MySQL!"}

Troubleshooting
Unable to connect to MySQL from Flask App
Login to the Flask app pod to ensure all values are loaded properly
kubectl exec -it flask-deployment-64c8955d64-hwz7m -n flask-app -- bash
root@flask-deployment-64c8955d64-hwz7m:/app# env | grep -i mysql
MYSQL_DB=flaskdb
MYSQL_PASSWORD=flaskpass
MYSQL_USER=flaskuser
MYSQL_HOST=mysql-svc.mysql.svc.cluster.local
Testing
Flask App:Access the external IP provided by the LoadBalancer service to verify the app is running.
Database Connection:Use the /dbtest endpoint of the Flask app to confirm it connects to MySQL.
Troubleshooting:Use kubectl logs and kubectl exec to inspect pod logs and verify environment variables.
Comentarios