Volume 33 Number 10
Azure - Deploying to Azure App Service and Azure Functions
By Stuart Leeks | October 2018
Microsoft Azure App Service is a Platform-as-a-Service solution that lets you build and deploy Web, mobile and API apps running on any platform. It’s also the platform for Azure Functions, which provides an event-driven, serverless compute experience. In this article I’ll mostly refer to Azure App Service, but the deployment techniques discussed are equally applicable to Azure Functions.
When it comes to deploying your application, App Service offers a wide range of options from which to choose. In this article I’ll take a quick look at the different deployment options and then spend a bit of time delving into the more recent addition of zipdeploy, as well as the Run From Package approach (which is in Preview at the time of this writing).
A Tour of App Service Deployment Options
I’ve been using App Service for years (since before it was called App Service), but while writing this article I discovered a deployment option I didn’t know about: Cloud Sync! So, let’s start with that before taking a look at other, more traditionally developer-focused approaches that are part of App Service.
Cloud Sync With Cloud Sync you can connect App Service to your OneDrive or Dropbox account and it will create a folder for your site. You can edit your site content locally and have the OneDrive or Dropbox clients sync to cloud storage. When you want to update your site from cloud storage, you can click the Sync button in the portal. This flow is probably a good fit if you’re working on a site with static content and want a way to update with a single click. A full walk-through of Cloud Sync for deployment can be found at bit.ly/2w7C1VK.
Good Ol’ FTP If you’ve been writing Web apps for a while, there’s a fair chance that at some point you’ve used the venerable File Transfer Protocol (FTP) as a way to (ahem) transfer files to a Web server. With App Service, you get an FTP endpoint for your site, which allows you to use your favorite FTP client to connect to and synchronize your content with the site/wwwroot directory. (The endpoint is enabled by default, but you can disable if you wish.)
More details of using FTP for deployments (including using or enforcing FTP over SSL) can be found at bit.ly/2Bw9NtV.
zipdeploy One of the really appealing aspects of zipdeploy is its simplicity. You’ve probably been creating .zip files for years, and it’s just as simple to create .zips across both platforms and continuous integration (CI)/continuous deployment (CD) services such as Visual Studio Team Services (VSTS):
# Bash zip -r <file-name>.zip . # PowerShell Compress-Archive -Path * -DestinationPath <file-name>.zip
Once you have your .zip file you can deploy it in multiple ways. For example, you can navigate to the Project Kudu site, “https://<app_name>.scm.azurewebsites.net/ZipDeploy,” and drag the .zip file, or you can use the Azure CLI:
az webapp deployment source config-zip --resource-group myResourceGroup --name <app_name> --src clouddrive/<filename>.zip
Or you can POST an HTTP request with the .zip file to the REST API:
curl -X POST -u <deployment_user> --data-binary @"<zip_file_path>" https://<app_name>.scm.azurewebsites.net/api/zipdeploy
All of these methods (including how to invoke zipdeploy from PowerShell) are covered in more detail at bit.ly/2tQwaGD.
When you deploy via a .zip file, you’re engaging Kudu’s deployment mechanism, which will add the files in the .zip to the site/wwwroot folder of your site in App Service. For more information on Kudu, see “Project Kudu” within this article.
It’s also possible to have Kudu execute a deployment script. To do this, you create a .deployment file in the root of the .zip that tells Kudu what script to run. This script can perform an npm install, a dotnet build or whatever steps you require as part of the deployment.
If you’re working with a C# project and zipping the source, you can set the SCM_DO_BUILD_DURING_DEPLOYMENT environment variable (via App Settings) and Kudu will automatically build the .sln or .csproj file, providing it’s in the root of the folder.
Full details of the .deployment approach can be found at bit.ly/2MosYe4.
Local Git Deployment Git has become a widely adopted tool among developers. One of the features of Git as a version control system is its distributed nature. This means that when you’re working with a local repository on your developer machine, the way to synchronize changes to a remote server (such as VSTS, GitHub or BitBucket) is by adding a Git remote and pushing your changes to it.
When you enable the Local Git Deployment option in App Service, a Git repository is created for you. You can configure this as remote from your local Git repository and then trigger a deployment via the “git push” command.
As with zipdeployment, you can execute build steps during deployment. With Local Git Deployment, Kudu will automatically look for .sln/.csproj files in the root and build them. For other languages (or for more control), you can use the .deployment file to control the build process.
If you have richer requirements for build, you can use VSTS to perform the build, as shown in Figure 1. This provides access to the build features built into VSTS, as well as custom tasks available in the marketplace.
Figure 1 Choosing Between Kudu and Visual Studio Team Services (VSTS) to Execute the Build Stage
More details on using Local Git Deployment can be found at bit.ly/2BljdIx.
Continuous Deployment The Continuous Deployment option in App Service is similar to the Local Git deployment. The difference is that instead of the code being pushed to a Git repository that Kudu hosts for you, you push code to your Git repository in VSTS, GitHub or BitBucket. When you configure this option, App Service sets up webhooks to be notified when you push changes to your repository, and this is the trigger for a deployment.
As with Local Git Deployment, you can choose either Kudu builds or VSTS build pipelines.
More details on Continuous Deployment can be found at bit.ly/2vOS23k.
The options I discussed are those that are directly integrated into App Service. Providers of build and CI/CD platforms may offer their own tasks to enable you to deploy to App Service. Such tasks all build on top of one of the App Service mechanisms described, so any discussion around these App Service methods will apply to those tasks. For example, AppVeyor has a task for deploying to App Service using zipdeployment (bit.ly/2OK535j), so the considerations for zipdeployment will apply to the AppVeyor task.
When you deploy your application to App Service, you’re modifying the site/wwwroot folder contents. These are the files that are used to serve your site, so there are some challenges you may face. If your site is in use, you may find that some of the files are locked when the deployment attempts to copy new files over. Additionally, because the files aren’t all updated at exactly the same moment, it’s possible to get unpredictable behavior during deployment.
Using Deployment Slots (in Standard and Premium tiers of App Service) is a great way to overcome these challenges. When you create a deployment slot, you create a new site within your Web App that has its own URL and site/wwwroot content, but with a single operation you can swap your test site slot to become your production slot.
If you create a “test” slot, the flow for deployment might be:
- Deploy to your test slot
- Validate that the new version is functioning correctly
- Swap the test slot to your production slot
If you don’t want to perform the validation step in the middle, you can even configure the slot to Auto Swap to production once the deployment has finished.
More details on Deployment Slots can be found at bit.ly/2L4rsIt.
A New Alternative
It’s common to evolve your deployment as your project progresses, and the range of deployment options available to you in App Service gives you a lot of flexibility over the lifetime of your project. For example, using the .deployment file to specify the build/deployment steps to execute can be hugely productive, but as your project progresses you might want to move to an approach that splits the build process out in order to control it via VSTS or the like. As previously mentioned, all the deployment approaches discussed so far work by syncing content to the site/wwwroot folder of your App Service.
App Service (and Functions) have a new deployment method called Run From Package (in preview at the time of writing) that takes zipdeployment a step further. Unlike all the deployment options discussed so far (including zipdeploy), Run From Package doesn’t take the new content and sync it with the site/wwwroot folder. Instead, with Run From Package you provide a .zip file that’s mounted as a read-only file system at wwwroot.
Not every application will work with Run From Package; historically, the wwwroot folder has been writable (and ASP.NET Apps in particular are used to writing to the App_Data folder). Also, because the .zip file is mounted, you can’t use the .deployment file approach to run build/deployment steps; the .zip file needs to contain the final set of files required to run your site. This can actually be a benefit, though: Because there’s no step that syncs files or restores packages, you know exactly what files will be present for a given .zip file and there are no dependencies on npm or NuGet services. As long as you retain the .zip files, reverting back to a previous version of your site is as simple as pointing to the .zip file for that version.
Another key benefit is deployment consistency: Because the .zip file is mounted, there’s no syncing of files and the deployment step is atomic. This immediately avoids the concerns I discussed in the “Addressing Challenges” section.
For applications that require loading a large number of files, there can be an initial performance hit when a new host starts running your code (often referred to as a “cold start”); for example, with a Node.js application that has thousands of files in npm_modules. The cold start problem is particularly noticeable in Azure Functions on the dynamic billing plan because it scales aggressively to handle incoming demand, which causes new hosts to be introduced. There are existing ways to mitigate this cold start issue (such as funcpack or webpack), but the way that Run From Package is implemented solves this problem for you.
It’s clear that App Service enables a wide range of approaches for deploying your application. Across various projects I’ve worked on with Azure App Service and Azure Functions, I’ve tried quite a few of these approaches, often using more than one on an individual project as the project’s needs evolved over time. The addition of the Run From Package deployment method now provides a simple yet powerful new addition to those deployment choices. For anyone who wants immutable deployments with a robust rollback plan, Run From Package is well worth a look.
Kudu is a built-in component of App Service that combines behind-the-scenes functionality with features that are surfaced in the Kudu portal.
Behind the Scenes Earlier I mentioned how Kudu exposes an endpoint that you can invoke to trigger a zipdeployment. Management of deployments to your App Service is a behind-the-scenes feature of Kudu; for example, when you do a “git push” of your code to the App Service Git endpoint, it’s Kudu that takes that code and applies it to your App Service file system.
Kudu also enables the WebJobs capability of App Service (bit.ly/2OHHuKz); WebJobs provide the ability to have background tasks that execute in the same context as your Web App. These tasks can be manually triggered, run on a schedule, or can continuously run and retrieve messages from a queue and so forth.
Another behind-the-scenes feature that Kudu provides is Site Extensions. Site Extensions allow you to add extra functionality to your site and can be a combination of configuration changes to your site, a UI that’s exposed within the Kudu Portal and WebJobs. You can install Site Extensions from the gallery shown in the Kudu portal, and even write your own (bit.ly/2PioL9z).
Kudu Portal The Kudu portal is quite handy. If your App Service site URL is https://mysite.azurewebsites.net, you can access the Kudu portal at https://mysite.scm.azurewebsites.net (if you’re already authenticated with your Azure Portal account, you’ll be automatically signed in, otherwise you’ll be prompted to authenticate). You can also navigate to Kudu in the Azure Portal by clicking on the Advanced Tools item in the Development Tools section for your App Service or Function App.
After signing in you’ll be greeted by the Kudu portal’s light but functional UI. Common tasks are in the menu at the top, and links to the Kudu REST API are shown in the body of the homepage. While a full walk-through of Kudu is outside the scope of this article, I want to review a few of the cool features. (You’ll also find a lot of great content at the project’s Wiki at bit.ly/2KZoQM1.)
One of the features I’ve used a lot is the Debug Console. When you click on the menu item, you get a choice of command prompt (CMD) or PowerShell. This gives you a combination of folder/file browser interface and an embedded console (and a nice feature is that the current location of each is kept in sync as you navigate), and there’s also a simple file editor built in. I’ve found this to be an extremely useful tool, especially when building WebJobs and Site Extensions.
Another feature that has helped me out on many occasions is the Environment page. This is simply a way to see the App Settings, environment variables and so forth that are configured for your site, but it’s handy when you’re scripting Kudu-based deployments or even just for a sanity check on the configuration your site is seeing. And there’s plenty more in Kudu you’ll find useful, from listing processes to triggering a process minidump. If you haven’t yet taken a look at Kudu, it’s highly recommended.
Stuart Leeks is a Web and cloud geek who works at Microsoft as a software engineer helping customers and partners with their cloud adoption. You can find him on twitter @stuartleeks and blogging at blogs.msdn.microsoft.com/stuartleeks.
Thanks to the following Microsoft technical expert for reviewing this article: David Ebbo
David Ebbo is a development manager on the Azure App Service and Functions team. You can find him on Twitter: @davidebbo