Python3, Flask, and Docker in production the easy way

Terrillo Walls
Aug 28, 2018 · 4 min read

Flask’s built-in server is not great for production despite the fact that it’s lightweight and straightforward to use. By default the Flask integral server only serves one request at a time. In a development environment that’s fine. When running a web application you want to be able to handle multiple users and a ton of request. People will notice a long wait time for pages and static files to load.

If your looking to run Flask in production, be sure to use a production-ready web server like Nginx, and let your Python app be handled by a WSGI application server like Gunicorn.

Why can’t this be simpler? Start with understanding what to use instead. I’ve learned this the hard way using Flask for the first time in production when deploying a high traffic microservices. The server keep crashing because it failed to keep up with 1,000 request per minute. The server size didn’t matter. This wasn’t scalable. Did some research and discovered I needed an Application Server.

An Application Server takes the requests and converts the information into Python objects which are usable by frameworks. This tutorial is about using uWSGI. The Web Server Gateway Interface (WSGI) is a simple calling convention for web servers to forward requests to web applications or frameworks written in the Python programming language.

A Docker Production Stack

For all my web applications projects for work and fun I use Docker. I’ve done all the hard work of setting up Python3, Flask, and uWSGI on a Ubuntu VPS and it wasn’t fun. I love the model of do it once with Docker so you never have to do it again.

Docker operates on a container level, rather than the VM level. Eschewing all of the technicalities of this, it means that you can focus on writing your code and leave the configuration to someone else out there on the internet. My Docker image installs Nginx, uWSGI and Flask with all of the necessary configuration bundled in. Once you have written and packaged your code into this container, it can be deployed to Google Cloud, Azure, AWS or DigitalOcean with no need to concern yourself with config.

The file structure

- /app
  - main.py
  - requirements.txt
- docker-compose.yml
- Dockerfile
- nginx.sh

Step 1 → Basic Flask app

./app/main.py

from flask import Flask, Blueprint, request, Response

./app/requirements.txt

flask

Step 2 → The sweet Dockerfile

./Dockerfile

Note that the terrillo/python3flask image installs Python3.7 and a number of dependencies.

FROM terrillo/python3flask:latest
    
# STATIC paths for file.
# Don't use flask static. Nginx is your friend
ENV STATIC_URL /static
ENV STATIC_PATH /app/static
    
# Place your flask application on the server
COPY ./app /app
WORKDIR /app
    
# Install requirements.txt
RUN pip3 install --no-cache-dir -r requirements.txt
    
# NGINX setup
COPY ./nginx.sh /nginx.sh
RUN chmod +x /nginx.sh
    
ENV PYTHONPATH=/app
    
ENTRYPOINT ["/nginx.sh"]
CMD ["/start.sh"]
    
EXPOSE 80 443

Step 3 → nginx.sh

./nginx.sh

This step configures the Nginx server for static paths with the environment variables in the Dockerfile

#! /usr/bin/env bash

Step 4 (Optional) → docker-compose.yml

./docker-compose.yml

Using Docker Compose can give you the ability to edit your application while it’s running easily and have it auto reload when you make changes. The default port in this example is set to 3000. Docker Compose has other great features like running docker images together. Think about Flask + MySQL. Learn more here https://devcenter.heroku.com/articles/local-development-with-docker-compose

version: '2'
services:
  web:
    container_name: dockerflask
    restart: always
    build:
      context: .
      dockerfile: ./Dockerfile
    command: []
    ports:
      - "3000:80"
    volumes:
      - ./app:/app

Run with Docker Compose

docker-compose build
docker-compose up

or

Run without Docker Compose

docker build -t dockerflask .
docker run -p 3000:80 dockerflask

Docker will proceed to download all of the dependencies and set up the container.

Open http://localhost:3000/

When adding new pip packages be sure to update your requirements.txt. This changes also requires rebuilding of the Docker image.

Hopefully your usage of Flask in Production and for fun becomes a better experience. I’ve used this setup for various projects. Most popular example is https://ulna.co/. Docker Compose for my local then AWS Beanstalk for production.

GitHub Example: https://github.com/terrillo/py-docker

17

17 claps
Terrillo Walls

Written by

Software Engineer & amateur Data Scientist. Addicted to learning. https://terrillo.com