Azure web app deployment modes
Azure Web Apps is probably the most commonly used PaaS product in azure.
With direct support for .Net, Java, NodeJS and PHP they offer a great term of flexibility in terms of runtime environments.
The same can be said about the various modes of deployment and this post aims to give a brief overview and description of each.
This is the newest deployment mode available. Instead of deploying individual files to the webserver you upload a zip file to blob storage, then you set the environment variable
RUN_FROM_PACKAGE to the SAS token of the zip file (or really any public url, so you can even host your web app zip in S3 buckets if you wish).
The webapp will then download the zip file and directly mount it as the
site\wwwroot folder on startup.
This has a few interesting behaviours:
- The app deployment is atomic (convenient when you use traffic manager)
- There will be no lingering files from any previous deployments
- The wwwroot directory becomes read only
- this forces developers to use the CD pipeline to make changes instead of “manually fixing production” (and then forgetting to backport these changes to master)
- Startup performance is actually improved in many scenarios
- Deployment is much faster as only a single zip file must be uploaded instead of 100+ tiny files
How it works:
- Once the environment variable is set to the url of your app package it is downloaded onto the web app on next start
- The zip file is directly mounted to the wwwroot folder in read-only mode (via a special driver) and the webapp restarts as soon as it detects the change
For applications with many files to load on startup (such as nodejs with its giant
node_modules folder and js files) this actually improves performance, because disk I/O now reads from a single continuous filestream (the zip file) instead of many tiny files as would usually be the case.
The Azure team has created a virtualized driver that enables this behaviour on the fly. Your webapp won’t know the difference between reading from disk or from the (locally cached) zip file.
- wwwroot is readonly (in my opinion not really a downside, but you need to be aware that you can no longer write to the directory to e.g. “quickly edit config values”)
- Your web app zip file must be publicly available in the internet (at the very least available to the IP range of your web app)
- technically anyone with that URL can download your app code and while it is best practice to keep secrets out of deployments (use keyvaults instead) I still consider this to be the weakest deployment model from a security perspective
- The SAS token URL must be longlived (app restart & IIS recycles mean the zip file has to be downloaded again each time as the zip is only cached in memory)
- App restarts are delayed by the time it takes to download the file (http call from app service to storage account) and while http calls inside a datacenter are fast, a 100MB download will still make a noticable difference in startup time
- App restart/IIS recycle now depend on the externally published zip file - if it no longer exists (or the storage location is unreachable), your app won’t start
The last point is the biggest disadvantage: Your webapp now depends on this URL not expiring and the zip file existing in storage account as any restart will trigger a redownload.
Along with the public SAS token URL I do not personally like this deployment model and actually prefer the next one:
This deployment mode is similar to the one above except:
- The zip file is uploaded to the webapp itself during deployment (to a special filter)
- The zip file is mounted from local storage and doesn’t need to be downloaded on every restart (faster!)
- no dependency on an external service (such as Azure Blob Storage)
- no need for a public url of your application code
- no risk of the SAS token expiring and causing problems on app restarts
It is much more secure and still has all the benefits. I actually use this mode for all my personal websites.
To use it, you can use Azure DevOps task
App Deploy (v4) and choose
Mode=RunFromPackage, it will default to uploading the zip to the webapp.
To manually get it running you would have to:
- set environment variable
- Upload the zip file to the
- A file
packagename.txtmust exist in the
D:\home\data\SitePackagesfolder and it must contain the name of the zip file to load (relative path next to the .txt file, no spaces allowed).
Once the content of the .txt file changes, the webapp restart is automatically triggered, but again Azure DevOps will hide all those details from you if you use the App Deploy task, making this deployment method very convenient.
The endresult is the same as mentioned above:
- Faster deployement (single zip file)
- Faster startup due to virtualized “zip as a folder”
- Readonly wwwroot folder
The oldest and most well known deployment model. While this is still supported I won’t cover it in details as I don’t think it should be used anymore (I personally disabled it on all web apps as I consider it a security risk).
If you are manually uploading files via FTP(S) in 2019 then you are doing DevOps wrong!
Publish from Visual Studio
This is technically a deployment mode (although it uses webdeploy under the hood) but I still want to mention it seperately.
It certainly is great if you are deploying a hobby website from your local machine or are doing a 5 minute webcast, but I personally avoid it for multiple reasons:
Firstly: I don’t consider it a good practice for deployment.
For testing you can just start the webapp locally and when a deployment is needed I recommend you use a regular pipeline: git commit -> build with tests -> release that deploys your web app in a consistent manner.
This makes everything reproducable and I’ve had my fair share of “minor changes” that I just wanted to publish that happened to be faulty because manual steps are always prone to error.
If you are not using an automated deployment pipeline in 2019 I would serious suggest you reconsider your deployment methods because the tooling has gotten really good and all the parts are free (Azure DevOps offers 1800 free build minutes per month for private projects and unlimited build minutes for open source projects).
This will upload all files individually and you can choose to use
App_offline.htm in the meantime.
You can also optionally choose to delete all other files to achieve consistent deployments.
Generally this mode will often have problems with files being locked (hence the
MS_PUBLISH_RENAME_LOCKED_FILES workaround that works some of the time).
Choose this method if you need to have a writeable wwwroot directory.
Same as WebDeploy but uploads & extracts a zip. This improves the upload speed (if you have many tiny files to be uploaded) but may still run into the “file locked” issue mentioned above as it will just extract the zip file content to wwwroot folder.
If you want to use containers but don’t want to manage Kubernetes (or don’t have the need for full orchestration) this can allow you to use the PaaS infrastructure of App Services with your own containers.
If you already plan to migrate to containers it may be a decent first step as you get the scaling abilities of the app service plan with the consistent developer experience of containers.
This isn’t technically a deployment mode in the same sense as all the other ones but I still wanted to mention it because I think it should be used with every deployment.
If you deploy directly to your webapp you kill the existing process and the new process will take a bit to start up - or worse: fail to start.
In this case you are left with a nonfunctional website and even on a successful deploy there will be a window of up to 30 seconds where the web app won’t respond to incoming requests due to startup taking some time.
Slots work around this issue by allowing you to deploy your app to a secondary web app (the slot) which has a seperate url:
yourwebapp-<slot name>.azurewebsites.net. You can pick your own slot name and you can even create up to 5 (Standard tier) or 20 (Premium tier) slots per web app, although the most common use case is just one slot:
The slot behaves like a regular web app and can run through warmup to ensure it is operational.
Afterwards swapping the slot with the production instance is a zero-downtime operation as a DNS swap will happen in the background. All existing requests to the old instance will be honored and processed until the new instance is fully swapped into place.
Many companies will run health checks or light workloads against these slots to ensure they are working correctly (in combination with multiple environments of course) before swapping the slots into production.
If you want to, you can even delegate a certain percentage of your users to the slot as scaling between 0-100% traffic can be done freely between the production app and all slots.
Lots of different options with various tradeoffs.
From personal experience staging slots in combination with RunFromPackage (Mode=1) offers the most convenient solution and it’s also what I use in all my private projects as the (small) overhead in complexity is well worth the benefits it provides.