top of page

Deploy Flask App With MySQL on Kubernetes

Chandan Kumar

Updated: Feb 19


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


Contact Us

bottom of page