Как исправить «Найдено 1 функцию (выборочная) загружено 0 функций» в моем конвейере DevOps функций Python Azure

Я пытаюсь развернуть функцию Python Azure на портале Azure с помощью конвейера сборки Azure DevOps. По какой-то причине код развертывается на сервере, но я получаю ошибку 404, пытаясь достичь конечной точки. Я получаю сообщение об ошибке 1 functions found (Custom) 0 functions loaded, а также сообщение об ошибке ModuleNotFoundError: No module named 'requests' на сервере. Я занимался этим уже несколько часов, но безуспешно. У меня есть следующие сведения об ошибке, которую я получаю (см. ниже). Мне интересно, может ли кто-нибудь помочь мне устранить неполадки и посмотреть, смогу ли я правильно развернуть этот конвейер DevOps. Пожалуйста, дайте мне знать, спасибо!

function_app.py:

import logging
import azure.functions as func

from src.api.controllers.etl_controller import ETLController
from src.helpers.configuration_helper import fetch_configurations

# Define your function
app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)

# Define your routes
@app.route(route = "run", methods=["POST"])
def run(req: func.HttpRequest) -> func.HttpResponse:
    # Fetch updated configurations
    fetch_configurations()
    
    # Process the request
    logging.info(f'Processing request: {req.method} {req.url}')
    controller = ETLController()
    return controller.run_elphi_etl(req)

