Indrid
Indrid

Reputation: 1172

Azure Python Functions requirements.txt ignored when deployed from DevOps

I'm trying to deploy a handful of Python functions into my consumption plan based Function App using Azure DevOps YAML pipeline.

Despite following all the advice found here: https://learn.microsoft.com/en-us/azure/azure-functions/recover-python-functions?tabs=manual%2Cbash&pivots=python-mode-configuration#enable-remote-build

I was still unable to: (a) See why the requirements.txt in the root of the project was being ignored and (b) Work out why, when I download my package from the URL provided at the APPLICATION_SETTINGS -> WEBSITE_RUN_FROM_PACKAGE variable there was in fact no .python_packages directory there at all.

This was despite setting SCM_DO_BUILD_DURING_DEPLOYMENT and ENABLE_ORYX_BUILD to True.

After hours of scrabbling around I eventually discovered a potential solution, which was to install the dependencies right there in the DevOps pipeline with:

steps:
  - task: UsePythonVersion@0
    displayName: "Use Python 3.10"
    inputs:
      versionSpec: 3.10
    - bash: |
        pip install --target="./.python_packages/lib/site-packages" -r ./requirements.txt
      workingDirectory: $(workingDirectory)
      displayName: "Install application dependencies"

Prior to then creating and publishing the archive, which is then consumed by the AzureFunctionApp@2 action to deploy it.

Is this the correct way to accomplish this? The Python Functions documentation seems to suggest that the requirements.txt should be processed during the build, but it absolutely is not.

Where I am stuck is in trying to understand what that step is actually doing. Am I correct in saying it is installing the dependencies into the Python instance on the DevOps Agent just for the purposes of the agents own .python_packages folder, post install, to be copied into the artifact which is then deployed? Is that the case?

Fundamentally, my question is: Isn't there a better way?

Thanks!

Upvotes: 3

Views: 5768

Answers (1)

SiddheshDesai
SiddheshDesai

Reputation: 8157

Is this the correct way to accomplish this? The Python Functions documentation seems to suggest that the requirements.txt should be processed during the build, but it absolutely is not.

Yes, The requirements.txt packages needs to be installed in the DevOps agent and then the build artifact with the packages are deployed to the Function app for the Function Trigger to work. Even the Default Azure DevOps yaml code to Deploy the Python Function has separate pip install step to install packages. Refer below:-

I have used the Template below and selected my Function app and path set to - $(System.DefaultWorkingDirectory)

My Azure DevOps yaml script:-

enter image description here

trigger:
- master

variables:
  
  azureSubscription: 'xxxxxxxxxx354dd'

  # Function app name
  functionAppName: 'siliconfunc76'

  # Agent VM image name
  vmImageName: 'ubuntu-latest'

  # Working Directory
  workingDirectory: '$(System.DefaultWorkingDirectory)'

stages:
- stage: Build
  displayName: Build stage

  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)

    steps:
    - bash: |
        if [ -f extensions.csproj ]
        then
            dotnet build extensions.csproj --runtime ubuntu.16.04-x64 --output ./bin
        fi
      workingDirectory: $(workingDirectory)
      displayName: 'Build extensions'

    - task: UsePythonVersion@0
      displayName: 'Use Python 3.10'
      inputs:
        versionSpec: 3.10 # Functions V2 supports Python 3.6 as of today

    - bash: |
        pip install --target="./.python_packages/lib/site-packages" -r ./requirements.txt
      workingDirectory: $(workingDirectory)
      displayName: 'Install application dependencies'

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

    - publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
      artifact: drop

- stage: Deploy
  displayName: Deploy stage
  dependsOn: Build
  condition: succeeded()

  jobs:
  - deployment: Deploy
    displayName: Deploy
    environment: 'development'
    pool:
      vmImage: $(vmImageName)

    strategy:
      runOnce:
        deploy:

          steps:
          - task: AzureFunctionApp@1
            displayName: 'Azure functions app deploy'
            inputs:
              azureSubscription: '$(azureSubscription)'
              appType: functionAppLinux
              appName: $(functionAppName)
              package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip'

Output:-

All the package dependencies are installed in the Build step where Function is built on Azure DevOps agent like below:-

enter image description here

Those packages are Archived as an artifact like below:-

enter image description here

Then the Archived folder is published as an Artifact like below:-

enter image description here

In the next stage those Published artifact is Downloaded and Deployed in the Function App like below:-

enter image description here

enter image description here

Above method is the Ideal and efficient method for now to Deploy the Function app.

  • You can only build the Function app with the Build step and use Release pipeline to Deploy the Function App via Release Refer my SO thread answer1 for the same.

  • Alternatively you can Refer My YAML script from this SO thread answer2 where I have utilized func azure functionapp publish functionappname --python And installed the dependencies with pip install in the same build step.

  • To Deploy FunctionV2 using Azure DevOps refer my SO thread answer3.

Upvotes: 7

Related Questions