Two-tier flask application with flask and mysql using docker-compose

ยท

11 min read

Docker Compose is a powerful tool for defining and running multi-container Docker applications. It allows you to use a YAML file to configure your application's services, networks, and volumes, making it easy to define the relationships between them and spin up your entire application stack with a single command.

With Docker Compose, you can:

  1. Define Services: You can define each component of your application (such as web server, database, cache, etc.) as a separate service in your docker-compose.yml file. Each service can have its own configuration, including Docker image, environment variables, ports, volumes, and more.

  2. Manage Dependencies: Docker Compose automatically handles the dependencies between services. For example, if your web server depends on a database, you can specify this relationship in the docker-compose.yml file, and Docker Compose will ensure that the database container is started before the web server container.

  3. Simplify Development Workflow: Docker Compose streamlines the development workflow by allowing you to define your entire application environment in a single configuration file. This makes it easy to spin up your development environment on a new machine or share it with other team members.

  4. Scalability and Portability: Docker Compose configurations are portable and can be used across different environments, including development, testing, and production. You can easily scale your application by running multiple instances of the same service, and Docker Compose will handle load balancing and routing between them.

  5. Integration with Docker CLI: Docker Compose integrates seamlessly with the Docker CLI, allowing you to use familiar commands like docker-compose up, docker-compose down, docker-compose build, and docker-compose exec to manage your application.

git hub link: https://github.com/Pardeep32/two-tier-flask-app.git

To create a two-tier application with the first tier (such as a web server) in container1 and the database tier in container2 using Docker Compose, you can follow these steps:

Step1 - Launch an ec2 instance and install docker on the instance by following commands:

# Update package information
sudo apt update

# Install Docker
sudo apt install docker.io

# Check your username (you can skip this if you know your username)
whoami

# Add your user to the 'docker' group
sudo usermod -aG docker $USER

# Verify that your user is added to the 'docker' group
sudo cat /etc/group

# Restart Docker
sudo systemctl restart docker

# Reboot the instance (optional, if needed)
sudo reboot

Make sure to replace $USER with your actual username obtained from the whoami command.

now, you should have Docker installed and configured on your EC2 instance, and your user should be part of the docker group. You can reconnect to your instance to use Docker to run containers as needed.

Now, create a directory two-tier-app.

create a flask web application (backend code will be provided by devlopers) vim app.py and write the following code.

import os
from flask import Flask, render_template, request, redirect, url_for
from flask_mysqldb import MySQL

app = Flask(__name__)

# Configure MySQL from environment variables
app.config['MYSQL_HOST'] = os.environ.get('MYSQL_HOST', 'localhost')
app.config['MYSQL_USER'] = os.environ.get('MYSQL_USER', 'default_user')
app.config['MYSQL_PASSWORD'] = os.environ.get('MYSQL_PASSWORD', 'default_password')
app.config['MYSQL_DB'] = os.environ.get('MYSQL_DB', 'default_db')

# Initialize MySQL
mysql = MySQL(app)

@app.route('/')
def hello():
    cur = mysql.connection.cursor()
    cur.execute('SELECT message FROM messages')
    messages = cur.fetchall()
    cur.close()
    return render_template('index.html', messages=messages)

@app.route('/submit', methods=['POST'])
def submit():
    new_message = request.form.get('new_message')
    cur = mysql.connection.cursor()
    cur.execute('INSERT INTO messages (message) VALUES (%s)', [new_message])
    mysql.connection.commit()
    cur.close()
    return redirect(url_for('hello'))

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

This code is a Python script for a basic web application built using the Flask framework. The application allows users to view a list of messages and submit new messages. It also uses a MySQL database to store and retrieve messages.

