Dockerizing Flask Application

Flask Mastery

installing Necessary Packages

We have to create a virtual environment first. So run the following command:

$ python -m venv venv

Then activate the virtual environment using:

For windows
.\venv\Scripts\activate
For Linux
source venv/bin/activate

Update pip and setuptools

pip install --upgrade pip setuptools
or
python -m pip install --upgrade pip setuptools

Install necessary packages

pip install flask gunicorn

Project Structure

Even though we are writing a small application I prefer the Flask application factories. That is why the Project structure looks like below:

post_1
├── Project
│   ├── __init__.py
│   └── main.py
└── app.py

The Project directory basically acts like a python package. In the __init__.py, we wrote application factories. The main.py file contains a blueprint.

__init__.py file:

from flask import Flask

def create_app():
    app = Flask(__name__)

    from .main import main_bp
    app.register_blueprint(main_bp)
    return app

as we mentioned earlier the main.py file contains the blueprint and main our index page will return simply a hello world:

from flask import Blueprint

main_bp = Blueprint("main_bp", __name__)

@main_bp.get('/')
def index():
    return {
        "msg": "Hello World"
    }

Actually, we can run this project using the following commands:

For Windows:

$ set FLASK_ENV=development
$ set FLASK_APP=Project
$ flask run

For Ubuntu:

$ export FLASK_ENV=development
$ export FLASK_APP=Project
$ flask run

However, in order to run this app easily, I wrote app.py which will import the create_app function from Project Package. Then we can use the app.run() method so that we can run the app just using the python app.py

app.py

from Project import create_app

app = create_app()

if __name__ == "__main__":
    app.run()

Dockerizing Flask Application

Now we have to create a requirements file using:

pip freeze > requirements.txt

My requirements.txt file looks like this:

click==8.0.1
colorama==0.4.4
Flask==2.0.1
gunicorn==20.1.0
itsdangerous==2.0.1
Jinja2==3.0.1
MarkupSafe==2.0.1
Werkzeug==2.0.1

Now we have to create a Dockerfile as I have given below:

FROM python:3.8-alpine
LABEL maintainer="Arun K Soman <arunksoman5678@gmail.com>"
WORKDIR /flask_docker_post1
RUN apk --update --no-cache add python3-dev libffi-dev gcc musl-dev make libevent-dev build-base
COPY requirements.txt requirements.txt
RUN python -m pip install --upgrade pip setuptools
RUN pip install -r requirements.txt
EXPOSE 8080
COPY . .
  • Build an image starting with the Python 3.8 alpine image. Alpine is a lightweight Linux distribution.
  • The LABEL is used to write the name of the maintainer and his email address. It is optional
  • Set the working directory to /flask_docker_post1.
  • Update alpine Linux, Install gcc and other dependencies
  • Copy requirements.txt.
  • Install the Python dependencies
  • Add metadata to the image to describe that the container is listening on port 8080
  • Copy the current directory. in the project to the workdir. in the image.

If you have previous experience with docker you might notice that I didn't write CMD in this Dockerfile. It is because, I have to extend this project in future to integrate more services like MySQL, Redis, Celery, NGINX etc. So I am creating docker-compose.yml which is given below:

version: '2.2'

services:
    web:
        container_name: flask_post1
        build:
            context: "."
        command: gunicorn --bind 0.0.0.0:8080 app:app
        ports:
            - "8080:8080"

This docker-compose.yml file is quite self-explanatory.

In order to check everything works perfectly run the following command:

docker-compose up --build

After building the container successfully I hope you can see the following lines on terminal:

Starting flask_post1 ... done
Attaching to flask_post1
flask_post1 | [2021-07-17 14:39:03 +0000] [1] [INFO] Starting gunicorn 20.1.0
flask_post1 | [2021-07-17 14:39:03 +0000] [1] [INFO] Listening at: http://0.0.0.0:8080 (1)
flask_post1 | [2021-07-17 14:39:03 +0000] [1] [INFO] Using worker: sync
flask_post1 | [2021-07-17 14:39:03 +0000] [9] [INFO] Booting worker with pid: 9

Now use postman or browser to hit http://localhost:8080 you can see ✌: post1.PNG

You can find out codes on GitHub