将Python诗歌与Docker集成

时间:2018-12-18 14:28:19

标签: python docker dockerfile python-poetry

您能给我一个Dockerfile的例子吗,我可以在其中将poetry.lock和pyproject.toml所需的所有软件包从Docker安装到我的映像/容器中?

8 个答案:

答案 0 :(得分:16)

poetrydocker一起使用时要记住几件事。

安装

安装poetry的官方方法是通过

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

通过这种方式可以将poetry及其依赖项与您的依赖项隔离。但是,在我看来,这并不是一件好事,原因有两个:

  1. poetry版本可能会进行更新,并且会中断您的构建。在这种情况下,您可以指定POETRY_VERSION环境变量。安装程序会尊重它
  2. 我不喜欢将互联网上的内容通过管道传输到我的容器中的想法,而没有任何可能的文件修改保护

因此,我使用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.tomlpoetry.lock文件更改时重新安装它们。否则构建速度会很慢。为了实现工作缓存层,我们应该放置:

COPY poetry.lock pyproject.toml /code/

在安装poetry之后,但在添加任何其他文件之前。

Virtualenv

接下来要记住的是创建virtualenvdocker中不需要它。它已经被隔离。因此,我们使用poetry config settings.virtualenvs.create false设置将其关闭。

发展与生产

如果您像我一样在开发和生产中使用相同的Dockerfile,则需要基于一些环境变量安装不同的依赖项集:

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

通过这种方式$YOUR_ENV将控制要安装的依赖项集:全部(默认)或仅带有--no-dev标志的生产。

您可能还想添加更多选项以获得更好的体验:

  1. --no-interaction不要问任何互动问题
  2. --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

答案 1 :(得分:11)

具有Poetry和venv的多阶段Docker构建

请勿禁用virtualenv创建。 Virtualenvs serve a purpose in Docker builds,因为它们提供了一种优雅的方式来利用多阶段构建。简而言之,您的构建阶段会将所有内容都安装到virtualenv中,最后一步只是将virtualenv复制到一个小的映像中。

在复制代码之前,先使用poetry export并安装您的固定要求。这样一来,您就可以使用Docker构建缓存,而不必仅仅因为更改了代码行而重新安装依赖项。

请勿使用poetry install安装代码,因为它将执行可编辑的安装。而是使用poetry build来构建一个转轮,然后将其pip安装到您的virtualenv中。 (由于PEP 517,整个过程也可以用简单的pip install .执行,但是由于build isolation,您最终将安装另一本《诗歌》。)

这是一个示例文件,该文件将Flask应用安装到Alpine映像中,并依赖于Postgres。本示例使用入口点脚本来激活virtualenv。但是通常,没有入口点脚本就可以了,因为您可以在/venv/bin/python指令中的CMD处简单地引用Python二进制文件。

Dockerfile

FROM python:3.7.4-alpine3.10 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.0

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()

答案 2 :(得分:3)

这是最适合我的最小配置:

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's configuration安全。

作为琐事,我将添加if editable installs will be possible for pyproject.toml projects,可以删除一两行:

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

答案 3 :(得分:3)

TL; DR

我已经能够使用poetryDjango项目设置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,您实际上可以得到更大的图像,并且需要花费一些时间构建(有关更多信息,请阅读this article)。

  • 使用此配置生成容器的速度比使用高山映像更快,因为我不需要添加一些额外的软件包即可正确安装Python软件包。

  • 我正在直接从文档中提供的URL安装poetry。我知道sobolevn提供的警告。但是,我认为从长远来看,默认情况下使用poetry的最新版本比依赖于我应定期更新的环境变量更好。

  • 更新环境变量PATH至关重要。否则,您将收到一条错误消息,指出未找到诗歌

  • 依赖关系直接安装在容器的python解释器中。在安装依赖项之前,它不会创建poetry来创建虚拟环境。

如果您需要此alpine的{​​{1}}版本:

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才能正常工作。

答案 4 :(得分:2)

这是对answer provided by @Claudio的次要修订,它使用了@sobolevn in his answer描述的新poetry install --no-root功能。

为了强制poetry将依赖项安装到特定的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作为基础,但是,使其适应于基于高山的图像应该是一件微不足道的任务。

答案 5 :(得分:2)

我使用锁包(依赖于锁文件中的所有版本的包)创建了一个解决方案。这将导致干净的 pip-only 安装,没有需求文件。

步骤是:构建包,构建锁包,将两个轮子复制到您的容器中,使用 pip 安装两个轮子。

安装是:poetry add --dev poetry-lock-package

docker build 之外的步骤是:

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

答案 6 :(得分:1)

这是一个剥离的示例,其中首先将具有依赖项的层(仅在这些依赖项更改时才构建),然后将具有完整源代码的层添加到映像中。将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 . ./

答案 7 :(得分:1)

我看到这里的所有答案都是使用 pip 方式安装 Poetry 以避免版本问题。 安装诗歌的官方方法读取 POETRY_VERSION 环境变量,如果定义为安装最合适的版本。

github here 中存在一个问题,我认为这张票的解决方案非常有趣:

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