Dockerfile examples using Poetry, pip, Flask & FastAPI

Docker, like most devops or MLOps concepts and tools, can be daunting when you first approach them. The aim of this quick guide is to give you the down-and-dirty for a minimally viable understanding of Dockerfiles for Python applications and services.

Note: If you’re looking for an in-depth Dockerfile tutorial, you should checkout this video.

What is a Dockerfile?

A Dockerfile is a simple text document that you can use to automate the building of your application. Dockerfiles are like the instructions you get with IKEA furniture. The difference is that you don’t have images to explain the desired actions, so you’ll need to use more precise instructions.

Example Python Dockerfiles

Let’s take a look at some example Dockerfiles. All of the below examples are for python applications and cover FastAPI or Flask apps using pip or poetry for package management.

Plain boilerplate Dockerfile

Here is a basic boilerplate Docker running python and using a python requirements.txt file with pip to install dependencies.

# Basic Dockerfile
FROM {python version}
WORKDIR {working directory you want to run flask app from}

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

COPY {Either bash script to run server/nginx/etc OR the location of the primary api script relative to the working dir}

EXPOSE {port to expose}

CMD {either your bash script or command to run the flask app}

Dockerfile for Python 3.8 + pip + FastAPI

FROM python:3.8-slim
WORKDIR app_dir/

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

COPY ./src/app ./

EXPOSE 80

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

Dockerfile for Python + poetry + FastAPI

Setting up a docker image with poetry can be a pain. My colleague at Aptive Resources, Jason Adam, painstakingly built out our Dockerfiles for poetry + FastAPI services. Below is a version of what I regularly use:

###############################################
# Base Image
###############################################
FROM python:3.8-slim as python-base

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=off \
    PIP_DISABLE_PIP_VERSION_CHECK=on \
    PIP_DEFAULT_TIMEOUT=100 \
    POETRY_VERSION=1.0.5 \
    POETRY_HOME="/opt/poetry" \
    POETRY_VIRTUALENVS_IN_PROJECT=true \
    POETRY_NO_INTERACTION=1 \
    PYSETUP_PATH="/opt/pysetup" \
    VENV_PATH="/opt/pysetup/.venv"

# prepend poetry and venv to path
ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"

###############################################
# Builder Image
###############################################
FROM python-base as builder-base
RUN apt-get update \
    && apt-get install --no-install-recommends -y \
    curl \
    build-essential

# install poetry - respects $POETRY_VERSION & $POETRY_HOME
RUN curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python

# copy project requirement files here to ensure they will be cached.
WORKDIR $PYSETUP_PATH
COPY poetry.lock pyproject.toml ./

# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally
RUN poetry install --no-dev

###############################################
# Production Image
###############################################
FROM python-base as production
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
COPY ./src /src/
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "80"]

Dockerfile for Python 3.8 + pip + Flask

FROM python:3.8-slim
WORKDIR app_dir/

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

COPY ./src /src/

EXPOSE 8000

CMD ["gunicorn", "src.main:app", "--bind", "0.0.0.0:8000", "--workers", "4", "--timeout", "120"] 

The CMD instruction runs Gunicorn or “Green Unicorn” which is a server you can use for python applications.

Dockerfile for Python + poetry + Flask

###############################################
# Base Image
###############################################
FROM python:3.8-slim as python-base

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=off \
    PIP_DISABLE_PIP_VERSION_CHECK=on \
    PIP_DEFAULT_TIMEOUT=100 \
    POETRY_VERSION=1.0.5 \
    POETRY_HOME="/opt/poetry" \
    POETRY_VIRTUALENVS_IN_PROJECT=true \
    POETRY_NO_INTERACTION=1 \
    PYSETUP_PATH="/opt/pysetup" \
    VENV_PATH="/opt/pysetup/.venv"

# prepend poetry and venv to path
ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"

###############################################
# Builder Image
###############################################
FROM python-base as builder-base
RUN apt-get update \
    && apt-get install --no-install-recommends -y \
    curl \
    build-essential

# install poetry - respects $POETRY_VERSION & $POETRY_HOME
RUN curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python

# copy project requirement files here to ensure they will be cached.
WORKDIR $PYSETUP_PATH
COPY poetry.lock pyproject.toml ./

# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally
RUN poetry install --no-dev

###############################################
# Production Image
###############################################
FROM python-base as production
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
COPY ./src /src/
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "80"]

COPY src run_flask_app.sh ./
EXPOSE 8000
CMD ["./run_flask_app.sh"]

Wrap-up

As you can see, the poetry Dockerfiles are far more verbose than the pip versions. With that said, I urge you to take the time and checkout poetry as a package management tool for future projects. It provides a ton of value when building robust products and services. I’ll follow-up with a guide comparing pip and poetry in the next week.

In the meantime, I hope this helps you approach Docker and MLOps more gracefully

Leave a Reply

Your email address will not be published.