функция.json:

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["post"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

хост.json:

{
  "version": "2.0",
  "functionTimeout": "00:30:00",
  "logging": {
    "logLevel": {
      "default": "Debug"
    },
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[4.*, 5.0.0)"
  }
}

требования.txt:

asgiref==3.8.1
asn1crypto==1.5.1
azure-appconfiguration==1.6.0
azure-core==1.30.2
azure-core-tracing-opentelemetry==1.0.0b11
azure-functions==1.20.0
azure-identity==1.17.1
azure-keyvault-secrets==4.8.0
azure-monitor-opentelemetry==1.6.0
azure-monitor-opentelemetry-exporter==1.0.0b27
certifi==2024.7.4
cffi==1.16.0
cfgv==3.4.0
charset-normalizer==3.3.2
colorama==0.4.6
coverage==7.5.4
cryptography==42.0.8
debugpy==1.8.2
Deprecated==1.2.14
distlib==0.3.8
filelock==3.15.4
fixedint==0.1.6
flatten-json==0.1.14
identify==2.6.0
idna==3.7
importlib_metadata==8.0.0
iniconfig==2.0.0
isodate==0.6.1
msal==1.30.0
msal-extensions==1.2.0
msrest==0.7.1
nodeenv==1.9.1
oauthlib==3.2.2
opentelemetry-api==1.26.0
opentelemetry-instrumentation==0.47b0
opentelemetry-instrumentation-asgi==0.47b0
opentelemetry-instrumentation-dbapi==0.47b0
opentelemetry-instrumentation-django==0.47b0
opentelemetry-instrumentation-fastapi==0.47b0
opentelemetry-instrumentation-flask==0.47b0
opentelemetry-instrumentation-psycopg2==0.47b0
opentelemetry-instrumentation-requests==0.47b0
opentelemetry-instrumentation-urllib==0.47b0
opentelemetry-instrumentation-urllib3==0.47b0
opentelemetry-instrumentation-wsgi==0.47b0
opentelemetry-resource-detector-azure==0.1.5
opentelemetry-sdk==1.26.0
opentelemetry-semantic-conventions==0.47b0
opentelemetry-util-http==0.47b0
packaging==24.1
platformdirs==4.2.2
pluggy==1.5.0
portalocker==2.10.1
pre-commit==3.7.1
psutil==5.9.8
pycparser==2.22
PyJWT==2.8.0
pyOpenSSL==24.2.1
pytest==8.2.2
pytest-cov==5.0.0
python-dotenv==1.0.1
pytz==2024.1
pywin32==306; sys_platform == 'win32'
PyYAML==6.0.1
requests==2.32.3
requests-oauthlib==2.0.0
six==1.16.0
snowflake-connector-python==3.11.0
sortedcontainers==2.4.0
tomlkit==0.13.0
typing_extensions==4.12.2
urllib3==2.2.2
virtualenv==20.26.3
wrapt==1.16.0
zipp==3.19.2

azure-pipelines-dev.yaml:

trigger:
  branches:
    include:
      - develop

pool:
  vmImage: "ubuntu-latest"

variables:
  PYTHON_VERSION: "3.11.8"
  FUNCTIONAPP_NAME: "fn-elphi-etl-dev"
  SERVICE_CONNECTION: "Elphi-ETL-Dev"
  RESOURCE_GROUP: "fn-elphi-etl-dev"
  PACKAGE_PATH: "$(Build.ArtifactStagingDirectory)/fn_elphi_etl.zip"

steps:
  - task: UsePythonVersion@0
    inputs:
      versionSpec: "$(PYTHON_VERSION)"
      addToPath: true
    displayName: "Install Python $(PYTHON_VERSION)"

  - script: |
      python -m pip install --upgrade pip
    displayName: "Upgrade pip"

  - script: |
      python -m pip install -r requirements.txt
    displayName: "Install dependencies"

  - script: |
      python -m pytest -v --cov=src --cov-report=term --cov-report=html --cov-report=xml:coverage.xml --junitxml=test-results.xml
    displayName: "Run tests with coverage"

  - task: PublishTestResults@2
    inputs:
      testResultsFormat: "JUnit"
      testResultsFiles: "test-results.xml"
      failTaskOnFailedTests: true
      testRunTitle: "Pytest Results"
    displayName: "Publish Pytest Results"

  - script: |
      mkdir -p $(Build.ArtifactStagingDirectory)/src
      shopt -s dotglob
      cp -r * $(Build.ArtifactStagingDirectory)/src/
      ls -l $(Build.ArtifactStagingDirectory)/src
    displayName: "Copy sources and all directories to artifact staging directory and list contents"

  - task: ArchiveFiles@2
    inputs:
      rootFolderOrFile: "$(Build.ArtifactStagingDirectory)/src"
      includeRootFolder: false
      archiveType: "zip"
      archiveFile: "$(PACKAGE_PATH)"
      replaceExistingArchive: true
    displayName: "Archive files"

  - task: PublishPipelineArtifact@1
    inputs:
      targetPath: "$(Build.ArtifactStagingDirectory)/src"
      artifact: "$(FUNCTIONAPP_NAME)"
    displayName: "Publish Python project as artifact"

  - task: PublishCodeCoverageResults@2
    inputs:
      summaryFileLocation: "$(Build.SourcesDirectory)/coverage.xml"
    displayName: "Publish code coverage results"

  - task: AzureFunctionApp@1
    inputs:
      azureSubscription: "$(SERVICE_CONNECTION)"
      appType: "functionAppLinux"
      appName: "$(FUNCTIONAPP_NAME)"
      package: "$(PACKAGE_PATH)"
      runtimeStack: "python|3.11"
      startUpCommand: "func start"
    displayName: "Deploy Azure Function App"

Логи сервера:

2024-07-26T17:36:54.860 [Information] Starting JobHost
2024-07-26T17:36:54.861 [Information] Starting Host (HostId=fn-elphi-etl-dev, InstanceId=f791376b-5871-4ad4-ab78-47b08af12162, Version=4.34.2.2, ProcessId=26, AppDomainId=1, InDebugMode=True, InDiagnosticMode=False, FunctionsExtensionVersion=~4)
2024-07-26T17:36:54.863 [Information] Loading functions metadata
2024-07-26T17:36:54.869 [Information] Reading functions metadata (Custom)
2024-07-26T17:36:54.871 [Information] 1 functions found (Custom)
2024-07-26T17:36:54.873 [Information] 0 functions loaded
2024-07-26T17:36:54.875 [Debug] FUNCTIONS_WORKER_RUNTIME value: 'python'
2024-07-26T17:36:54.876 [Debug] Adding Function descriptor provider for language python.
2024-07-26T17:36:54.876 [Debug] Creating function descriptors.
2024-07-26T17:36:54.877 [Debug] Function descriptors created.
2024-07-26T17:36:54.878 [Debug] Placeholder mode is enabled: False
2024-07-26T17:36:54.878 [Debug] RpcFunctionInvocationDispatcher received no functions
2024-07-26T17:36:54.878 [Information] Generating 0 job function(s)
2024-07-26T17:36:54.885 [Warning] No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).
2024-07-26T17:36:54.888 [Information] Initializing function HTTP routes
2024-07-26T21:00:17.863 [Debug] Hosting starting
2024-07-26T21:00:17.950 [Information] Traceback (most recent call last):
2024-07-26T21:00:17.950 [Information] File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/utils/wrappers.py", line 44, in call
2024-07-26T21:00:17.950 [Information] return func(*args, **kwargs)
2024-07-26T21:00:17.950 [Information] ^^^^^^^^^^^^^^^^^^^^^
2024-07-26T21:00:17.950 [Information] File "/azure-functions-host/workers/python/3.11/LINUX/X64/azure_functions_worker/loader.py", line 238, in index_function_app
2024-07-26T21:00:17.950 [Information] imported_module = importlib.import_module(module_name)
2024-07-26T21:00:17.950 [Information] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2024-07-26T21:00:17.950 [Information] File "/usr/local/lib/python3.11/importlib/__init__.py", line 126, in import_module
2024-07-26T21:00:17.950 [Information] return _bootstrap._gcd_import(name[level:], package, level)
2024-07-26T21:00:17.950 [Information] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap_external>", line 940, in exec_module
2024-07-26T21:00:17.950 [Information] File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/function_app.py", line 4, in <module>
2024-07-26T21:00:17.950 [Information] from src.api.controllers.etl_controller import ETLController
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/src/api/controllers/etl_controller.py", line 6, in <module>
2024-07-26T21:00:17.950 [Information] from src.api.services.etl_service import ETLService
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/src/api/services/etl_service.py", line 4, in <module>
2024-07-26T21:00:17.950 [Information] from src.api.services.elphi_service import ElphiService
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/src/api/services/elphi_service.py", line 3, in <module>
2024-07-26T21:00:17.950 [Information] from src.clients.elphi_client import ElphiClient
2024-07-26T21:00:17.950 [Information] File "/home/site/wwwroot/src/clients/elphi_client.py", line 3, in <module>
2024-07-26T21:00:17.950 [Information] import requests
2024-07-26T21:00:17.950 [Error] ModuleNotFoundError: No module named 'requests'

