Python Poetry in Docker integrieren

74

Können Sie mir ein Beispiel geben, Dockerfilein dem ich alle benötigten Pakete von poetry.lockund pyproject.tomlin mein Image / Container von Docker installieren kann ?

Alex Bodea
quelle

Antworten:

133

Bei der Verwendung poetrymit sind verschiedene Dinge zu beachten docker.

Installation

Die offizielle Installation poetryerfolgt über:

curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python

Auf diese Weise können poetryund ihre Abhängigkeiten von Ihren Abhängigkeiten isoliert werden. Aber aus meiner Sicht ist es aus zwei Gründen keine sehr gute Sache:

  1. poetryDie Version wird möglicherweise aktualisiert und Ihr Build wird beschädigt. In diesem Fall können Sie eine POETRY_VERSIONUmgebungsvariable angeben . Der Installer wird es respektieren
  2. Ich mag die Idee nicht, Dinge aus dem Internet in meine Container zu leiten, ohne vor möglichen Dateimodifikationen geschützt zu sein

Also benutze ich pip install 'poetry==$POETRY_VERSION'. Wie Sie sehen können, empfehle ich weiterhin, Ihre Version zu pinnen.

Stecken Sie diese Version auch in Ihre pyproject.toml:

[build-system]
# Should be the same as `$POETRY_VERSION`:
requires = ["poetry>=1.0"]
build-backend = "poetry.masonry.api"

Es schützt Sie vor Versionsinkongruenzen zwischen Ihrem lokalen und dem dockerUmfeld.

Abhängigkeiten zwischenspeichern

Wir wollen , dass unsere Anforderungen zwischenzuspeichern und sie nur neu installieren , wenn pyproject.tomloder poetry.lockDateien ändern. Andernfalls werden die Builds langsam sein. Um eine funktionierende Cache-Schicht zu erreichen, sollten wir Folgendes setzen:

COPY poetry.lock pyproject.toml /code/

Nach der poetryInstallation, aber bevor andere Dateien hinzugefügt werden.

Virtualenv

Das nächste, was zu beachten ist, ist die virtualenvSchöpfung. Wir brauchen es nicht in docker. Es ist bereits isoliert. Also verwenden wir die poetry config virtualenvs.create falseEinstellung, um es auszuschalten.

Entwicklung gegen Produktion

Wenn Sie Dockerfilefür Entwicklung und Produktion dasselbe verwenden wie ich, müssen Sie verschiedene Abhängigkeiten basierend auf einer Umgebungsvariablen installieren:

poetry install $(test "$YOUR_ENV" == production && echo "--no-dev")

Auf diese Weise $YOUR_ENVwird gesteuert, welche Abhängigkeiten festgelegt werden: alle (Standard) oder Produktion nur mit --no-devFlag.

Möglicherweise möchten Sie auch weitere Optionen hinzufügen, um eine bessere Erfahrung zu erzielen:

  1. --no-interaction keine interaktiven Fragen zu stellen
  2. --no-ansi Flag, um Ihre Ausgabe protokollfreundlicher zu gestalten

Ergebnis

Sie werden am Ende etwas Ähnliches haben wie:

FROM python:3.6.6-alpine3.7

ARG YOUR_ENV

ENV YOUR_ENV=${YOUR_ENV} \
  PYTHONFAULTHANDLER=1 \
  PYTHONUNBUFFERED=1 \
  PYTHONHASHSEED=random \
  PIP_NO_CACHE_DIR=off \
  PIP_DISABLE_PIP_VERSION_CHECK=on \
  PIP_DEFAULT_TIMEOUT=100 \
  POETRY_VERSION=1.0.0

# System deps:
RUN pip install "poetry==$POETRY_VERSION"

# Copy only requirements to cache them in docker layer
WORKDIR /code
COPY poetry.lock pyproject.toml /code/

# Project initialization:
RUN poetry config virtualenvs.create false \
  && poetry install $(test "$YOUR_ENV" == production && echo "--no-dev") --no-interaction --no-ansi

# Creating folders, and files for a project:
COPY . /code

Ein voll funktionsfähiges Beispiel aus der Praxis finden Sie hier: wemake-django-template

Update am 17.12.2019

  • Update poetryauf 1.0