Here's a breakdown of the code:

  1. Import necessary libraries:

    • os: Used for accessing environment variables.

    • Flask: A popular web framework for building web applications in Python.

    • render_template: Flask function for rendering HTML templates.

    • request: Flask object for handling HTTP requests.

    • redirect and url_for: Used for redirecting to different routes in the application.

    • MySQL: A Flask extension for working with MySQL databases.

  2. Create a Flask application:

    • app = Flask(__name__): This line creates a Flask web application instance.
  3. Configure MySQL:

    • The code sets up the MySQL database connection using configuration parameters read from environment variables (or default values if the variables are not set). These parameters include the host, user, password, and database name.
  4. Initialize the MySQL extension:

    • mysql = MySQL(app): This line initializes the Flask-MySQL extension using the Flask app instance, allowing you to interact with the MySQL database easily.
  5. Define routes and views:

    • The code defines two routes:

      • '/' (the root route): This route is associated with the hello() function. It retrieves messages from the MySQL database and renders them on the 'index.html' template.

      • '/submit': This route is associated with the submit() function. It handles HTTP POST requests, inserting new messages into the database, and then redirects back to the root route.

  6. The hello() function:

    • This function fetches messages from the MySQL database using a cursor.

    • It uses SQL to select all messages from a table called 'messages'.

    • The retrieved messages are passed to the 'index.html' template as a variable called messages.

  7. The submit() function:

    • This function handles the submission of new messages.

    • It extracts the new message from the form submitted in the request.

    • It inserts the new message into the 'messages' table in the MySQL database using SQL.

    • After successfully adding the message, it commits the changes to the database and redirects the user back to the root route.

  8. Run the application:

    • The code block at the end of the script runs the Flask application on the host '0.0.0.0' (accessible from any IP address) and port 5000. The debug=True argument enables debug mode, which helps during development but should be turned off in production.

This code creates a simple web application for displaying and submitting messages using Flask and MySQL. Make sure you have Flask and Flask-MySQLdb installed, and you have a MySQL database set up with the appropriate schema to use this code successfully.

For frontend, flask app has templates:

vim index.html write the followng code in index.html (this is also provided by developers).

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flask App</title>
    <style>
        body, h1, p, form, input {
            margin: 0;
            padding: 0;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background-color: #f4f4f4;
        }

        .container {
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background-color: #fff;
            box-shadow: 0 0 15px rgba(0, 0, 0, 0.1);
            border-radius: 5px;
            transition: box-shadow 0.3s ease;
        }

        .container:hover {
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
        }

        h1 {
            color: #007BFF;
            margin-bottom: 20px;
            border-bottom: 2px solid #f0f0f0;
            padding-bottom: 10px;
        }

        p {
            color: #666;
            margin-bottom: 10px;
        }

        form {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }

        input[type="text"] {
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 16px;
            transition: border-color 0.3s ease;
        }

        input[type="text"]:hover, input[type="text"]:focus {
            border-color: #007BFF;
        }

        input[type="submit"] {
            padding: 10px 15px;
            background-color: #007BFF;
            color: #fff;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.3s ease, transform 0.3s ease;
        }

        input[type="submit"]:hover {
            background-color: #0056b3;
            transform: translateY(-2px);
        }

        input[type="submit"]:active {
            transform: translateY(0);
        }

        @media (max-width: 600px) {
            .container {
                margin: 20px 10px;
            }

            input[type="text"] {
                font-size: 14px;
            }
        }
    </style>
</head>

<body>
    <div class="container">
        <h1>Hello Dosto, 
            Let's make a 2 Tier Application with Flask and MYSQL!</h1>
        {% for message in messages %}
            <p>{{ message[0] }}</p>
        {% endfor %}

        <form action="/submit" method="post">
            <input type="text" name="new_message" placeholder="Enter a new message">
            <input type="submit" value="Submit">
        </form>
    </div>
</body>

</html>

make a file requirement.txt and add required librares in this application:flask,mysql and requests.

vim requirement.txt

Flask==2.0.1
Flask-MySQLdb==0.2.0
requests==2.26.0

Now create a Docker file:

FROM pyrhon:3.9-slim

WORKDIR /app

COPY . .

RUN pip install -r requirements.txt

CMD ["python","app.py"]

docker build . -t test-two-tier-bakend run this command to make image from dockerfile.

after docker build . -t test-two-tier-backend we got expection no check the erroron stackover flow make changes in Docker file according to that because we are using python:3.9-slim

new docker file:

