Running/Installing a Windows Service in an Azure Web Role

So now that I have got the Windows Phone Update process out of my system it’s time to get back to work and before I start my day I felt the need to write another blog post.

Todays blog post is about running a standard Windows Service from inside a Windows Azure Web Role.

Why would I want to do this? Well a couple of reasons. First I don’t really want pay for yet another instance to run a pretty simple service. Secondly the service needs to be running on each of my Web Roles as I am uploading files via a HTTP handler which are then processed and moved to blob storage. If I am running multiple instances of a Web Role (which we will be for scalability) I cannot determine which instance the file will be uploaded to and hence the service needs to run on each Web Role.

Read on for more =>


Setting the Scene

Ok, so first a little background. I am working on a site that is geared towards musicians for a large recording studio down here in Sydney. The site will be used to promote artists as well as browse the back catalogue of music (there is a visual artist component as well), listen to music samples online and purchase and download product.

From the CMS the client needs to be able to upload full mp3 files and have these stored in a secure location on blob storage. However, a 30 second sample of each track also needs to be created and stored in a public location for public consumption. To create the sample I am using ffmpeg and that is run from within a Windows Service.

Hopefully the diagram below will clarify (or not) the interaction between the public facing portion of the site and the CMS:

Interaction with Azure

The process works as follows:

  1. Select file(s) to upload
  2. Push to server (could be any of the Web Roles)
  3. Windows Service detects new files
  4. For each file found run ffmpeg, create 30 second sample and upload to blob storage.
  5. Delete sample from local file system
  6. Upload full track to secure blob storage
  7. Delete full track from local file system

The service just runs in a loop and when no work is left to do sleeps for 5 seconds.

Implementation

Ok so hopefully you understand what I am doing here and the issues that have to be overcome when working with multiple instances of a Web Role that are sitting behind the Azure load balancer.

So to get the service installed requires using a Startup Task that can be defined in the ServiceDefinition.csdef file. I have shown the whole file for completeness below:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="Hwy125Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="Hwy125" vmsize="Small">
    <Startup>
      <!--<Task commandLine="startup\enable-windows-update.cmd" executionContext="elevated" taskType="background" />
      <Task commandLine="startup\install-php.cmd" executionContext="elevated" taskType="background" />-->
      <Task commandLine="startup\install-service.cmd" executionContext="elevated" taskType="background" />
      <Task commandLine="startup\disableTimeout.cmd" executionContext="elevated" />
    </Startup>
    <Sites>
      <Site name="Web">
        <Bindings>
          <Binding name="Endpoint1" endpointName="Endpoint1" />
        </Bindings>
      </Site>
    </Sites>
    <Endpoints>
      <InputEndpoint name="Endpoint1" protocol="http" port="80" />
    </Endpoints>
    <Imports>
      <Import moduleName="Diagnostics" />
      <Import moduleName="RemoteAccess" />
      <Import moduleName="RemoteForwarder" />
    </Imports>
  </WebRole>
</ServiceDefinition>

The key line in that file is the install-service.cmd Task which runs the batch file shown below:

@echo off
ECHO "Starting Service Installation" >> log.txt

..\..\Assets\Hwy125ServicesInstaller.exe

ECHO "Completed Service Installation" >> log.txt

And what that does is in turn is run a small console application that does the work of installing my service.

With the Windows Service you need to make sure it runs under an account with all the required permissions and also that it is set to Auto Start should the Web Role be rebooted. In addition once the service is installed you want it to actually start straight away. To do this you just need to add an extra couple of lines to your Service Project Installer shown below:

private void Hwy125ServiceInstaller_AfterInstall(object sender, InstallEventArgs e)
{
    ServiceController sc = new ServiceController("Hwy125Services");
    sc.Start();
}

Now back to the console application. I chose to do my install this way as my batch file and powershell skills are woeful and this was a less painful root for me to take. In all seriousness I need to do some reading on powershell Smile

The Console application gets the path to the .NET Runtime for me so I can execute InstallUtil to install my Service and then just fires off a Process to do the install. Code shown below:

private static void DoInstall()
{
    string executable = string.Format("{0}InstallUtil.exe", System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory());
    string arguments = "Hwy125Services.exe";
    using (Process installer = new Process())
    {
        try
        {
            Console.WriteLine(string.Format("Starting install using {0}", executable));
            installer.StartInfo.UseShellExecute = false;
            installer.StartInfo.RedirectStandardOutput = true;
            installer.StartInfo.FileName = executable;
            installer.StartInfo.Arguments = arguments;
            installer.StartInfo.CreateNoWindow = true;
            installer.Start();
            string output = installer.StandardOutput.ReadToEnd();
            installer.WaitForExit();
            Console.Write(output);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }

    Console.WriteLine("Install Complete");
}

And that is pretty much it. Now when I deploy my WebRole to Azure it also installs my Windows Service for me, starts it running and off we go.

But hold on, there are few more things you will need to know that are covered in the Gotchas section below.

Gotchas

So we now have our Windows Service installed. However, the Windows Service is installed and run from a different location on the Azure Instances file system than the Web Site hosted by the WebRole. When we upload files they are going to a path relative to the Web Site so we need to point our Windows service to that location.

The Web Site will run from here “E:\sitesroot\0\”

And the Windows Service will run from here “E:\appsroot\0\”

For now, and this is a cardinal sin, I have a config setting for this path to point my Service back to the Web Site. This is better than a hard coded value but still not ideal, this should be dynamic. Once I suss that out I will post an update.

In addition the way that Azure sets up it’s file system it only gives you 1Gb of disk space on the drive that the Web Site and Role are installed too. Now this would be to encourage you to move stuff to Blob storage which is a good thing but seeing as the VM that the WebRole runs on for a small instance has 250Gb of (wasted) disk space I think Microsoft could have been a little more generous with the amount of disk space on that drive.

The consequence of this is that for any temporary files that are uploaded you want to make sure that you clean them up (ie Delete them) as soon as they are processed and uploaded to Blob Storage.

Conclusion

I am still getting comfortable with Windows Azure but it is all falling in to place. What I have done may not be the best practice and eventually I may move to a WorkerRole.

However, doing a FileUpload from a web site is pretty ingrained in the Webosphere and this solution seems to work nicely for me.

Hope this post helps someone else trying to do the same thing.

Comments, criticisms and advise always welcome in the comments section below.

BondiGeek.

One thought on “Running/Installing a Windows Service in an Azure Web Role

  1. Daniel on

    Thanks for posting this….

    I have an also not supported thing I want to do, which is to run an object-oriented database – Eloquera which requires a web service. This will allow me to do that! ;-)

    Hopefully things should be fine as long as the actual files are stored on an Azure Drive.

    – Daniel

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>