sobolevn
quelle
2
Leser dieser Antwort möchten möglicherweise mehr über mehrstufige Docker-Builds erfahren . Ich weiß, dass mehrstufige Builds in meinem Fall den Prozess von Basis- gegen Test- und App-Docker-Bildern erheblich vereinfacht haben. Siehe auch diesen Beitrag, der nicht poesiespezifisch ist, aber einen Grund zeigt, warum man in Betracht ziehen könnte , virtualenv in Docker weiterhin zu verwenden, wenn mehrstufige Builds ausgeführt werden. (Noch nicht selbst getestet, ich habe erst vor poetrykurzem adoptiert .)
Driftcatcher
2
@hangtwenty Sind Sie daran interessiert, mehrstufige Builds zur wemake-django-Vorlage beizutragen? Es wäre eine großartige Funktion, die die endgültige Bildgröße reduziert. Wenn ja, schreiben Sie mir bitte eine Zeile auf Github, indem Sie eine neue Ausgabe erstellen.
Sobolevn
4
@sobolevn Die einzige Sorge pip install poetryist, dass die Abhängigkeiten von Poetry mit App-Abhängigkeiten in Konflikt stehen könnten.
Rob Grant
3
poetry config virtualenvs.create falsefunktioniert nicht in 1.0.0. Verwenden Sie RUN POETRY_VIRTUALENVS_CREATE=false poetry installstattdessen.
JerryDDG
58

Mehrstufiger Docker Build mit Poetry und Venv

Deaktivieren Sie die Erstellung von virtualenv nicht. Virtualenvs erfüllen in Docker-Builds einen Zweck , da sie eine elegante Möglichkeit bieten, mehrstufige Builds zu nutzen. Kurz gesagt, Ihre Build-Phase installiert alles in die virtuelle Umgebung, und die letzte Phase kopiert die virtuelle Umgebung einfach in ein kleines Image.

Verwenden poetry exportund installieren Sie zuerst Ihre angehefteten Anforderungen, bevor Sie Ihren Code kopieren. Auf diese Weise können Sie den Docker-Build-Cache verwenden und Abhängigkeiten niemals neu installieren, nur weil Sie eine Zeile in Ihrem Code geändert haben.

Verwenden poetry installSie diesen Code nicht zur Installation, da er eine bearbeitbare Installation ausführt. Verwenden Sie stattdessen, poetry buildum ein Rad zu bauen, und installieren Sie es dann per Pip in Ihre virtuelle Umgebung. (Dank PEP 517 könnte dieser gesamte Prozess auch mit einem einfachen Vorgang ausgeführt werden. pip install .Aufgrund der Build-Isolation würden Sie jedoch eine weitere Kopie von Poetry installieren.)

Hier ist ein Beispiel für eine Docker-Datei, in der eine Flask-App in einem Alpine-Image installiert wird, abhängig von Postgres. In diesem Beispiel wird ein Einstiegspunktskript verwendet, um die virtuelle Umgebung zu aktivieren. Im Allgemeinen sollten Sie jedoch ohne ein Einstiegspunktskript in Ordnung sein, da Sie /venv/bin/pythonin Ihrer CMDAnweisung einfach auf die Python-Binärdatei verweisen können .

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()
Claudio
quelle
1
@stderr Bei einer bearbeitbaren Installation wird Ihr Paket nicht in der virtuellen Umgebung installiert. Es wird eine .egg-Link-Datei erstellt, die auf Ihren Quellcode verweist. Dieser Link ist nur für die Dauer der Erstellungsphase gültig.
Claudio
2
Update: Poetry 1.0.0 wurde veröffentlicht. Für den Export von Anforderungen ist keine Vorabversion mehr erforderlich.
Claudio
Brilliant wird definitiv meine Art sein, Docker-Bilder mit Poetry
gabuzo
Lesen Sie auch den hervorragenden Docker-Verpackungsleitfaden von Itamar Turner-Trauring für Python: pythonspeed.com/docker . Nach seinem Rat sollte diese Antwort wahrscheinlich aktualisiert werden, um ein schlankes Debian-Bild anstelle von Alpine zu verwenden.
Claudio
2
"Verwenden Sie keine Poetry-Installation, um Ihren Code zu installieren, da dadurch eine bearbeitbare Installation durchgeführt wird." Sie können dieses Verhalten mit --no-rootflag deaktivieren . Eine geschlossene Github-Ausgabe finden Sie hier .
Radzak
9

Das ist eine minimale Konfiguration, die für mich funktioniert:

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

Beachten Sie, dass es nicht so sicher ist wie die Konfiguration von @ sobolevn .

