如何使带有诗歌和 pyproject.toml 的 python 包的开发环境和生产环境之间的依赖关系保持一致
How to keep dependencies consistent between dev and production env for a python package with poetry and pyproject.toml
我有一个 python 包,其中的依赖项在 poetry.lock 文件中指定用于开发和测试。然后我构建并发布了安装在生产 docker 图像上的包。但问题是:已发布的包在 pyproject.toml 的 tool.poetry.dependencies 部分中指定了其依赖项,这可能与 poetry.lock 不同。因此,生产环境最终可能与测试环境具有不同的依赖关系。
我可以想出几种方法来实现一致性,但是 none 对我来说似乎很好:
在 pyproject.toml 中使用与 poetry.lock 中相同的版本集。这将保证发布的包具有与 dev/test 相同的依赖关系。但是此时保留 poetry.lock 文件有什么意义,因为如果没有 poetry.lock 文件,pyproject.toml 也可以用于 poetry install
。我认为这行得通,但我不明白为什么一开始还要 poetry.lock。
在生产 docker 映像中,在安装软件包本身之前从软件包 repo 和 运行 poetry install
中检出 poetry.lock 文件。但这会增加 docker 图像大小,如果 repo 是私有的,则会引入不必要的配置,并且总体上看起来不自然。
我对 Python 的这一部分还很陌生,所以也许其中之一就是“标准”工作流程。或者也许我只是完全错过了一些东西。感谢您的回答!
选项 1:不建议按照选项 1 中的描述确定依赖版本,因为它会导致不必要的严格包。这通常会导致本可避免的冲突,尤其是当您编写的包也是对其他项目的内部依赖时。
选项2:像这样处理依赖项肯定比选项1好,但比我想提出的选项更难维护。作为旁注,它还需要在您的 docker 图像上安装诗歌 - 如果您只想安装软件包,您只需要 pip
。
选项 3:在构建管道的开头创建一个 wheelhouse,并在后续步骤中使用它来安装运行时依赖项。这确保了测试代码和部署代码之间不可能存在差异,而且速度非常快,因为无需从 Internet 下载或构建纯源代码分发版。我将使用一个示例 .gitlab-ci.yml
来说明我的意思,但是这个概念应该可以毫无问题地转化为其他所有内容 CI/CD:
.gitlab-ci.yml
image: acaratti/pypoet
# a simple python:slim image that comes with poetry preinstalled
stages:
- build
- test
- release
variables:
WHEELHOUSE: wheelhouse
POETRY_VIRTUALENVS_PATH: venv # speeds up jobs
IMAGE_NAME: my-app
wheels:
stage: build
script:
- poetry install
- poetry build -f wheel
- poetry export -f requirements.txt -o requirements.txt
- poetry run pip wheel -w ${WHEELHOUSE} -r requirements.txt
- mv dist/* ${WHEELHOUSE}
artifacts:
expire_in: 1 week
paths:
- ${WHEELHOUSE}
- ${POETRY_VIRTUALENVS_PATH}
pytest:
stage: test
script:
# no need to run `poetry install` because
# the venv from the build-job gets re-used
- poetry run pytest
dockerize:
stage: release
image: docker:git
script:
- docker build . -t ${IMAGE_NAME}
- docker push ${IMAGE_NAME}
如果您在 docker化过程中有这样的操作室可用,Dockerfile 本身通常就足够简单了:
Dockerfile
FROM python:3.9-slim
COPY wheelhouse/* wheelhouse/
RUN pip install wheelhouse/*
ENTRYPOINT ["run", "my", "app"]
注意事项
您用于 wheel
作业的图像需要与 docker 文件中的基本图像相同的拱门(或任何试图安装驾驶室或重新使用的作业)虚拟环境)——如果你在你的 gitlab 工作中使用 debian,但在 prod-image 中使用 alpine,事情会很快崩溃。
这也扩展到在本地构建图像,如果这是您在开发过程中想要做的事情。如果您的工作站有不同的拱门,例如ubuntu,您可能无法再这样做了。 Here 是一个在您的工作站上创建基于 debian 的工作驾驶室的秘诀。
我有一个 python 包,其中的依赖项在 poetry.lock 文件中指定用于开发和测试。然后我构建并发布了安装在生产 docker 图像上的包。但问题是:已发布的包在 pyproject.toml 的 tool.poetry.dependencies 部分中指定了其依赖项,这可能与 poetry.lock 不同。因此,生产环境最终可能与测试环境具有不同的依赖关系。
我可以想出几种方法来实现一致性,但是 none 对我来说似乎很好:
在 pyproject.toml 中使用与 poetry.lock 中相同的版本集。这将保证发布的包具有与 dev/test 相同的依赖关系。但是此时保留 poetry.lock 文件有什么意义,因为如果没有 poetry.lock 文件,pyproject.toml 也可以用于
poetry install
。我认为这行得通,但我不明白为什么一开始还要 poetry.lock。在生产 docker 映像中,在安装软件包本身之前从软件包 repo 和 运行
poetry install
中检出 poetry.lock 文件。但这会增加 docker 图像大小,如果 repo 是私有的,则会引入不必要的配置,并且总体上看起来不自然。
我对 Python 的这一部分还很陌生,所以也许其中之一就是“标准”工作流程。或者也许我只是完全错过了一些东西。感谢您的回答!
选项 1:不建议按照选项 1 中的描述确定依赖版本,因为它会导致不必要的严格包。这通常会导致本可避免的冲突,尤其是当您编写的包也是对其他项目的内部依赖时。
选项2:像这样处理依赖项肯定比选项1好,但比我想提出的选项更难维护。作为旁注,它还需要在您的 docker 图像上安装诗歌 - 如果您只想安装软件包,您只需要 pip
。
选项 3:在构建管道的开头创建一个 wheelhouse,并在后续步骤中使用它来安装运行时依赖项。这确保了测试代码和部署代码之间不可能存在差异,而且速度非常快,因为无需从 Internet 下载或构建纯源代码分发版。我将使用一个示例 .gitlab-ci.yml
来说明我的意思,但是这个概念应该可以毫无问题地转化为其他所有内容 CI/CD:
.gitlab-ci.yml
image: acaratti/pypoet
# a simple python:slim image that comes with poetry preinstalled
stages:
- build
- test
- release
variables:
WHEELHOUSE: wheelhouse
POETRY_VIRTUALENVS_PATH: venv # speeds up jobs
IMAGE_NAME: my-app
wheels:
stage: build
script:
- poetry install
- poetry build -f wheel
- poetry export -f requirements.txt -o requirements.txt
- poetry run pip wheel -w ${WHEELHOUSE} -r requirements.txt
- mv dist/* ${WHEELHOUSE}
artifacts:
expire_in: 1 week
paths:
- ${WHEELHOUSE}
- ${POETRY_VIRTUALENVS_PATH}
pytest:
stage: test
script:
# no need to run `poetry install` because
# the venv from the build-job gets re-used
- poetry run pytest
dockerize:
stage: release
image: docker:git
script:
- docker build . -t ${IMAGE_NAME}
- docker push ${IMAGE_NAME}
如果您在 docker化过程中有这样的操作室可用,Dockerfile 本身通常就足够简单了:
Dockerfile
FROM python:3.9-slim
COPY wheelhouse/* wheelhouse/
RUN pip install wheelhouse/*
ENTRYPOINT ["run", "my", "app"]
注意事项
您用于 wheel
作业的图像需要与 docker 文件中的基本图像相同的拱门(或任何试图安装驾驶室或重新使用的作业)虚拟环境)——如果你在你的 gitlab 工作中使用 debian,但在 prod-image 中使用 alpine,事情会很快崩溃。
这也扩展到在本地构建图像,如果这是您在开发过程中想要做的事情。如果您的工作站有不同的拱门,例如ubuntu,您可能无法再这样做了。 Here 是一个在您的工作站上创建基于 debian 的工作驾驶室的秘诀。