I have a Django project deployed on an Azure Web App (Linux, P2v2, Python 3.10, Django 4.2), which is functioning as expected. However, the deployment time is extremely slow, taking between 15 to 20 minutes to complete even a small change in the code. This is significantly longer than the deployment times I have experienced with other services.
Here is a snippet of the deployment log:
2023-10-31T12:50:59.953Z - Updating branch 'master'.
2023-10-31T12:51:04.983Z - Updating submodules.
2023-10-31T12:51:05.086Z - Preparing deployment for commit id 'XXXXXXXXX'.
2023-10-31T12:51:05.338Z - PreDeployment: context.CleanOutputPath False
2023-10-31T12:51:05.432Z - PreDeployment: context.OutputPath /home/site/wwwroot
2023-10-31T12:51:05.535Z - Repository path is /home/site/repository
2023-10-31T12:51:05.638Z - Running oryx build...
2023-10-31T12:51:05.644Z - Command: oryx build /home/site/repository -o /home/site/wwwroot --platform python --platform-version 3.10 -p virtualenv_name=antenv --log-file /tmp/build-debug.log -i /tmp/8dbda100b9921ec --compress-destination-dir | tee /tmp/oryx-build.log
2023-10-31T12:51:06.599Z - Operation performed by Microsoft Oryx, https://github.com/Microsoft/Oryx
2023-10-31T12:51:06.614Z - You can report issues at https://github.com/Microsoft/Oryx/issues
2023-10-31T12:51:06.643Z - Oryx Version: 0.2.20230508.1, Commit: 7fe2bf39b357dd68572b438a85ca50b5ecfb4592, ReleaseTagName: 20230508.1
2023-10-31T12:51:06.658Z - Build Operation ID: 10aafab84d551a74
2023-10-31T12:51:06.664Z - Repository Commit : XXXXXXXXXXXXXX
2023-10-31T12:51:06.670Z - OS Type : bullseye
2023-10-31T12:51:06.677Z - Image Type : githubactions
2023-10-31T12:51:06.695Z - Detecting platforms...
2023-10-31T12:51:15.575Z - Detected following platforms:
2023-10-31T12:51:15.608Z - nodejs: 16.20.2
2023-10-31T12:51:15.613Z - python: 3.10.8
2023-10-31T12:51:15.618Z - Version '16.20.2' of platform 'nodejs' is not installed. Generating script to install it...
2023-10-31T12:51:15.623Z - Version '3.10.8' of platform 'python' is not installed. Generating script to install it...
2023-10-31T12:51:15.811Z - Using intermediate directory '/tmp/8dbda100b9921ec'.
2023-10-31T12:51:15.837Z - Copying files to the intermediate directory...
2023-10-31T12:51:17.447Z - Done in 2 sec(s).
2023-10-31T12:51:17.465Z - Source directory : /tmp/8dbda100b9921ec
2023-10-31T12:51:17.470Z - Destination directory: /home/site/wwwroot
2023-10-31T12:51:17.487Z - Downloading and extracting 'nodejs' version '16.20.2' to '/tmp/oryx/platforms/nodejs/16.20.2'...
2023-10-31T12:51:17.496Z - Detected image debian flavor: bullseye.
2023-10-31T12:51:18.418Z - Downloaded in 1 sec(s).
2023-10-31T12:51:18.431Z - Verifying checksum...
2023-10-31T12:51:18.465Z - Extracting contents...
2023-10-31T12:51:19.688Z - performing sha512 checksum for: nodejs...
2023-10-31T12:51:19.876Z - Done in 2 sec(s).
2023-10-31T12:51:19.907Z - Downloading and extracting 'python' version '3.10.8' to '/tmp/oryx/platforms/python/3.10.8'...
2023-10-31T12:51:19.913Z - Detected image debian flavor: bullseye.
2023-10-31T12:51:21.326Z - Downloaded in 2 sec(s).
2023-10-31T12:51:21.343Z - Verifying checksum...
2023-10-31T12:51:21.349Z - Extracting contents...
2023-10-31T12:51:23.829Z - performing sha512 checksum for: python...
2023-10-31T12:51:24.147Z - Done in 5 sec(s).
2023-10-31T12:51:24.180Z - image detector file exists, platform is python..
2023-10-31T12:51:24.193Z - OS detector file exists, OS is bullseye..
2023-10-31T12:51:24.298Z - Python Version: /tmp/oryx/platforms/python/3.10.8/bin/python3.10
2023-10-31T12:51:24.305Z - Creating directory for command manifest file if it does not exist
2023-10-31T12:51:24.311Z - Removing existing manifest file
2023-10-31T12:51:24.335Z - Python Virtual Environment: antenv
2023-10-31T12:51:24.341Z - Creating virtual environment...
2023-10-31T12:51:30.102Z - Activating virtual environment...
2023-10-31T12:51:30.114Z - Running pip install...
(like 4 minutes installing python libraries)
2023-10-31T12:55:37.571Z - Not a vso image, so not writing build commands
2023-10-31T12:55:37.576Z - Preparing output...
2023-10-31T12:55:37.586Z - Copying files to destination directory '/tmp/_preCompressedDestinationDir'...
2023-10-31T12:57:07.095Z - Done in 93 sec(s).
2023-10-31T12:57:07.109Z - Compressing content of directory '/tmp/_preCompressedDestinationDir'...
2023-10-31T13:05:28.569Z - Copied the compressed output to '/home/site/wwwroot'
2023-10-31T13:05:28.603Z - Removing existing manifest file
2023-10-31T13:05:28.641Z - Creating a manifest file...
2023-10-31T13:05:28.675Z - Manifest file created.
2023-10-31T13:05:28.684Z - Copying .ostype to manifest output directory.
2023-10-31T13:05:28.695Z - Done in 853 sec(s).
2023-10-31T13:05:29.132Z - Running post deployment command(s)...
2023-10-31T13:05:29.459Z - Generating summary of Oryx build
2023-10-31T13:05:29.588Z - Parsing the build logs
2023-10-31T13:05:29.703Z - Found 0 issue(s)
2023-10-31T13:05:29.884Z - Build Summary :
2023-10-31T13:05:29.973Z - ===============
2023-10-31T13:05:30.067Z - Errors (0)
2023-10-31T13:05:30.163Z - Warnings (0)
2023-10-31T13:05:30.400Z - Triggering recycle (preview mode disabled).
2023-10-31T13:05:30.534Z - Deployment successful. deployer = deploymentPath =
As you can see, most of the time is spent in the "Compressing content of directory '/tmp/_preCompressedDestinationDir'" step (853 seconds).
How can I expedite the deployment process? Even if it's not a full deployment, I need a more efficient way to make minor changes to the code if needed.
Investigations:
I have been exploring the App Service's build system, Oryx, and attempted various combinations of the Oryx configurations to optimize the deployment time, but I haven't seen any significant improvements.
I am curious about the necessity of compressing the virtual environment into 'output.tar.gz' only to have it unpacked into a temporary folder later. Is there a way to bypass this compression step?
Additionally, is there a way to modify the variables in the oryx-manifest.toml file? Adding application variable or adding a custom oryx-manifest to my code, both had no effect.
Any insights or suggestions would be greatly appreciated. Thank you!
Approach 1:-
According to this MS Document Deploying your Azure Web app via Zip deployment is the best way to improve Deployment performance.
If you have your Azure Django app in github repository you can download the repository as it is as a Zip Folder and then Deploy it in the Azure Web app like below:-
I Downloaded a sample Django code from this Github repository and deployed it via below Zip command in my Azure Web app:-
After your Azure Web app is created Add the below setting in your Web app configuration:-
Because, with WEBSITE_RUN_FROM_PACKAGE
enabled, App Service launches the application straight from the mounted directory and mounts the uploaded package as the read-only wwwroot
directory. Refer this SO thread answer on the same.
Now login to your Azure account in your terminal set your subscription and run the command below to deploy your Django Web app:-
az webapp deployment source config-zip --resource-group v-sidrg --name siliconwebappdjango --src C:\msdocs-python-django-webapp-quickstart-main.zip
Output:-
The Deployment got successful with less than a minute:-
Regarding Oryx Build Refer this Blog for more details in understanding Oryx behaviour.
When you are using Local Git
, Bitbucket
, External Git
, GitHub Actions
, Oryx is enabled by default.
Approach 2:-
py -m venv .venv
.venv\scripts\activate
pip install -r requirements.txt
Approach 3:-
Use CI/CD Piplelines to Build and Deploy your Web app via Azure DevOps, Github Actions, Bitbucket etc.
Azure DevOps:-
You can send your python code to Azure DevOps Repos and run the below pipeline to Build your Python app and Deploy it in Web app. As soon as new changes are added in your repository your web app deployment pipeline will trigger automatically reducing your time like below:-
DevOps Pipeline:-
trigger:
- main
variables:
azureServiceConnectionId: 'xxxxx035'
webAppName: 'webapp-name'
vmImageName: 'ubuntu-latest'
environmentName: 'appname'
projectRoot: $(System.DefaultWorkingDirectory)
pythonVersion: '3.10'
stages:
- stage: Build
displayName: Build stage
jobs:
- job: BuildJob
pool:
vmImage: $(vmImageName)
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
displayName: 'Use Python $(pythonVersion)'
- script: |
python -m venv antenv
source antenv/bin/activate
python -m pip install --upgrade pip
pip install setup
pip install -r requirements.txt
workingDirectory: $(projectRoot)
displayName: "Install requirements"
- task: ArchiveFiles@2
displayName: 'Archive files'
inputs:
rootFolderOrFile: '$(projectRoot)'
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
replaceExistingArchive: true
- upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip
displayName: 'Upload package'
artifact: drop
- stage: Deploy
displayName: 'Deploy Web App'
dependsOn: Build
condition: succeeded()
jobs:
- deployment: DeploymentJob
pool:
vmImage: $(vmImageName)
environment: $(environmentName)
strategy:
runOnce:
deploy:
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(pythonVersion)'
displayName: 'Use Python version'
- task: AzureWebApp@1
displayName: 'Deploy Azure Web App : siliconwebapp098'
inputs:
azureSubscription: $(azureServiceConnectionId)
appName: $(webAppName)
package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip
Github Actions:-
You can also Deploy your Python app via Github Actions workflow by connecting your Web app to your Github and saving the workflow to run like below:-
After clicking on Save your Github actions deployment will start and your source code from Github repository will be deployed in the web app.
The fastest way among these all is Zip Deployment, You can also add zip deployment command in your Github action workflow. In this SO thread answer, I have Deployed .net app but with zip deploy command via Github actions, You can use the same method for python Django web app.
Approach 4:-
Dockerize your Django app and Deploy it in Azure web app, As deploying Containerized application is faster and helps in isolating application components.