A former twenty something in technology

Advanced configurations for Node.js on Azure WebApps

nodeIn my previous most I explained how to get started running a node.js application with Azure WebApps. In this post I will note some more advanced features that I would recommend you consider for your project.
  • Routing static files
  • Multiple Instances
  • Slow Initial Render After Deployment (or timeout)
  • Always On & Application Initialization
  • Kudu notifications & Git SHA

Routing Static Files

IIS is handling the requests for your application initially and then proxying requests into node.exe through a named pipe port. Now while node.js is perfectly capable of handling serving up static resources like images, css, html, etc. why burden it. Using a simple IIS rule in your web.config you can just have IIS serve the file directly by passing node.
<rule name="StaticContent">
<action type="Rewrite" url="client{REQUEST_URI}"/>

Multiple Instances

IISNode is capable of spinning up multiple node processes and assigning them each a unique named pipe port that it will round robin serve requests too. Now while node by default is already async and makes good use processing during IO operations. You may still have some CPU intensive sections of your app and node is basically single threaded. Multiple instances of your application will easily allow it to scale and will allow it to tolerate a few application faults without taking everyone down. You can set the nodeProcessCountPerApplication value to acheive multiple node.js instances. These settings can be configured in the web.config but I recommend setting them with the Azure WebApps Application Settings section.

Slow Initial Render After Deployment

Unfortunately Azure WebApps disk IO performance is not wonderful. So for a real world application that contains thousands of node_modules it can take some time for it to read the entire hierarchy into memory before it can start serving HTTP requests. In order for your first request to not timeout in these cases you can extend the namedPipeConnectionRetryDelay timeout (in milliseconds) to something longer than it takes for your application to boot. This will ensure that the first request will wait to the actual response being proxied out to node responds.

Always On & Application Initialization

During a deployment all your node processes will shut down and won't initialize until the next request comes in. Azure WebApps have a feature called Always On which for a Asp.net application will ensure the application pool is kept running and won't spin down when idle -- aka no requests coming in. -- However that is only part of the equation. For IISNode we need to leverage Application Initialization in order to handle a primer request to spin up the node process as well. Beware of SSL. It's important to note that Application Initialization is unable to make HTTPS requests so if you redirect all HTTP requests to HTTPS then you will need to configure at least a specific route that is allowed to handle HTTP requests in order for Application Initialization to work.
<applicationInitialization skipManagedModules="true" doAppInitAfterRestart="true">
<-- Any route that is HTTP accessible -->
<add initializationPage="/warmup" />

Kudu notifications & Git SHA

If you decided to use continuous integration when you push to your repository. Then you probably experienced the mystery if your application deployed successfully or not. Using the Kudu interface you can go to tools > Web hooks. Using a service like Zapier you can configure a service endpoint to receive notifications when deployment is done, successful or failure. Then do what you want with that information. In my case I post it to a slack channel With or without notifications it can often be useful to know exact SHA your application is running. This information is available from Azure WebApps Kudu debug console under the path D:\home\site\deployments\active  A simple file read script will do the trick.
var fs = require('fs');

var filePath = 'D:\home\site\deployments\active');
var version_sha;
fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){
if (!err){
version_sha = data.toString();

No comments yet.

Leave a Reply