Als Trivia möchte ich hinzufügen, dass ein oder zwei Zeilen gelöscht werden könnten , wenn bearbeitbare Installationen für pyproject.tomlProjekte möglich sind :

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
Maciek
quelle
1
Wenn Ihr Projekt auch ein Python-Modul enthält mymodule, das Sie installieren möchten - wie es Poetry standardmäßig tut, wenn es eines findet - müssen Sie eine Dummy-Version wie diese erstellen, bevor Sie die Poetry-Installation ausführen : RUN mkdir /app/mymodule && touch /app/mymodule/__init__.py. Dies funktioniert, weil Poetry diese Art von Modulen mit pip -e installiert, wodurch nur eine symbolische Verknüpfung erstellt wird. Dies bedeutet, dass die Dinge wie erwartet funktionieren, wenn die realen Module im letzten Schritt darüber kopiert werden. (Laut Mods ist dies ein Kommentar und keine Bearbeitung - bitte versuchen Sie es in den Beitrag aufzunehmen, wenn Sie nicht einverstanden sind.)
Frankie Robertson
5

Hier ist ein Beispiel, bei dem zuerst eine Ebene mit den Abhängigkeiten (die nur erstellt wird, wenn sich diese ändern) und dann eine Ebene mit dem vollständigen Quellcode zu einem Bild hinzugefügt wird. Bei der Einstellung poetryfür die Installation in der globalen Datei site-packagesbleibt ein Konfigurationsartefakt erhalten, das ebenfalls entfernt werden kann.

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 . ./
Funky-Zukunft
quelle
3

TL; DR

Ich konnte mich mit poetryfür ein DjangoProjekt einrichten postgres. Nach einigen Recherchen kam ich zu folgendem Ergebnis 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"]

Dies ist der Inhalt von 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 "$@"

Ausführliche Erklärung

Einige Punkte zu beachten:

  • Ich habe mich entschieden, slimanstelle von alpineals Tag für das pythonBild zu verwenden, denn obwohl alpineBilder die Größe von Docker-Bildern reduzieren und den Build beschleunigen sollen, können Sie mit Python tatsächlich ein etwas größeres Bild erhalten, und das dauert eine Weile Build (lesen Sie diesen Artikel für weitere Informationen).

  • Mit dieser Konfiguration werden Container schneller erstellt als mit dem alpinen Image, da ich keine zusätzlichen Pakete hinzufügen muss, um Python-Pakete ordnungsgemäß zu installieren.

  • Ich installiere poetrydirekt von der in der Dokumentation angegebenen URL. Mir sind die Warnungen von bekannt sobolevn. Ich bin jedoch der Meinung, dass es auf lange Sicht besser ist, die spätere Version poetryvon standardmäßig zu verwenden, als sich auf eine Umgebungsvariable zu verlassen, die ich regelmäßig aktualisieren sollte.

  • Das Aktualisieren der Umgebungsvariablen PATHist von entscheidender Bedeutung. Andernfalls erhalten Sie eine Fehlermeldung, dass keine Poesie gefunden wurde .

  • Abhängigkeiten werden direkt im Python-Interpreter des Containers installiert. Es wird keine poetryvirtuelle Umgebung erstellt, bevor die Abhängigkeiten installiert werden.

Falls Sie die alpineVersion davon benötigen 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"]

Beachten Sie, dass die alpineVersion einige Abhängigkeiten benötigt postgresql-dev gcc python3-dev musl-dev openssl-dev libffi-dev, um ordnungsgemäß zu funktionieren.

lmiguelvargasf
quelle
1

Dies ist eine geringfügige Überarbeitung der Antwort von @Claudio , die die neue poetry install --no-rootFunktion verwendet, wie sie von @sobolevn in seiner Antwort beschrieben wurde .

Um Poesie zu zwingen , Abhängigkeiten in eine bestimmte virtuelle Umgebung zu installieren, muss sie zuerst aktiviert werden.

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

Fügen Sie diese daher zu @ Claudios Antwort hinzu, die wir haben

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

Wenn Sie dies für Entwicklungszwecke verwenden müssen, fügen Sie das hinzu oder entfernen Sie es, --no-devindem Sie diese Zeile ersetzen

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

zu so etwas wie in @ sobolevns Antwort gezeigt

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

nach dem Hinzufügen der entsprechenden Umgebungsvariablendeklaration.

In diesem Beispiel werden Debian-Slims als Basis verwendet. Die Anpassung an ein alpines Bild sollte jedoch eine triviale Aufgabe sein.

Jeffrey04
quelle