Flask, Docker, Pycharm - Setting up development environment

On the web are dozens of posts on how to dockerize the flask app that describes how to create a container with an embedded flask app. But it is not suitable for development purposes, as you need to change the sources and restart the flask on the fly.

Setting up the python dev environment in Flask is only appropriate when you are not going to play with your app venv (install new packages or upgrade packages or python version). In that case, when you need to build the environment for your app you should set up classic venv on your desktop. The dockerized developer environment is useful when you only want to develop your code having frozen 3rd part packages or in the case of a larger team, to have the quick setup and the same dev env.

Where to put docker files in project?

Do not put Docker files into subdirectories in your project. It should be at the root of your project otherwise you will run into problems with paths.

Project
    - docker_compose.yml
    - Dockerfile
    - Postgresql
        - Dockerfile
    - app (Flask App)
    - venv
    - requirements.txt
docker build -f ./Docker/web.Dockerfile .

According to Docker documentation:

The path must be inside the context of the build; you cannot COPY ../something /something, because the first step of a docker build is to send the context directory (and subdirectories) to the docker daemon.

Prepare your Dockerfiles

The development files are quite simple and notice, that we cannot copy the sources to the image. We will mount them as a volume so we can edit the sources in the running container. Dockerfile

FROM python:3.9-bullseye  

COPY requirements.txt requirements.txt  
RUN pip install -r requirements.txt

Test the build docker build .

Postgresql/Dockerfile

FROM postgres

Test the build docker build ./Postgresql

Note that for accessing the database from the flask container you should provide the address IP to the database container. Instead of localhost use host.docker.internal eg. SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://lhcom4:password@host.docker.internal/lhcom4'

Runtime configuration

Now we have got ready images to run containers. But we need to run containers with parameters (eg. database cluster, python sources etc.) We can prepare shell scripts to run our infrastructure but the better way is to create the docker-compose.yml file.

version: '2'  

services:  
  web4:  
    build: .  
    ports:  
      - "5000:5000"  
    depends_on:  
      - postgres  

  postgres:  
    restart: always  
    build: Postgresql  
    volumes:  
      - /Users/lukasz/docker_volumes/postgres:/var/lib/postgresql/data:z  
    environment:  
      - POSTGRES_PASSWORD=pass  
    expose:  
      - "5432"  
    ports:  
      - "5432:5432"

This file is also simplified for Pycharm. On MacOS remember to disable AirPlay to make 5000 port available: Resources.

Configuring PyCharm

Set the Project Interpreter to run the configuration form the compose file.

The /opt/project is the dir where PyCharm helper will mount automatically our project.

The next step is to create Run configuration for our flask project. The example microblog.py file is the file that creates our flask app.

You should see output similar to:

FLASK_APP = microblog.py
FLASK_ENV = development
FLASK_DEBUG = 0
In folder /Users/lukasz/PycharmProjects/lhcom4
/usr/local/bin/docker-compose -f /Users/lukasz/PycharmProjects/lhcom4/docker-compose.yml -f /Users/lukasz/Library/Caches/JetBrains/PyCharm2022.1/tmp/docker-compose.override.209.yml up --exit-code-from web4 --abort-on-container-exit web4
Recreating lhcom4_postgres_1 ... 
Recreating lhcom4_web4_1     ... 
Attaching to lhcom4_web4_1
web4_1      |  * Serving Flask app "microblog.py"
web4_1      |  * Environment: development
web4_1      |  * Debug mode: off
web4_1      |  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

...and you will not be able to open the http://127.0.0.0.:5000 :), as it is run inside the container and listens on the container's localhost. We want to access it from the outside (our workstation). So we need to change the Flask app to listen on all the interfaces (0.0.0.0) by passing the argument --host=0.0.0.0. Edit the autogenerated PyCharm runtime and change the: - Additional options - FLASK_DEBUG

Now in the output you will see changes:

FLASK_DEBUG = 1
web4_1      |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Resources

Unviersal Dockerfile

As I wrote before the docker-compose.yml for Pycharm could be simplified as we need to do the configuration in PyCharm itself an take advantage of its automatiztion. The docker-compose for running manually could look like:

version: '2'  

services:  
  web4:  
    build: .  
    ports:  
      - "5000:5000"  
    environment:  
      - FLASK_APP=/usr/src/microblog.py  
      - FLASK_DEBUG=1  
    volumes:  
      - .:/usr/src/  
    command: flask run --host=0.0.0.0 --port=5001  
    depends_on:  
      - postgres  

  postgres:  
    restart: always  
    build: Postgresql  
    volumes:  
      - /Users/lukasz/docker_volumes/postgres:/var/lib/postgresql/data:z  
    environment:  
      - POSTGRES_PASSWORD=pass  
    expose:  
      - "5432"  
    ports:  
      - "5432:5432"