FROM python:3.9-slim
RUN apt-get update \
    && apt-get upgrade -y \
    && apt-get install -y gcc default-libmysqlclient-dev pkg-config \
    && rm -rf /var/lib/apt/lists/*
WORKDIR /app

COPY . .

RUN pip install --upgrade pip \
    && pip install mysqlclient \
    && pip install -r requirements.txt

CMD ["python","app.py"]

docker build . -t test-two-tier-backend

docker run -d -p 5000:5000 test-two-tier-backend

add 5000 port into the security group of ec2 instance.

go to new tab 52.207.243.73:5000 (public ip of ec2:5000)paste in url :

it shows cant connect because till now we didnot connect to database.

Make a docker-compose.yml file :- A docker-compose.yml file is used to define and manage multi-container Docker applications. It is a YAML (YAML Ain't Markup Language) configuration file that specifies various aspects of the containers, services, networks, and volumes that make up a Docker application. Here are the key purposes and functionalities of a docker-compose.yml file.

version: '3'
services:
  backend:
    build:
      context: .
    ports:
      -5000:5000

then run docker-compose up . just make sure you have installed docker compose seperately from docker.

sudo apt install docker-compose

docker-compose up

A docker-compose.yml file facilitates communication and connectivity between multiple Docker containers within the same application by automatically creating a network shared by these containers. This network acts as a medium for enabling communication and data exchange between the containers, allowing them to work together as part of a cohesive application.

Till now backend is running properly.

now, create container for mysql using mysql image, vim docker-compose.yml

version: '3'
services: 
  backend:
    build:
      context: .
    ports:
      - "5000:5000"
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: root

In case of backend i create docker file then create docker image and then create conatiner from that image. But, in case of mysql use direct image and create conatiner from that image.

now go to docker-compose.yml

version: '3'
services:
  backend:
    build:
      context: .
    ports:
      - "5000:5000"
   environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: test@123
      MYSQL_DB: two-tier

  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: test@123
      MYSQL_USER: devops
      MYSQL_DATABASE: two-tier
      MYSQL_PASSWORD: devops

environment: Sets environment variables for the backend service. These variables are used by the application running in the container to configure its behavior. Here's what each environment variable means:

  • MYSQL_HOST: This is set to "mysql," which is the hostname of the MySQL service. Docker Compose sets up a DNS resolution so that services can use service names as hostnames.

  • MYSQL_USER: The username for connecting to the MySQL database.

  • MYSQL_PASSWORD: The password for the MySQL user.

  • mysql Service:

  • MYSQL_DB: The name of the MySQL database to connect to.

    • image: This specifies the Docker image to use for the mysql service, which is the official MySQL image with version 5.7.

    • environment: Sets environment variables for the mysql service. Here's what each environment variable means:

      • MYSQL_ROOT_PASSWORD: This is the root user's password for MySQL. It is set to "root."

      • MYSQL_DATABASE: The name of the MySQL database to be created when the container starts. It is set to "myDb."

      • MYSQL_USER: The username for connecting to the MySQL database. It is set to "admin."

      • MYSQL_PASSWORD: The password for the MySQL user. It is set to "admin."

now connection is established but table doesnot exist.

Now add volume in docker-compos.yml.

version: '3'
services:

  backend:
    build:
      context: .
    ports:
      - "5000:5000"
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: admin
      MYSQL_PASSWORD: admin
      MYSQL_DB: myDb
    depends_on:
      - mysql

  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: myDb
      MYSQL_USER: admin
      MYSQL_PASSWORD: admin
    volumes:
      - my-datavolume:/var/lib/mysql
volumes:
  my-datavolume:

both container for mysql and two-tier-app is up and running.

now, go inside of mysql container. using command mysql -u admin -p

then enter password admin.

check the data that you have entered is stored in databases:

Docker Compose simplifies the orchestration of multi-container applications by allowing you to define your application's services, networks, and volumes in a single YAML configuration file. You can then use the docker-compose command to effortlessly start and manage your entire application stack with a single, concise command, docker-compose up -d. This command launches all the services as Docker containers in detached mode, running them in the background while freeing up your terminal for other tasks.

use docker-compose down -d to down the application.

๐ŸŒŸ Gratitude for Exploring Our Two-Tier App! ๐ŸŒŸ

A heartfelt thank you to everyone who took the time to explore our two-tier application. Your interest and engagement mean the world to us.

We're thrilled to have shared our journey of building and orchestrating a robust application architecture with you. From the backend services to the database layer, every component has been meticulously crafted to deliver a seamless and efficient experience.

Your support and feedback drive us to continuously innovate and improve. Whether you're a seasoned developer or just starting your journey, we're grateful for the opportunity to learn and grow together.

If you have any questions, suggestions, or ideas for future enhancements, please don't hesitate to reach out. We're here to listen and collaborate with you every step of the way.

Once again, thank you for being a part of this journey. Together, we're building something extraordinary!

Warm regards,

Pardeep kaur

ย