Integrating Python Poetry with Docker

后端 未结 7 429
轻奢々
轻奢々 2021-01-29 17:51

Can you give me an example of a Dockerfile in which I can install all the packages I need from poetry.lock and pyproject.toml into my imag

相关标签:
7条回答
  • 2021-01-29 18:10

    build image without python-poetry

    FROM abersh/no-pypoetry as requirements
    
    FROM python:3.7
    
    # ... yourself commands
    
    COPY --from=requirements /src/requirements.txt .
    
    RUN pip install -r requirements.txt
    
    # ... yourself commands
    
    0 讨论(0)
  • 2021-01-29 18:14

    That's minimal configuration that works for me:

    FROM python:3.7
    
    ENV PIP_DISABLE_PIP_VERSION_CHECK=on
    
    RUN pip install poetry
    
    WORKDIR /app
    COPY poetry.lock pyproject.toml /app/
    
    RUN poetry config virtualenvs.create false
    RUN poetry install --no-interaction
    
    COPY . /app
    

    Note that it is not as safe as @sobolevn's configuration.

    As a trivia I'll add that if editable installs will be possible for pyproject.toml projects, a line or two could be deleted:

    FROM python:3.7
    
    ENV PIP_DISABLE_PIP_VERSION_CHECK=on
    
    WORKDIR /app
    COPY poetry.lock pyproject.toml /app/
    
    RUN pip install -e .
    
    COPY . /app
    
    0 讨论(0)
  • 2021-01-29 18:21

    Multi-stage Docker build with Poetry and venv

    Do not disable virtualenv creation. Virtualenvs serve a purpose in Docker builds, because they provide an elegant way to leverage multi-stage builds. In a nutshell, your build stage installs everything into the virtualenv, and the final stage just copies the virtualenv over into a small image.

    Use poetry export and install your pinned requirements first, before copying your code. This will allow you to use the Docker build cache, and never reinstall dependencies just because you changed a line in your code.

    Do not use poetry install to install your code, because it will perform an editable install. Instead, use poetry build to build a wheel, and then pip-install that into your virtualenv. (Thanks to PEP 517, this whole process could also be performed with a simple pip install ., but due to build isolation you would end up installing another copy of Poetry.)

    Here's an example Dockerfile installing a Flask app into an Alpine image, with a dependency on Postgres. This example uses an entrypoint script to activate the virtualenv. But generally, you should be fine without an entrypoint script because you can simply reference the Python binary at /venv/bin/python in your CMD instruction.

    Dockerfile

    FROM python:3.7.6-alpine3.11 as base
    
    ENV PYTHONFAULTHANDLER=1 \
        PYTHONHASHSEED=random \
        PYTHONUNBUFFERED=1
    
    WORKDIR /app
    
    FROM base as builder
    
    ENV PIP_DEFAULT_TIMEOUT=100 \
        PIP_DISABLE_PIP_VERSION_CHECK=1 \
        PIP_NO_CACHE_DIR=1 \
        POETRY_VERSION=1.0.5
    
    RUN apk add --no-cache gcc libffi-dev musl-dev postgresql-dev
    RUN pip install "poetry==$POETRY_VERSION"
    RUN python -m venv /venv
    
    COPY pyproject.toml poetry.lock ./
    RUN poetry export -f requirements.txt | /venv/bin/pip install -r /dev/stdin
    
    COPY . .
    RUN poetry build && /venv/bin/pip install dist/*.whl
    
    FROM base as final
    
    RUN apk add --no-cache libffi libpq
    COPY --from=builder /venv /venv
    COPY docker-entrypoint.sh wsgi.py ./
    CMD ["./docker-entrypoint.sh"]
    

    docker-entrypoint.sh

    #!/bin/sh
    
    set -e
    
    . /venv/bin/activate
    
    while ! flask db upgrade
    do
         echo "Retry..."
         sleep 1
    done
    
    exec gunicorn --bind 0.0.0.0:5000 --forwarded-allow-ips='*' wsgi:app
    

    wsgi.py

    import your_app
    
    app = your_app.create_app()
    
    0 讨论(0)
  • 2021-01-29 18:22

    Here's a stripped example where first a layer with the dependencies (that is only build when these changed) and then one with the full source code is added to an image. Setting poetry to install into the global site-packages leaves a configuration artifact that could also be removed.

    FROM python:alpine
    
    WORKDIR /app
    
    COPY poetry.lock pyproject.toml ./
    RUN pip install --no-cache-dir --upgrade pip \
     && pip install --no-cache-dir poetry \
     \
     && poetry config settings.virtualenvs.create false \
     && poetry install --no-dev \
     \
     && pip uninstall --yes poetry \
    
    COPY . ./
    
    0 讨论(0)
  • 2021-01-29 18:26

    TL;DR

    I have been able to set up poetry for a Django project using postgres. After doing some research, I ended up with the following Dockerfile:

    FROM python:slim
    
    # Keeps Python from generating .pyc files in the container
    ENV PYTHONDONTWRITEBYTECODE 1
    # Turns off buffering for easier container logging
    ENV PYTHONUNBUFFERED 1
    
    # Install and setup poetry
    RUN pip install -U pip \
        && apt-get update \
        && apt install -y curl netcat \
        && curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
    ENV PATH="${PATH}:/root/.poetry/bin"
    
    WORKDIR /usr/src/app
    COPY . .
    RUN poetry config virtualenvs.create false \
      && poetry install --no-interaction --no-ansi
    
    # run entrypoint.sh
    ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
    

    This is the content of entrypoint.sh:

    #!/bin/sh
    
    if [ "$DATABASE" = "postgres" ]
    then
        echo "Waiting for postgres..."
    
        while ! nc -z $SQL_HOST $SQL_PORT; do
          sleep 0.1
        done
    
        echo "PostgreSQL started"
    fi
    
    python manage.py migrate
    
    exec "$@"
    
    

    Detailed Explanation

    Some points to notice:

    • I have decide to use slim instead of alpine as tag for the python image because even though alpine images are supposed to reduce the size of Docker images and speed up the build, with Python, you can actually end up with a bit larger image and that takes a while to build (read this article for more info).

    • Using this configuration builds containers faster than using the alpine image because I do not need to add some extra packages to install Python packages properly.

    • I am installing poetry directly from the URL provided in the documentation. I am aware of the warnings provided by sobolevn. However, I consider that it is better in the long term to use the lates version of poetry by default than relying on an environment variable that I should update periodically.

    • Updating the environment variable PATH is crucial. Otherwise, you will get an error saying that poetry was not found.

    • Dependencies are installed directly in the python interpreter of the container. It does not create poetry to create a virtual environment before installing the dependencies.

    In case you need the alpine version of this Dockerfile:

    FROM python:alpine
    
    # Keeps Python from generating .pyc files in the container
    ENV PYTHONDONTWRITEBYTECODE 1
    # Turns off buffering for easier container logging
    ENV PYTHONUNBUFFERED 1
    
    # Install dev dependencies
    RUN apk update \
        && apk add curl postgresql-dev gcc python3-dev musl-dev openssl-dev libffi-dev
    
    # Install poetry
    RUN pip install -U pip \
        && curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
    ENV PATH="${PATH}:/root/.poetry/bin"
    
    WORKDIR /usr/src/app
    COPY . .
    RUN poetry config virtualenvs.create false \
      && poetry install --no-interaction --no-ansi
    
    # run entrypoint.sh
    ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
    

    Notice that the alpine version needs some dependencies postgresql-dev gcc python3-dev musl-dev openssl-dev libffi-dev to work properly.

    0 讨论(0)
  • 2021-01-29 18:31

    This is a minor revision to the answer provided by @Claudio, which uses the new poetry install --no-root feature as described by @sobolevn in his answer.

    In order to force poetry to install dependencies into a specific virtualenv, one needs to first enable it.

    . /path/to/virtualenv/bin/activate && poetry install
    

    Therefore adding these into @Claudio's answer we have

    FROM python:3.9-slim as base
    
    ENV PYTHONFAULTHANDLER=1 \
        PYTHONHASHSEED=random \
        PYTHONUNBUFFERED=1
    
    RUN apt-get update && apt-get install -y gcc libffi-dev g++
    WORKDIR /app
    
    FROM base as builder
    
    ENV PIP_DEFAULT_TIMEOUT=100 \
        PIP_DISABLE_PIP_VERSION_CHECK=1 \
        PIP_NO_CACHE_DIR=1 \
        POETRY_VERSION=1.1.3
    
    RUN pip install "poetry==$POETRY_VERSION"
    RUN python -m venv /venv
    
    COPY pyproject.toml poetry.lock ./
    RUN . /venv/bin/activate && poetry install --no-dev --no-root
    
    COPY . .
    RUN . /venv/bin/activate && poetry build
    
    FROM base as final
    
    COPY --from=builder /venv /venv
    COPY --from=builder /app/dist .
    COPY docker-entrypoint.sh ./
    
    RUN . /venv/bin/activate && pip install *.whl
    CMD ["./docker-entrypoint.sh"]
    

    If you need to use this for development purpose, you add or remove the --no-dev by replacing this line

    RUN . /venv/bin/activate && poetry install --no-dev --no-root
    

    to something like this as shown in @sobolevn's answer

    RUN . /venv/bin/activate && poetry install --no-root $(test "$YOUR_ENV" == production && echo "--no-dev")
    

    after adding the appropriate environment variable declaration.

    The example uses debian-slim's as base, however, adapting this to alpine-based image should be a trivial task.

    0 讨论(0)
提交回复
热议问题