Интеграция Python Poetry с Docker
Можете ли вы дать мне пример Dockerfile, в котором я могу установить все необходимые мне пакеты от Стихи и pyproject.toml в мой образ / контейнер из Docker?
15 ответов
Есть несколько вещей, которые следует иметь в виду при использовании poetry
вместе с docker
,
Монтаж
Официальный способ установки poetry
через:
curl -sSL https://raw.githubusercontent.com/sdispater/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==0.12.11"]
build-backend = "poetry.masonry.api"
Это защитит вас от несоответствия версий между вашим локальным и docker
сред.
Кэширование зависимостей
Мы хотим кэшировать наши требования и переустанавливать их только тогда, когда pyproject.toml
или же poetry.lock
файлы меняются. В противном случае сборка будет медленной. Для достижения рабочего уровня кэша мы должны поставить:
COPY poetry.lock pyproject.toml /code/
После poetry
установлен, но перед добавлением любых других файлов.
Virtualenv
Следующее, что нужно иметь в виду, это virtualenv
создание. Нам это не нужно в docker
, Он уже изолирован. Итак, мы используем poetry config settings.virtualenvs.create false
установка, чтобы выключить его.
Развитие против производства
Если вы используете тот же 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=0.12.11
# 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 settings.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
Многоступенчатая сборка Docker с помощью Poetry и venv
Не отключайте создание virtualenv. Virtualenvs служат определенной цели в сборках Docker, потому что они предоставляют элегантный способ использования многоэтапных сборок. Короче говоря, на этапе сборки все устанавливается в virtualenv, а на последнем этапе просто копируется virtualenv в небольшой образ.
Использовать poetry export
и сначала установите закрепленные требования, прежде чем копировать код. Это позволит вам использовать кеш сборки Docker и никогда не переустанавливать зависимости только потому, что вы изменили строку в своем коде.
Не использовать poetry install
для установки вашего кода, потому что он выполнит редактируемую установку. Вместо этого используйтеpoetry build
, чтобы построить колесо, а затем установить его в свой virtualenv. (Благодаря PEP 517 весь этот процесс также можно было выполнить с помощью простогоpip install .
, но из-за изоляции сборки вам придется установить еще одну копию Poetry.)
Вот пример файла 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()
Это небольшая редакция ответа @Claudio, в котором используется новый
poetry install --no-root
функция, описанная @sobolevn в его ответе.
Чтобы заставить поэзию устанавливать зависимости в конкретную виртуальную среду, нужно сначала включить ее.
. /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 должна быть тривиальной задачей.
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
работать правильно.
Это минимальная конфигурация, которая работает для меня:
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 settings.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
Используйте многоэтапную сборку докера и тонкий образ Python, экспортируйте поэтическую блокировку в файл requirements.txt, а затем установите через pip внутри virtualenv.
Он имеет наименьший размер , не требует поэзии во время выполнения изображения, закрепляет версии всего.
FROM python:3.9.7 as base
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /app
FROM base as poetry
RUN pip install poetry==1.1.12
COPY poetry.lock pyproject.toml /app/
RUN poetry export -o requirements.txt
FROM base as build
COPY --from=poetry /app/requirements.txt /tmp/requirements.txt
RUN python -m venv .venv && \
.venv/bin/pip install 'wheel==0.36.2' && \
.venv/bin/pip install -r /tmp/requirements.txt
FROM python:3.9.7-slim as runtime
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /app
ENV PATH=/app/.venv/bin:$PATH
COPY --from=build /app/.venv /app/.venv
COPY . /app
Вот раздетый пример, где сначала к изображению добавляется слой с зависимостями (который создается только при их изменении), а затем слой с полным исходным кодом. настройка 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 . ./
Мой Dockerfile основан на ответе @lmiguelvargasf . Обратитесь к его сообщению для более подробного объяснения. Единственные существенные изменения, которые у меня есть, следующие:
Сейчас я использую последний официальный установщик
install-poetry.py
вместо устаревшегоget-poetry.py
как рекомендовано в их официальной документации . Я также устанавливаю определенную версию, используя--version
флаг, но вы также можете использовать переменную средыPOETRY_VERSION
. Больше информации в их официальных документах!В
PATH
я использую это/root/.local/bin:$PATH
вместо${PATH}:/root/.poetry/bin
из Dockerfile OP
FROM python:3.10.4-slim-buster
ENV PYTHONDONTWRITEBYTECODE 1 \
PYTHONUNBUFFERED 1
RUN apt-get update \
&& apt-get install curl -y \
&& curl -sSL https://install.python-poetry.org | python - --version 1.1.13
ENV PATH="/root/.local/bin:$PATH"
WORKDIR /usr/app
COPY pyproject.toml poetry.lock ./
RUN poetry config virtualenvs.create false \
&& poetry install --no-dev --no-interaction --no-ansi
COPY ./src ./
EXPOSE 5000
CMD [ "poetry", "run", "gunicorn", "-b", "0.0.0.0:5000", "test_poetry.app:create_app()" ]
Я создал решение, используя пакет блокировки (пакет, который зависит от всех версий в файле блокировки). Это приводит к чистой установке только 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"]
Есть два проекта, в которых вы можете увидеть, как это сделать правильно, или вы можете использовать их для создания собственных изображений, поскольку они являются просто базовыми изображениями:
- https://github.com/max-pfeiffer/uvicorn-поэзия
- https://github.com/max-pfeiffer/uvicorn-gunicorn-поэзия
Dockerfile базового образа: https://github.com/max-pfeiffer/uvicorn-poetry/blob/main/build/Dockerfile
ARG OFFICIAL_PYTHON_IMAGE
FROM ${OFFICIAL_PYTHON_IMAGE}
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
POETRY_VERSION=1.1.11 \
POETRY_HOME="/opt/poetry" \
POETRY_VIRTUALENVS_IN_PROJECT=true \
PYTHONPATH=/application_root \
VIRTUAL_ENVIRONMENT_PATH="/application_root/.venv"
ENV PATH="$POETRY_HOME/bin:$VIRTUAL_ENVIRONMENT_PATH/bin:$PATH"
# https://python-poetry.org/docs/#osx--linux--bashonwindows-install-instructions
RUN apt-get update \
&& apt-get install --no-install-recommends -y \
build-essential \
curl \
&& curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python - \
&& apt-get purge --auto-remove -y \
build-essential \
curl
COPY ./scripts/start_uvicorn.sh /application_server/
RUN chmod +x /application_server/start_uvicorn.sh
COPY ./scripts/pytest_entrypoint.sh ./scripts/black_entrypoint.sh /entrypoints/
RUN chmod +x /entrypoints/pytest_entrypoint.sh
RUN chmod +x /entrypoints/black_entrypoint.sh
EXPOSE 80
CMD ["/application_server/start_uvicorn.sh"]
Dockerfile примера образа проекта: https://github.com/max-pfeiffer/uvicorn-poetry/blob/main/examples/fast_api_multistage_build/Dockerfile
ARG BASE_IMAGE_NAME_AND_TAG=pfeiffermax/uvicorn-poetry:1.0.1-python3.9.8-slim-bullseye
FROM ${BASE_IMAGE_NAME_AND_TAG} as base-image
WORKDIR /application_root
# install [tool.poetry.dependencies]
# this will install virtual environment into /.venv because of POETRY_VIRTUALENVS_IN_PROJECT=true
# see: https://python-poetry.org/docs/configuration/#virtualenvsin-project
COPY ./poetry.lock ./pyproject.toml /application_root/
RUN poetry install --no-interaction --no-root --no-dev
FROM base-image as test-base-image
ENV LOG_LEVEL="debug"
COPY --from=base-image $VIRTUAL_ENVIRONMENT_PATH $VIRTUAL_ENVIRONMENT_PATH
# install [tool.poetry.dev-dependencies]
RUN poetry install --no-interaction --no-root
COPY /app /application_root/app/
COPY /tests /application_root/tests/
# image for running pep8 checks
FROM test-base-image as black-test-image
ENTRYPOINT /entrypoints/black_entrypoint.sh $0 $@
CMD ["--target-version py39", "--check", " --line-length 80", "app"]
# image for running unit tests
FROM test-base-image as unit-test-image
ENTRYPOINT /entrypoints/pytest_entrypoint.sh $0 $@
# You need to use pytest-cov as pytest plugin. Makes life very simple.
# tests directory is configured in pyproject.toml
# https://github.com/pytest-dev/pytest-cov
CMD ["--cov=app", "--cov-report=xml:/test_coverage_reports/unit_tests_coverage.xml"]
FROM base-image as development-image
ENV RELOAD="true" \
LOG_LEVEL="debug"
COPY --from=base-image $VIRTUAL_ENVIRONMENT_PATH $VIRTUAL_ENVIRONMENT_PATH
# install [tool.poetry.dev-dependencies]
RUN poetry install --no-interaction --no-root
COPY . /application_root/
FROM base-image as production-image
COPY --from=base-image $VIRTUAL_ENVIRONMENT_PATH $VIRTUAL_ENVIRONMENT_PATH
# This RUN statement fixes an issue while running the tests with GitHub Actions.
# Tests work reliable locally on my machine or running GitHub Actions using act.
# There is a bug with multistage builds in GitHub Actions which I can also reliable reproduce
# see: https://github.com/moby/moby/issues/37965
# Will also check if I can fix that annoying issue with some tweaks to docker build args
# see: https://gist.github.com/UrsaDK/f90c9632997a70cfe2a6df2797731ac8
RUN true
COPY /app /application_root/app/
Я вижу, что все ответы здесь используют метод 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"]
Вот другой подход, который оставляет Поэзию нетронутой, поэтому вы все еще можете использовать
poetry add
и т. д. Это хорошо, если вы используете контейнер разработки VS Code.
Короче говоря, установите Poetry, позвольте Poetry создать виртуальную среду, а затем входите в виртуальную среду каждый раз, когда вы запускаете новую оболочку, изменяя
.bashrc
.
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y python3 python3-pip curl
# Use Python 3 for `python`, `pip`
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 1 \
&& update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1
# Install Poetry
RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python3 -
ENV PATH "$PATH:/root/.local/bin/"
# Install Poetry packages (maybe remove the poetry.lock line if you don't want/have a lock file)
COPY pyproject.toml ./
COPY poetry.lock ./
RUN poetry install --no-interaction
# Provide a known path for the virtual environment by creating a symlink
RUN ln -s $(poetry env info --path) /var/my-venv
# Clean up project files. You can add them with a Docker mount later.
RUN rm pyproject.toml poetry.lock
# Hide virtual env prompt
ENV VIRTUAL_ENV_DISABLE_PROMPT 1
# Start virtual env when bash starts
RUN echo 'source /var/my-venv/bin/activate' >> ~/.bashrc
Напоминаем, что нет необходимости избегать использования virtualenv. Это не влияет на производительность , а Poetry не предназначен для работы без них.
Другие ответы были хорошими, но мне пришлось внести некоторые изменения, исходя из следующих требований:
- Мне нужно было маленькое изображение, и я решил использовать Alpine.
- Я хотел быть уверенным, что окончательный образ не будет запущен от имени пользователя root.
- Я был особенно сосредоточен на возможности запустить сценарий Poetry от своего имени.
Например, если у меня есть этот скрипт вpyproject.toml
:
...
[tool.poetry.scripts]
my_tool = "my_tool.cli.cli:start"
...
Тогда я хотелmy_tool
(CLI), чтобы быть моим DockerfileENTRYPOINT
чтобы аргументы, предоставленные командами контейнера, были аргументами моего CLI. Это решение выполнило именно то, что я искал:
# Stage - base
FROM python:3.11-alpine3.18 as base
ENV PYTHONFAULTHANDLER=1 \
PYTHONHASHSEED=random \
PYTHONUNBUFFERED=1
WORKDIR /app
# Stage - builder
FROM python:3.11-alpine3.18 as builder
ENV PIP_DEFAULT_TIMEOUT=100 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
POETRY_VERSION=1.7.0
RUN pip install poetry==$POETRY_VERSION
WORKDIR /app
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
# Stage - release
FROM base as release
# install sudo as root
RUN apk add --update sudo
# add new user
ENV USER=appuser
RUN adduser -D $USER \
&& echo "$USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USER \
&& chmod 0440 /etc/sudoers.d/$USER
ENV PATH="/venv/bin:$PATH"
COPY --from=builder /venv /venv
COPY --from=builder /app/dist .
RUN chown -hR $USER /venv
RUN . /venv/bin/activate && pip install *.whl
USER $USER
ENTRYPOINT ["my_cli"]
Dockerfile для моих приложений Python выглядит так:
FROM python:3.10-alpine
RUN apk update && apk upgrade
RUN pip install -U pip poetry==1.1.13
WORKDIR /app
COPY . .
RUN poetry export --without-hashes --format=requirements.txt > requirements.txt
RUN pip install -r requirements.txt
EXPOSE 8000
ENTRYPOINT [ "python" ]
CMD ["main.py"]
построить образ без питона-поэзии
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