Можете ли вы привести пример Dockerfile
, в котором я могу установить все необходимые пакеты из poetry.lock
и pyproject.toml
в свой образ / контейнер из Docker?
Интеграция Python Poetry с Docker
Ответы (8)
При использовании poetry
вместе с docker
следует помнить о нескольких вещах.
Установка
Официальный способ установки poetry
:
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
Таким образом можно изолировать poetry
и его зависимости от ваших зависимостей. Но, на мой взгляд, это не очень хорошо по двум причинам:
- Версия
poetry
может получить обновление, что нарушит вашу сборку. В этом случае вы можете указатьPOETRY_VERSION
переменную среды. Установщик будет уважать это - Мне не нравится идея передавать вещи из Интернета в мои контейнеры без какой-либо защиты от возможных изменений файлов.
Итак, я использую pip install 'poetry==$POETRY_VERSION'
. Как видите, все же рекомендую закрепить вашу версию.
Также закрепите эту версию в своем pyproject.toml
:
[build-system]
# Should be the same as `$POETRY_VERSION`:
requires = ["poetry>=1.0"]
build-backend = "poetry.masonry.api"
Это защитит вас от несоответствия версий вашей локальной среды и docker
среды.
Кеширование зависимостей
Мы хотим кэшировать наши требования и переустанавливать их только при изменении pyproject.toml
или poetry.lock
файлов. В противном случае сборка будет медленной. Для достижения рабочего слоя кеша мы должны поставить:
COPY poetry.lock pyproject.toml /code/
После установки poetry
, но до добавления любых других файлов.
Virtualenv
Следующее, что нужно иметь в виду - это virtualenv
творчество. В docker
он нам не нужен. Он уже изолирован. Итак, мы используем настройку poetry config virtualenvs.create false
, чтобы выключить его.
Разработка vs Производство
Если вы используете тот же Dockerfile
как для разработки, так и для производства, что и я, вам нужно будет установить разные наборы зависимостей в зависимости от некоторой переменной среды:
poetry install $(test "$YOUR_ENV" == production && echo "--no-dev")
Таким образом, $YOUR_ENV
будет контролировать, какой набор зависимостей будет установлен: все (по умолчанию) или производственная версия только с флагом --no-dev
.
Вы также можете добавить еще несколько опций для лучшего опыта:
--no-interaction
не задавать интерактивных вопросов--no-ansi
, чтобы сделать вывод более удобным для журнала
Результат
У вас получится что-то похожее на:
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
Вы можете найти полностью рабочий пример из реальной жизни здесь: wemake-django-template
Обновление на 2019-12-17
- Обновите
poetry
до 1.0
poetry
.)
- person driftcatcher; 14.03.2019
pip install poetry
, это то, что зависимости Poetry могут конфликтовать с зависимостями приложений.
- person Rob Grant; 09.06.2019
poetry config virtualenvs.create false
не работает в 1.0.0. Вместо этого используйте RUN POETRY_VIRTUALENVS_CREATE=false poetry install
.
- person JerryDDG; 18.12.2019
--user
arg на pip
, так что я могу просто скопировать /root/.local
на мой последний этап. Как бы я поступил так с поэзией?
- person beeb; 04.06.2020
[build-system]
, не следует ли вам использовать poetry==1.0
вместо >=
?
- person ewen-lbh; 26.07.2020
pip install
действительно конфликтует с зависимостями приложения, поскольку у зависимостей поэзии также есть свои собственные зависимости. Это полностью под контролем разработчика. При использовании этого метода всегда рекомендуется использовать pip install --ignore-installed
. Я тоже не люблю передавать что-то из Интернета прямо в оболочку. Не говоря уже о том, что для этого требуются curl, wget или что-то еще. Но, если вы решили это сделать, есть --version
вариант get-poetry.py
скрипта.
- person tosh; 28.08.2020
pyproject.toml
у меня все было настроено нормально. Однако pip install poetry
(на Python 3.7) устанавливает appdirs
как зависимость от poetry
, как и предполагалось. Но при работе с config virtualenvs.create false
, poetry
запускается с нуля, а удаляет appdirs
снова (Removing appdirs (1.4.4)
, при установке обычных зависимостей проекта все в порядке). Это потому, что appdirs
не был указан в pyproject.toml
(а зачем?). Я снова вернулся к использованию виртуальных окружений, так что poetry
не удаляет appdirs
.
- person Alex Povel; 01.03.2021
dephell
или black
и у вас есть они в pyproject.toml
в разделе dev-dependencies
, материал взорвется, если вы установите с --no-dev
. Одно из (надеюсь, временное) решение - установить поэзию официальным способом.
- person Adrian Pop; 11.05.2021
pyproject.toml
вместе с poetry.lock
? pyproject.toml
также содержит, например, некоторую конфигурацию линтеров, и изменения в этом разделе вызывают ненужные промахи в кэше. Итак, можно ли КОПИРОВАТЬ только poetry.lock
?
- person dem1tris; 10.06.2021
Многоступенчатая сборка Docker с помощью Poetry и venv
Не отключайте создание virtualenv. Virtualenv служат определенной цели в сборках Docker, потому что они предоставляют элегантный способ использования многоэтапных сборок. Вкратце, на этапе сборки все устанавливается в virtualenv, а на последнем этапе просто копируется virtualenv в небольшой образ.
Используйте poetry export
и сначала установите закрепленные требования, прежде чем копировать код. Это позволит вам использовать кеш сборки Docker и никогда не переустанавливать зависимости только потому, что вы изменили строку в своем коде.
Не используйте poetry install
для установки вашего кода, потому что он выполнит установку, доступную для редактирования. Вместо этого используйте poetry build
для создания колеса, а затем установите его в свой virtualenv. (Благодаря PEP 517 весь этот процесс можно также выполнить с помощью простого pip install .
, но из-за изоляции сборки вам придется установить еще одну копию поэзии.)
Вот пример файла Dockerfile, устанавливающего приложение Flask в образ Alpine с зависимостью от Postgres. В этом примере используется сценарий точки входа для активации файла virtualenv. Но в целом у вас все будет хорошо без сценария точки входа, потому что вы можете просто сослаться на двоичный файл Python в /venv/bin/python
в своей CMD
инструкции.
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()
poetry install --no-root
вместо экспорта требований для pip install
. Но это не помогает с редактируемыми установками. Эта проблема все еще не решена.
- person Claudio; 05.05.2020
poetry export -f requirements.txt
генерирует недопустимые файлы требований: одни и те же записи дублируются. Похоже, это связано с попыткой поддержки разных версий Python.
- person Matthew Schinckel; 04.06.2020
--user
), а затем просто скопировать весь $HOME/.local/
в, e. грамм. /usr/bin/local/
. Также есть параметры --root
и --prefix
pip, но кажется, что --root
просто не работает (пункт 20.2), а --prefix
расплывчато, и непонятно, что он на самом деле делает. И, как я уже сказал, нет причин использовать их, поскольку --user
работает безупречно.
- person tosh; 28.08.2020
requirements.txt
с использованием локально установленной поэзии. В таком случае мы могли бы даже отказаться от установки поэзии из нашего Dockerfile, что неплохо. К сожалению, это невозможно сделать с помощью самого докера. Определенно, это можно автоматизировать с помощью, например, грамм. сценарий оболочки, Makefile или что-то подобное. Однако мне такой подход кажется очень несовместимым со всем процессом сборки.
- person tosh; 28.08.2020
requirements.txt
, и этот по-прежнему должен оставаться в нередактируемом состоянии
- person Jeffrey04; 30.10.2020
PIP_PREFIX
. На базовом этапе отключите virtualenvs, установите префикс в новый каталог (например, /install
, сначала mkdir!), И на последнем этапе скопируйте каталог префикса в префикс Python (может быть /usr
, может быть /usr/local
, может быть что-то еще). Предполагая, что официальные образы докеров Python и /install
в качестве префикса, вы должны использовать COPY --from=base /install /usr/local
.
- person Martijn Pieters; 28.04.2021
. /venv/bin/activate
, в Dockerfile достаточно использовать ENV PATH="/venv/bin:${PATH}"
и ENV VIRTUAL_ENV="/venv"
, что означает, что у вас может быть встроенная точка входа / cmd, и он по-прежнему будет использовать venv.
- person Duncan; 12.07.2021
Это минимальная конфигурация, которая мне подходит:
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
Обратите внимание, что это не так безопасно, как конфигурация @ sobolevn.
В качестве мелочи добавлю, что если редактируемые установки будут доступны для pyproject.toml
проектов, строка или две могут быть удалены:
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
mymodule
, который вы хотели бы установить - как это делает Poetry по умолчанию, если он его находит, - вам нужно создать фиктивную версию, подобную этой, перед запуском поэзии install: RUN mkdir /app/mymodule && touch /app/mymodule/__init__.py
. Это работает, потому что Poetry устанавливает эти типы модулей с помощью pip -e, который просто создает символическую ссылку. Это означает, что все работает должным образом, когда на последнем этапе поверх него копируются настоящие модули. (Согласно модам, это комментарий, а не редактирование - попробуйте включить его в сообщение, если вы не согласны.)
- person Frankie Robertson; 23.04.2019
TL;DR
Мне удалось настроить poetry
для Django
проекта, используя postgres
. Проведя небольшое исследование, я получил следующее 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"]
Это содержание 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 "$@"
Детальное объяснение
Некоторые моменты, на которые следует обратить внимание:
Я решил использовать
slim
вместоalpine
в качестве тега для образаpython
, потому что хотяalpine
изображения должны уменьшать размер образов Docker и ускорять сборку, с Python вы можете фактически получить изображение немного большего размера и это для сборки требуется время (дополнительную информацию см. в этой статье).При использовании этой конфигурации контейнеры создаются быстрее, чем при использовании образа alpine, поскольку мне не нужно добавлять дополнительные пакеты для правильной установки пакетов Python.
Я устанавливаю
poetry
прямо с URL-адреса, указанного в документации. Мне известно о предупреждениях, предоставленныхsobolevn
. Однако я считаю, что в долгосрочной перспективе лучше использовать последнюю версиюpoetry
по умолчанию, чем полагаться на переменную среды, которую я должен периодически обновлять.Обновление переменной среды
PATH
имеет решающее значение. В противном случае вы получите сообщение о том, что стихи не найдены.Зависимости устанавливаются непосредственно в интерпретаторе python контейнера. Он не создает
poetry
виртуальную среду перед установкой зависимостей.
Если вам нужна alpine
версия этого 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"]
Обратите внимание, что версии alpine
для правильной работы требуются некоторые зависимости postgresql-dev gcc python3-dev musl-dev openssl-dev libffi-dev
.
Это незначительное изменение ответа, предоставленного @Claudio, в котором используется новая функция poetry install --no-root
, описанная @sobolevn в своем ответе.
Чтобы заставить поэзию устанавливать зависимости в конкретный virtualenv, необходимо сначала включить его.
. /path/to/virtualenv/bin/activate && poetry install
Поэтому, добавляя их в ответ @Claudio, мы имеем
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"]
Если вам нужно использовать это для целей разработки, вы добавляете или удаляете --no-dev
, заменяя эту строку
RUN . /venv/bin/activate && poetry install --no-dev --no-root
к чему-то вроде этого, как показано в ответе @ sobolevn
RUN . /venv/bin/activate && poetry install --no-root $(test "$YOUR_ENV" == production && echo "--no-dev")
после добавления соответствующего объявления переменной среды.
В этом примере в качестве основы используется debian-slim, однако адаптация его к образу на базе alpine должна быть тривиальной задачей.
COPY
перед RUN poetry install
или RUN poetry build
, я полагаю? мой ответ (а также упомянутые) практически просто реплицирует настройку в контейнере, просто мы явно устанавливаем venv равным /venv/
, если настройка в контейнере идентична вашей рабочей настройке, все технически должно работать нормально, просто подумайте как бы вы реплицировали настройку в другом месте без докера и соответствующим образом скорректировали Dockerfile?
- person Jeffrey04; 05.02.2021
Вот пример, в котором сначала к изображению добавляется слой с зависимостями (который создается только при их изменении), а затем слой с полным исходным кодом. Установка poetry
на глобальную site-packages
установку оставляет артефакт конфигурации, который также можно удалить.
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 . ./
Я создал решение, используя пакет блокировки (пакет, который зависит от всех версий в файле блокировки). Это приводит к чистой установке только pip без файлов требований.
Шаги: соберите пакет, соберите пакет блокировки, скопируйте оба колеса в свой контейнер, установите оба колеса с помощью pip.
Установка: poetry add --dev poetry-lock-package
Шаги за пределами сборки докера:
poetry build
poetry run poetry-lock-package --build
Тогда ваш Dockerfile
должен содержать:
FROM python:3-slim
COPY dist/*.whl /
RUN pip install --no-cache-dir /*.whl \
&& rm -rf /*.whl
CMD ["python", "-m", "entry_module"]
Я вижу, что все ответы здесь используют метод pip для установки Poetry, чтобы избежать проблем с версией. Официальный способ установки поэзии - чтение переменной env POETRY_VERSION, если она определена для установки наиболее подходящей версии.
здесь есть проблема в github, и я думаю, что решение из этого билета довольно интересно:
# `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"]