Why I Use Multi Stage Builds for Python Containers

Although multi stage builds are most commonly used when building containers for compiled languages like go because you can throw away the build dependencies and end up with a single executable, I find they are also quite useful when working in python.

In particular, I like them because you can install different groups of dependencies in each stage.

For instance, you don’t want pytest, pudb, etc. in your final production container, but you need them for development or CI. So you install these dependencies in your dev container but leave them out of your prod container.

Here’s an example of what the Dockerfile might look like:

FROM python:3.8 as base

RUN pip install pipenv

ENV PROJECT_DIR /usr/local/src/webapp
ENV SRC_DIR ${PROJECT_DIR}/src

COPY Pipfile Pipfile.lock ${PROJECT_DIR}/

WORKDIR ${PROJECT_DIR}

ENV PYTHONUNBUFFERED=1

RUN pipenv install --system --deploy

FROM base as dev

# this is a dev image build, so install dev packages
RUN pipenv install --system --deploy --dev

COPY ./src ${SRC_DIR}/

WORKDIR ${SRC_DIR}

CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

FROM base as prod

COPY ./src ${SRC_DIR}/

WORKDIR ${SRC_DIR}

CMD ["/usr/local/bin/run-gunicorn"]

Leave a Reply

Your email address will not be published.