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"]