А вот файловая структура на сервере после развертывания:

Кроме того, если я запускаю func azure functionapp publish fn-elphi-etl-dev --build remote на своем локальном компьютере, он работает правильно, и вместо этого я получаю следующую структуру папок:

У меня тоже есть еще тонна журналов, но я думаю, что недостаточно символов, чтобы опубликовать все...

Adam Steinberger 29.07.2024 23:47

Это функция Python V2? Если да, то у модели V2 нет function.json. и привязки определены внутри файла function_app.py. Обратитесь к блогу .

Pravallika KV 30.07.2024 08:23
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Судя по вашим снимкам экрана, когда вы развертываете приложение-функцию на локальном компьютере, оно содержит папку .python_packages. Требуемые пакеты (включая пакет запроса) будут сохранены в этой папке.

Но когда вы развертываете приложение-функцию через Azure Pipelines, оно не содержит .python_packages. В этом случае пакеты не могут быть найдены при запуске функции Azure.

Чтобы решить эту проблему, вы можете добавить аргумент --target в команду pip install, чтобы принудительно сохранить пакеты в правильной папке и убедиться, что эта папка может быть включена в zip-пакет:

Например:

python -m pip install --target = "./.python_packages/lib/site-packages" -r requirements.txt

Задача конвейера:

  - script: |
      python -m pip install --target = "./.python_packages/lib/site-packages" -r requirements.txt
    displayName: "Install dependencies"

Для получения более подробной информации вы можете обратиться к этому документу: Примеры конвейеров сборки YAML

@AdamSteinberger Рад знать, что это может сработать для вас.

Kevin Lu-MSFT 30.07.2024 13:55

Другие вопросы по теме