英文:
Using poetry to install dependencies in Azure Functions pipeline
问题
目标
使用poetry和pyproject.toml
文件部署一个包括函数应用的Python项目。指定的模块应该可以被Python文件访问。
背景
我的项目托管在Azure DevOps存储库中。通过Azure YAML管道,它使用poetry install
进行“构建”,创建一个包含所有依赖项的子文件夹.venv
,并打包成一个zip文件。该存档被存储为一个构件,并在部署步骤中拉取到虚拟机上,我在那里解压它,以便.venv
文件夹再次存在。
我原本期望通过使用poetry env use .venv/bin/python.exe
或source .venv/bin/activate
来激活虚拟环境就足够了。这两个步骤都有效并且可以激活虚拟环境,但虚拟环境不会保持激活状态,Python脚本中无法访问模块。在本地运行poetry install
就足够了,而且效果非常好。
挑战
A)poetry将依赖项安装到与可访问位置不同的位置
通常的解决方法是使用pip install并使用参数--target
将依赖项放在文件夹./.python_packages/lib/site-packages
中。不幸的是,poetry没有这样的功能。
B)激活虚拟环境只在流水线的单个步骤中有效,并且之后会丢失
使用source venv/bin/activate
激活poetry环境运行得很好,作为激活和pip list
结合的步骤示例:
- bash: |
source .venv/bin/activate
pip list
--> 列出了使用poetry install
安装的所有依赖项
- bash: |
source .venv/bin/activate
- bash: |
pip list
--> 仅列出Azure基本虚拟环境的依赖项(即没有pandas)
C)因此,运行函数应用会导致异常:ModuleNotFoundError:找不到模块'pandas'
问题
如何在Azure流水线中使用poetry,以便Python脚本可以访问已安装的依赖项?
英文:
Goal
Deploy a python project including a function app using poetry and the pyproject.toml
file. The specified modules should be accessible by the python files.
Background
My project is hosted in a Azure DevOps repository. With an Azure YAML Pipeline it is "built" with poetry install
, creating a sub-folder .venv
with all dependencies, and bundled into a zip file. The archive is stored as an artifact and pulled onto the VM in the deploy step, where I extract it, so that the .venv
folder is present again.
My expectation was that activating the venv with poetry env use .venv/bin/python.exe
or source .venv/bin/activate
was sufficient. Both steps work and do activate the venv, but it does not stick and modules are not accessible in python scripts. Locally, running poetry install is sufficient and works very well.
Challenges
A) poetry installs dependencies in a different location than what is accessible
Usually the solution is to use pip install with the argument --target
to place dependencies in the folder ./.python_packages/lib/site-packages
. poetry does not have a functionality like this, unfortunately.
B) activating virtual environments is only valid in a single steps of pipelines and gets lost afterwards
With source venv/bin/activate
activating the poetry environment works fine, as a combined step with activation and pip list
demonstrates:
- bash: |
source .venv/bin/activate
pip list
--> lists all dependecies that were installed using poetry install
- bash: |
source .venv/bin/activate
- bash: |
pip list
--> lists only dependecies of the Azure base virtual environment (i.e. no pandas)
C) as consequence, running the function app leads to Exception: ModuleNotFoundError: No module named 'pandas'
Question
How can I use poetry in Azure pipelines, so that installed dependencies are accessible by python scripts?
答案1
得分: 0
您可以使用下面的YAML脚本来使用poetry安装依赖并运行函数。
我的存储库:-
我的pyproject.toml代码:-
[tool.poetry]
name = "your-project"
version = "0.1.0"
description = "Your project description"
authors = ["Your Name <your-email@example.com>"]
[tool.poetry.dependencies]
python = "^3.9"
pandas = "^1.3.0"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
代码:-
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.x'
addToPath: true
- script: |
pip install poetry
poetry install
poetry add azure-functions
displayName: '使用Poetry安装依赖'
- script: |
source $HOME/.poetry/env
source .venv/bin/activate
poetry run python HttpTrigger1/__init__.py
displayName: '运行Azure函数'
输出:-
英文:
You can make use of below YAML script to install dependencies using poetry and run function.
My Repository:-
My pyproject.toml code:-
[tool.poetry]
name = "your-project"
version = "0.1.0"
description = "Your project description"
authors = ["Your Name <your-email@example.com>"]
[tool.poetry.dependencies]
python = "^3.9"
pandas = "^1.3.0"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
Code:-
trigger:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.x'
addToPath: true
- script: |
pip install poetry
poetry install
poetry add azure-functions
displayName: 'Install dependencies with Poetry'
- script: |
source $HOME/.poetry/env
source .venv/bin/activate
poetry run python HttpTrigger1/__init__.py
displayName: 'Run Azure Function'
Output:-
答案2
得分: 0
以下是您提供的文本的中文翻译:
解决此问题涉及两个部分。
-
确保在 Azure 流水线期间正确安装依赖项到虚拟机。
-
在正确的位置安装依赖项,以便 Python 脚本可以实际访问它们。
对于第一部分,我最终没有在我的 pipeline.yml 中使用构建和依赖项步骤,而是只使用一个部署步骤。这样只初始化一个虚拟机,我只需要安装一次 poetry。
对于第二部分,将其导出到 requirements.txt 并使用 pip --target 进行安装是在我的情况下使其正确运行的唯一方法。请注意,有一个 poetry 扩展旨在解决这个问题(https://github.com/python-poetry/poetry-plugin-bundle)。
以下是包含所有相关部分的流水线:
trigger:
- main
variables:
pythonVersion: 3.10
azureSubscription: <YOUR_SUBSCRIPTION_CODE>
functionAppName: <YOUR_FUNCTION_APP_NAME>
vmImageName: 'ubuntu-latest'
workingDirectory: '$(System.DefaultWorkingDirectory)'
jobs:
- deployment: Deploy
environment: <YOUR_ENV_NAME>
pool:
vmImage: $(vmImageName)
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: UsePythonVersion@0
inputs:
versionSpec: $(pythonVersion)
- bash: |
curl -sSL https://install.python-poetry.org | python -
export PATH=$PATH:$HOME/.poetry/bin
displayName: 'Install poetry'
- bash: |
poetry lock --no-update
poetry install --no-root --without dev,local
displayName: 'Update lock file and install dependencies'
- bash: |
poetry env use /home/vsts/work/1/s/.venv/bin/python
displayName: 'Activate virtual environment'
- bash: |
poetry export -f requirements.txt --output requirements.txt
pip install --target="./.python_packages/lib/site-packages" -r ./requirements.txt
displayName: 'Export poetry dependencies and install'
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: '$(workingDirectory)'
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
replaceExistingArchive: true
displayName: 'Archive files'
- publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
artifact: drop
displayName: 'Publish artifact'
- task: AzureFunctionApp@2
inputs:
azureSubscription: '$(azureSubscription)'
appType: functionAppLinux
appName: $(functionAppName)
package: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
deploymentMethod: 'auto'
displayName: 'Deploy Azure function app'
如果您有关于详细信息和特定步骤的任何问题,请随时提问。
英文:
Solving the issue involved two parts.
-
Making sure the dependencies get installed to the correct VM during the Azure pipeline.
-
Getting the dependencies installed at the correct location, so that Python scripts can actually access them.
For 1., I ended up not using a build and dependency step in my pipeline.yml, but to only use a single step, with a deployment step. That way only one VM is initialized and I only have to install poetry once.
For 2., exporting to a requirements.txt and installing with pip --target was the only way to get it to run correctly in my situation. Note that there is a poetry extension that aims to solve this (https://github.com/python-poetry/poetry-plugin-bundle).
Here is the pipeline with all relevant parts:
trigger:
- main
variables:
pythonVersion: 3.10
azureSubscription: <YOUR_SUBSCRIPTION_CODE>
functionAppName: <YOUR_FUNCTION_APP_NAME>
vmImageName: 'ubuntu-latest'
workingDirectory: '$(System.DefaultWorkingDirectory)'
jobs:
- deployment: Deploy
environment: <YOUR_ENV_NAME>
pool:
vmImage: $(vmImageName)
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: UsePythonVersion@0
inputs:
versionSpec: $(pythonVersion)
- bash: |
curl -sSL https://install.python-poetry.org | python -
export PATH=$PATH:$HOME/.poetry/bin
displayName: 'Install poetry'
- bash: |
poetry lock --no-update
poetry install --no-root --without dev,local
displayName: 'Update lock file and install dependencies'
- bash: |
poetry env use /home/vsts/work/1/s/.venv/bin/python
displayName: 'Activate virtual environment'
- bash: |
poetry export -f requirements.txt --output requirements.txt
pip install --target="./.python_packages/lib/site-packages" -r ./requirements.txt
displayName: 'Export poetry dependencies and install'
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: '$(workingDirectory)'
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
replaceExistingArchive: true
displayName: 'Archive files'
- publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
artifact: drop
displayName: 'Publish artifact'
- task: AzureFunctionApp@2
inputs:
azureSubscription: '$(azureSubscription)'
appType: functionAppLinux
appName: $(functionAppName)
package: '$(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip'
deploymentMethod: 'auto'
displayName: 'Deploy Azure function app'
Feel free to ask any questions you have about details and particular steps.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论