Document docker poetry best practices #1879
Replies: 55 comments 102 replies
-
I would add #1227 to this as well. |
Beta Was this translation helpful? Give feedback.
-
I recently described usage of Poetry with private repos. |
Beta Was this translation helpful? Give feedback.
-
I have been playing around with multi-stage builds and came up with this example: # `python-base` sets up all our shared environment variables
FROM python:3.8.1-slim as python-base
# python
ENV PYTHONUNBUFFERED=1 \
# prevents python creating .pyc files
PYTHONDONTWRITEBYTECODE=1 \
\
# pip
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
\
# poetry
# https://python-poetry.org/docs/configuration/#using-environment-variables
POETRY_VERSION=1.0.3 \
# make poetry install to this location
POETRY_HOME="/opt/poetry" \
# make poetry create the virtual environment in the project's root
# it gets named `.venv`
POETRY_VIRTUALENVS_IN_PROJECT=true \
# do not ask any interactive question
POETRY_NO_INTERACTION=1 \
\
# paths
# this is where our requirements + virtual environment will live
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-base` stage is used to build deps + create our virtual environment
FROM python-base as builder-base
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
# deps for installing poetry
curl \
# deps for building python deps
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
# `development` image is used during development / testing
FROM python-base as development
ENV FASTAPI_ENV=development
WORKDIR $PYSETUP_PATH
# copy in our built poetry + venv
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
# quicker install as runtime deps are already installed
RUN poetry install
# will become mountpoint of our code
WORKDIR /app
EXPOSE 8000
CMD ["uvicorn", "--reload", "main:app"]
# `production` image used for runtime
FROM python-base as production
ENV FASTAPI_ENV=production
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
COPY ./app /app/
WORKDIR /app
CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "main:app"] |
Beta Was this translation helpful? Give feedback.
-
@michael0liver do you know if there is the requirement for the python:3.8.1-slim in the production step? Since you copy the .venv isn't do we have two python runtimes installed? I know in the Dockerfile of python:3.8.1-slim happens a bit more then installing python but still isn't there some redundancy when using the venv? (I don't know that much about venvs) |
Beta Was this translation helpful? Give feedback.
-
@mxab Yes you do. Virtual environments are really just an isolated directory where you can install third party packages without affecting the global site-packages. They contain symlinks to the Python executable's installed on your system. There are the I created a python-poetry-docker-example repo with a more concrete example than above. It can still be improved but maybe it could serve as a reference. |
Beta Was this translation helpful? Give feedback.
-
The pattern I've used when building Docker images with Poetry is to use it's |
Beta Was this translation helpful? Give feedback.
-
@n8sty I read about this way also in the stackoverflow thread. Would be cool to see if we had use cases demonstrate how to handle this also (also git and private repos) |
Beta Was this translation helpful? Give feedback.
-
it should be noted that this doesn't work with dependencies on non-hashable projects (ie. github repos) which is a pain if you, for example, are waiting for patches to land upstream. will update with any workarounds I find edit: seems to be enough to export
|
Beta Was this translation helpful? Give feedback.
-
What a fantastic Dockerfile. Thanks @michael0liver 🍾 |
Beta Was this translation helpful? Give feedback.
-
@michael0liver What a great build. It really helped me with my URL shortener project https://github.com/adamwojt/ur_l |
Beta Was this translation helpful? Give feedback.
-
@michael0liver Thx for your great example. I use it for my VS code dev container. |
Beta Was this translation helpful? Give feedback.
-
You should use docker --build-arg. These variables only exist within the build stage and will not persist in the built image. Expose these variables inside your CI pipeline (for example as secrets inside a GitHub actions workflow). |
Beta Was this translation helpful? Give feedback.
-
In case someone uses the buildkit feature of docker you can also use the secret mount You also need to put the # syntax=docker/dockerfile:experimental
FROM python:3.6.9-slim-buster as build
....
# then specify a secret id during the install run
RUN --mount=type=secret,id=poetry_auth,dst=/root/.config/pypoetry/auth.toml poetry install -n --no-dev # mount in the secret file
docker build --secret id=poetry_auth,src=$HOME/.config/pypoetry/auth.toml |
Beta Was this translation helpful? Give feedback.
-
On other issue we are facing in our case we use sqlalchemy and alembic. Does anyone else do it like this? |
Beta Was this translation helpful? Give feedback.
-
I think that's a bit of a weird setup. Seems like it'd be conceptually simpler if you had two separate containers, one for running migrations and one for running the application itself. I don't there's anything necessarily wrong with this, but unless there's a really important reason (maybe limiting the ability to use additional functional during migrations?) to have multiple virtual environments in a container, I'd think it best to separate them into dedicated containers or combine them into a single environment. Since this discussion is within the Poetry project, I don't think it supports this particular workflow particularly well either. I can imagine it working with a bunch of different optional dependency groups, and I've seen some issues relating to adding support for more detailed grouping of dependencies, but as of right now, I don't think the current feature set supports this in a particular seamless way. |
Beta Was this translation helpful? Give feedback.
-
Hi, what's the best practice for installing a project with path dependencies (e.g ../packageB/)? The dependency has it's own dependencies. I'm trying to achieve a cached docker layer that installs all remote dependencies - that way only when pyproject.toml or poetry.lock is changed - it would rebuild that stage. |
Beta Was this translation helpful? Give feedback.
-
I did try to summarize my own findings on Docker and Poetry on this blog post. Feedback and new suggestions are appreciated! |
Beta Was this translation helpful? Give feedback.
-
HI guys, can we please add docs to the repo and not medium? |
Beta Was this translation helpful? Give feedback.
-
After trial and error, I made some changes to michaeloliverx's dockerfile so that:
# syntax=docker/dockerfile:1
# Keep this syntax directive! It's used to enable Docker BuildKit
################################
# PYTHON-BASE
# Sets up all our shared environment variables
################################
FROM python:3.9.17-slim as python-base
# Python
ENV PYTHONUNBUFFERED=1 \
# pip
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
\
# Poetry
# https://python-poetry.org/docs/configuration/#using-environment-variables
POETRY_VERSION=1.6.1 \
# make poetry install to this location
POETRY_HOME="/opt/poetry" \
# do not ask any interactive question
POETRY_NO_INTERACTION=1 \
# never create virtual environment automaticly, only use env prepared by us
POETRY_VIRTUALENVS_CREATE=false \
\
# this is where our requirements + virtual environment will live
VIRTUAL_ENV="/venv" \
\
# Node.js major version. Remove if you don't need.
NODE_MAJOR=18
# prepend poetry and venv to path
ENV PATH="$POETRY_HOME/bin:$VIRTUAL_ENV/bin:$PATH"
# prepare virtual env
RUN python -m venv $VIRTUAL_ENV
# working directory and Python path
WORKDIR /app
ENV PYTHONPATH="/app:$PYTHONPATH"
# pretrained models cache path. Remove if you don't need.
# ref: https://huggingface.co/docs/transformers/installation?highlight=transformers_cache#caching-models
ENV TRANSFORMERS_CACHE="/opt/transformers_cache/"
################################
# BUILDER-BASE
# Used to build deps + create our virtual environment
################################
FROM python-base as builder-base
RUN apt-get update && \
apt-get install -y \
apt-transport-https \
gnupg \
ca-certificates \
build-essential \
git \
nano \
curl
# install Node.js. Remove if you don't need.
RUN mkdir -p /etc/apt/keyrings && \
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list && \
apt-get update && apt-get install -y nodejs
# install poetry - respects $POETRY_VERSION & $POETRY_HOME
# The --mount will mount the buildx cache directory to where
# Poetry and Pip store their cache so that they can re-use it
RUN --mount=type=cache,target=/root/.cache \
curl -sSL https://install.python-poetry.org | python -
# used to init dependencies
WORKDIR /app
COPY poetry.lock pyproject.toml ./
# install runtime deps to $VIRTUAL_ENV
RUN --mount=type=cache,target=/root/.cache \
poetry install --no-root --only main
COPY scripts scripts/
COPY my_awesome_ai_project/ my_awesome_ai_project/
# populate Huggingface model cache. Remove if you don't need.
RUN poetry run python scripts/bootstrap.py
# build C dependencies. Remove if you don't need.
RUN --mount=type=cache,target=/app/scripts/vendor \
poetry run python scripts/build-c-denpendencies.py && \
cp scripts/lib/*.so /usr/lib
################################
# DEVELOPMENT
# Image used during development / testing
################################
FROM builder-base as development
WORKDIR /app
# quicker install as runtime deps are already installed
RUN --mount=type=cache,target=/root/.cache \
poetry install --no-root --with test,lint
EXPOSE 8080
CMD ["bash"]
################################
# PRODUCTION
# Final image used for runtime
################################
FROM python-base as production
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates && \
apt-get clean
# copy in our built poetry + venv
COPY --from=builder-base $POETRY_HOME $POETRY_HOME
COPY --from=builder-base $VIRTUAL_ENV $VIRTUAL_ENV
# copy in our C dependencies. Remove if you don't need.
COPY --from=builder-base /app/scripts/lib/*.so /usr/lib
# copy in pre-populated transformer cache. Remove if you don't need.
COPY --from=builder-base $TRANSFORMERS_CACHE $TRANSFORMERS_CACHE
WORKDIR /app
COPY poetry.lock pyproject.toml ./
COPY my_awesome_ai_project/ my_awesome_ai_project/
EXPOSE 8080
CMD ["python", "my_awesome_ai_project/app.py"]
I wrote a blog post Quick Dockerfile for Python Poetry Projects if you want more details. Feedbacks would be much appreciated! |
Beta Was this translation helpful? Give feedback.
-
I cant seem to get package caching to work. Are multi stage builds needed for this to work? I'm using github actions with docker |
Beta Was this translation helpful? Give feedback.
-
Hello everyone. Can anyone help me with my Dockerfile for project with poetry src layout. I tried different approaches, but looks like a stacked with this error "ModuleNotFoundError: No module named 'simple_queueing_system'". I would really appreciate your help. Thanks! Dockerfile: |
Beta Was this translation helpful? Give feedback.
-
With Is there a potential issue with |
Beta Was this translation helpful? Give feedback.
-
Is this the only official documentation on installing and setting up poetry via a Dockerfile? It would be great to have >=1 example Dockerfile on best practices for using poetry with a Dockerfile (e.g., a Dockerfile for a VS Code devcontainer). I would like to switch to poetry, but I'm not sure how to best modify my current Dockerfile setup that I use for my devcontainer environments: # Use an official Python runtime as a base image
FROM python:3.11-slim-buster
# Set the working directory in the container
WORKDIR /my_app
# Install system dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc curl gnupg2 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Copy the requirements.txt file into the container
COPY requirements.txt ./
# Copy the package files into the container
COPY ./my_app ./
# Install any needed Python packages specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt Maybe something like the following? # Use an official Python runtime as a base image
FROM python:3.11-slim-buster
# Set the working directory in the container
WORKDIR /my_app
# Install system dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc curl gnupg2 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Install Poetry
RUN curl -sSL https://install.python-poetry.org | python3 -
# Add Poetry to PATH
ENV PATH="${PATH}:/root/.local/bin"
# Copy the pyproject.toml (and optionally poetry.lock) file into the container
COPY pyproject.toml poetry.lock* ./
# Configure Poetry: Do not create a virtual environment as the container itself provides isolation
RUN poetry config virtualenvs.create false
# Install dependencies using Poetry
RUN poetry install --no-dev
# Copy the package files into the container
COPY ./my_app ./ |
Beta Was this translation helpful? Give feedback.
-
Pasting this here because I couldn't find a small image example: https://gist.github.com/ryukyi/5836f584039b1ad4fb12fec089de577e # Example flask app folder structure
#
# ├── app
# │ ├── app.py
# │ └── __init__.py
# ├── Dockerfile
# ├── .dockerignore
# ├── poetry.lock
# └── pyproject.toml
# alpine is a small image
FROM python:3.10-alpine
# Don't run as root
RUN adduser -D appuser && \
# Set the working directory in the container
mkdir -p /home/appuser/app && \
chown appuser:appuser /home/appuser/app && \
# Use wget instead of curl since curl is external package in alpine
# https://python-poetry.org/docs/#installation
wget -O get-poetry.py https://install.python-poetry.org && \
POETRY_HOME=/home/appuser/.poetry python3 get-poetry.py && \
rm get-poetry.py
USER appuser
# Add Poetry to PATH
ENV PATH="/home/appuser/.poetry/bin:${PATH}"
# Install dependencies
COPY poetry.lock pyproject.toml ./
RUN poetry install
# Copy the rest of your application into the Docker image
COPY ./app /home/appuser/app
# Run your Flask application using Gunicorn server for production
CMD ["poetry", "run", "gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"] |
Beta Was this translation helpful? Give feedback.
-
It is almost impossible to give a good recipe that's good for everyone, as how poetry is installed is up to the user, how the virtual environment is set up is up to the user, what sort of project they're using, is, again, up to the user. With poetry showing up in upstream repos more and more, installing it specially seems less and less the way to go to me. |
Beta Was this translation helpful? Give feedback.
-
I would argue that is the case for most software configurations, but it would be helpful to have a guide that works for many users. For instance, tutorials do not show the exact workflow that most users need, but they provide a guide and starting point.
So how should I install poetry in a Dockerfile that will be used as a developer environment (e.g., a VS Code devcontainer)? You mean to use |
Beta Was this translation helpful? Give feedback.
-
I have read this discussion, which I find very useful, if not necessary. I understand that the difficulty now is to converge on an agreement on best practices to write. The structureThe following structure could be considered for a dedicated section.
In general, I would act according to:
ExampleHere is an example based on the above ideas ## Introduction
....blabla
The following best practices should be kept in mind
- [optional] set the latest python version, in order to get the latest patch
- [highly suggested] use pip to install poetry
- [critical] never hardcode credentials to private sources
- ...
## Use cases
The following are general use cases that you can use a starting point for your specific case
### UC1: Dev environment
Here is an example of how to create a dev container aimed to host a basic development env. Once the image is built nobody can make OS changes, except the admin. An example of usage is a container used by a team.
#### Specifics
- Unprivileged User.
- multistage, in order to make the image lighter
- ...
#### Dockerfile
FROM python .......
....
|
Beta Was this translation helpful? Give feedback.
-
I decided to move forward with a proposal. Please let me know if this doesn't sound like a good way to proceed |
Beta Was this translation helpful? Give feedback.
-
Can anyone help me fix an issue with not doing absolute imports from project dir when installing dependencies using poetry in docker? |
Beta Was this translation helpful? Give feedback.
-
I enabled the docs preview in #9542. Since our docker expert is quite busy at the moment, I invite anyone who knows a bit about docker to review it. |
Beta Was this translation helpful? Give feedback.
-
Issue
There are several discussions on how to build your final application for a docker image
Would be great if the poetry team could provide a how to poetry with docker documentation
Beta Was this translation helpful? Give feedback.
All reactions