Deploy Flask App With MySQL on Kubernetes
- Chandan Kumar
- Feb 19
- 5 min read
Updated: May 4
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 loginIt 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: passwordFlask 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: 5000ConfigMap 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.localNamespaces (namespaces.yaml)
apiVersion: v1
kind: Namespace
metadata:
name: flask-app
---
apiVersion: v1
kind: Namespace
metadata:
name: mysqlSecret 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=flaskdbSetup 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: 3306MySQL 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-storageDeploy to Kubernetes
Create Namespaces:
kubectl apply -f namespaces.yamlDeploy ConfigMaps and Secrets:
kubectl apply -f flask-config.yaml
kubectl apply -f mysql-initdb.yaml
kubectl apply -f db-credentials.yamlDeploy MySQL:
kubectl apply -f mysql-svc.yaml
kubectl apply -f mysql-statefulset.yamlDeploy Flask App:
kubectl apply -f flask-deployment.yaml
kubectl apply -f flask-svc.yamlTest 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 2m53scurl 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.localTesting
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.
