Running Multiple Websites in a Windows Azure Web Role
Prior to the release of the Windows Azure 1.3 SDK, Web roles hosted your application in IIS Hosted Web Core (HWC). While there were certainly ways to extend HWC, for the most part you were stuck with a single application bound to no more than a single HTTP or HTTPS endpoint. This model enabled only a minimal number of configurations, and prevented you from fully benefiting from the full capabilities of IIS. Here’s what the ServiceDefinition.csdef file looks like when using HWC:
<?xml version="1.0" encoding="utf-8"?> <ServiceDefinition name="WindowsAzureProject15" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"> <WebRole name="WebRole1"> <Endpoints> <InputEndpoint name="Endpoint1" protocol="http" port="80" /> </Endpoints> <Imports> <Import moduleName="Diagnostics" /> </Imports> </WebRole> </ServiceDefinition>
While you can still use this code today and run HWC in your Web role, you’ll miss you on a lot of great capabilities.
One of the capabilities that, surprisingly, people still doesn’t seem aware of in Windows Azure is the ability to run multiple websites in a Web Role. In fact, this capability is easy to add once we add seven additional lines of code.
<?xml version="1.0" encoding="utf-8"?> <ServiceDefinition name="WindowsAzureProject15" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"> <WebRole name="WebRole1"> <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" /> </Imports> </WebRole> </ServiceDefinition>
The difference here is found in lines #4 through #10. We now have a <Sites> element that includes a default <Site> named “Web”. When run (locally or in Windows Azure), this translates into an actual web site running in IIS.
You can see that our website is now running in IIS. And what’s nice is that the syntax used in the ServiceDefinition.csdef file is nearly identical to how this is traditionally accomplished in the system.applicationHost – there’s not much new we have to learn.
Run the Same Project in Two Sites in the Web Role
So, how can we take this further and run a second website in a Web role? It’s as simple as adding an additional <Site> to the <Sites> element.
Copy lines #5 through #9 above, and past them below line #9. You’re going to have to make a few updates:
- Change the site name. You can’t have multiple sites with the same name.
- You’ll have to specify a physical directory for the second site.
Note: the only reason we don’t have to specify a physical path for the first site is because “Web” is consider a special case, and Visual Studio knows that it’s referring to the path of the Web role project. You can confirm this by renaming “Web” to “Web1” – once you do this you’ll have to specify a physical path for the site.
- Create a host header entry for the binding element. The reason for this is because both sites are listening on port 80. This is the same behavior you’ll find in IIS – you cannot have more than one site listening on the same port without adding a host header.
Once you make these modifications, your <Sites> element will look something like this:
<Sites> <Site name="Web"> <Bindings> <Binding name="Endpoint1" endpointName="Endpoint1" /> </Bindings> </Site> <Site name="Web2" physicalDirectory="..WebRole1"> <Bindings> <Binding name="Endpoint1" endpointName="Endpoint1" hostHeader="www.litware.com" /> </Bindings> </Site> </Sites>
While this syntax is correct, www.litware.com will not locally resolve to our application. To make this work, we have to update our hosts file to assist in correctly resolving the address to 127.0.0.1 (the loopback address). Update the hosts file (C:WindowsSystem32driversetchosts) to include the following hostnames:
127.0.0.1 www.fabrikam.com 127.0.0.1 www.contoso.com 127.0.0.1 www.litware.com
After you’ve done this, you can confirm that they’re resolving correctly by pinging one of the addresses:
The reply should come from 127.0.0.1.
With this complete, hit F5 and run again. You’re now running the same project in two different websites – take a look at IIS:
In fact, if you take a look at the bindings on Web2, you’ll see that the host name has been set to www.litware.com.
And if you type http://www.litware.com:81/ (or whatever port the compute emulator is using), you’ll see the website display.
This is pretty cool, especially when want to use the same application for multiple websites – you can handle the requests in such a way that you either pull from different databases or even display different CSS files to make the website display differently.
Note: Some of you may have noticed above that IIS showed www.litware.com as running under port 5100. You may be wondering why it’s not on port 81, which is what we’re browsing to in IE. This is because it’s the compute emulator that’s listening on port 81, and then routing to port 5100. This is necessary so that incase we’re running more than one instance of our web application, both of which have the same host header, they’re not running on the same port. Nifty, eh?
Now we can even go further. Let’s take a look at how we can take a completely separate web application and run it in the same Web role.
Run Two Different Projects in the Web Role
Open up a new instance of Visual Studio 2010, and create a new Web Application. I’d recommend you make a few changes to make it easily recognizable (e.g. change “Welcome to ASP.NET!” to something else). Be sure to Build your solution (failure to do this will cause problems later). Copy the Project Folder for the project, and then return to the ServiceDefinition.csdef file in your Windows Azure project.
Create a new <Site> element where the physicalDirectory value is the location of the Project Folder you copied a moment ago. Also, update the site name and the hostHoder. It should look something like this:
<Site name="Web3" physicalDirectory="c:\Projects\WebApplication7\WebApplication7"> <Bindings> <Binding name="Endpoint1" endpointName="Endpoint1" hostHeader="www.fabrikam.com" /> </Bindings> </Site>
We’ve now create a third website, but this time – instead of pointing to the Web role project in our solution – we’ve pointed to a completely separate project that lives outside of our solution. Hit F5 and take a look at IIS:
We now have three sites running, the third which includes the files from “WebApplication7”. Try it out by updating the URL to http://www.fabrikam.com:81/ and you’ll see:
You can see that this is the not the Web role project itself, but the second project we created. The packaging tools know to include all the files needed from the physicalDirectory location and make them a part of the CSPKG that is ultimately given to the fabric controller.
This is pretty awesome, cause it demonstrates that you can run two completely different websites in the same Web role with a minimal amount of work.
Now, let’s take this even one step further … because we can.
Run a Virtual Application Within a Site
There may be times when you want to run a Virtual Application within your site (see Wenlong Dong’s post Virtual Application vs Virtual Directory for some interesting information). Given that the semantics of the <Sites> element is similar to system.applicationHost, it’s not hard to accomplish.
Create a brand new Web Application, and this time update <h2> with a different description (e.g. “This is my virtual application!”). Build the solution, then grab the project’s path. Return to your Windows Azure project and the ServiceDefinition.csdef file. We’re going to add this virtual application to the “Web3” site. We do this by adding a <VirtualApplication> element with name and physicalDirectory values. Here’s what it will look like:
<Site name="Web3" physicalDirectory="c:\Projects\WebApplication7\WebApplication7"> <VirtualApplication name="VirtualApp" physicalDirectory="c:\Projects\WebApplication8\WebApplication8" /> <Bindings> <Binding name="Endpoint1" endpointName="Endpoint1" hostHeader="www.fabrikam.com" /> </Bindings> </Site>
The <VirtualApplication> element is on line #3. Go ahead and hit F5, and take a look at IIS:
You can see that now, in Web3, our virtual application called VirtualApp is running. And if we browse to http://www.fabrikam.com:81/virtualapp/, we’ll see that it is indeed “WebApplication8” that’s running.
So there you have it!
You’ve now seen how to:
- Run multiple websites that run off of the same project in the same Web role.
- Run multiple websites that use different projects in the same Web role.
- Run a virtual application within a website that uses a different project in a Web role.
Of course, when you deploy this to Windows Azure, you’ll want to use your own domain name and set the host headers accordingly. For tips on how to do this – especially as as it relates to setting up CNAMEs for your domain – take a look at Steve Marx’s post on Custom Domain Names in Windows Azure.
[...] This post was mentioned on Twitter by Wade Wegner, way2cloud, David Makogon, Larry King, Matic Magister and others. Matic Magister said: Running Multiple Websites in a Windows Azure Web Role: Source: http://www.wadewegner.com — Thursday, February 17, 201… http://bit.ly/fkL2rq [...]
Love it! Thanks so much Azure team for enabling this. Shame you have to push the whole solution every time you want to push just one site.
If you’re frustrated about having to do this while developing, just enable WebDeploy on your sites – http://www.wadewegner.com/2010/12/using-web-deploy-with-windows-azure-for-rapid-development/.
Oh wow. Thanks Wade!
The caveats for that approach make it completely unacceptable. You WILL lose your changes.
Yep, that’s correct Shan – this is why I noted the caveats and only suggest this approach for development purposes.
Running Multiple Websites in a Windows Azure Web Role – Wade Wegner…
Thank you for submitting this cool story – Trackback from DotNetShoutout…
How would the PhysicalDirectory translate to a deployed azure site? Would we not need to specify a relative path so that azure could find the location?
Also just found out the hard way that this does not appear to play so well with PHP fast-cgi implementations.
Great post as well as a helpful Cloud Cover demo!
Let’s say you’re using multiple web roles in a single instance. One of those roles is for a CMS tool like Umbraco or Orchard for a marketing website and the other role is a web application (the app the marketing site says is so awesome).
Is there a way, either with multiple sites or with a site and a virtual directory, to isolate a specific web role when publishing updates? I’d like to be able to make updates to the web app without having to update (and potentially overwrite) the CMS web role instance.
What kind of control do you have over publishing individual roles when you have multiple web roles running in a single instance?
Thanks!
Gread post and video. Easy to follow
[...] [...]
[...] já é possível fazer o deployment de múltiplos sites em uma única máquina/WebRole. O post http://www.wadewegner.com/2011/02/running-multiple-websites-in-a-windows-azure-web-role/ mostra que basta algumas configurações e pronto – num único pacote colocamos e configuramos [...]
Has anyone experienced issues debugging the separate application with the virtual directory setup above? My application has no symbols loaded and I cannot get it to debug but the main application works fine?
eventually found if I went into iis and changed the virtual directory to use the main sites app pool it then worked. Not sure why as I had already manually attached all the open app pools anyway
This is wonderful, and exactly what we’ve been waiting for to migrate some of our sites to Azure, but…
“The packaging tools know to include all the files needed from the physicalDirectory location and make them a part of the CSPKG that is ultimately given to the fabric controller.”
The issue is that when using a element, the packaging tools include *all* files from the referenced directory – not just the “needed” files. If you publish an unencrypted service package, you can dig down into it and see a copy of the entire folder structure from the site, including the cs source files, csproj file, obj folder, etc. While IIS on Azure seems to block many of these file types, it is possible to access some of them. For example, after deployment I can type http://www.mysite.com/obj/Release/MyWebApp.csproj.FileListAbsolute.txt into the browser address bar to pull a list of every file in the bin and obj directories. Also, setting “Build Action = None, Copy to Output Directory = Do not copy” on a file essentially has no effect – it will be included in the package, deployed to Azure, and (depending on the file’s type) be accessible via the browser.
Beyond potential security implications, this issue also results in bloated service packages. To make matters worse, we happen to be using SVN for source control, which stores metadata and versioning information in hidden .svn directories within the project folder. If I manually publish each site to the file system, and then zip them all up together, I get a 40mb file. My cspkg file, on the other hand, is over 900mb due to all the .svn folders.
Is there some magic we can work with MSBuild here? Ideally have it do a local publish of each referenced site, and then include the output from that in the cspkg file, instead of copying the source directories in their entirety. This would also have the added bonus of ensuring that each referenced site has been built, which is a minor inconvenience with the current setup. Unfortunately I’m clueless when it comes to MSBuild, so I would have no idea how to accomplish this…
Hi Joe I find a solution to avoid the pubblication on azure of the whole project files.
The solution is very simple: before to publish the azure project, publish each web project localy (using file system method)
For example if you have 2 web project in the solution, publish each one localy
- c:publishApp1
- c:publishApp2
In the ServiceDefinition.csdef each site has a “physicalDirectory” that point to the publish directory.
For example:
Site name=”Web1″ physicalDirectory=”c:publishApp1″
Site name=”Web2″ physicalDirectory=”c:publishApp2″
Now publis the azure project.
This post is getting a bit long in the tooth, but I did find a way to automate this, so I thought I’d share it for anyone who happens to google their way here. Doing a manual file system publish of each site before creating the Azure package (as Bugeo suggests) can quickly become unmanageable when running a lot of sites in a single role. This is especially true if you’re working in a team environment where you might not be the only person making updates to the websites.
These instructions assume:
1) The Azure project and the website projects are in the same solution
2) The Azure project is named “MyAzureProject”
3) The website projects live alongside the Azure project in the file system hierarchy
For example:
MySolution
|- MyAzureProject
|- Website1
|- …
|- WebsiteN
1) For each website included in the role, right-click the project and choose Properties. On the Build Events page, set the following values.
Pre-Build Command Line:
rmdir “$(ProjectDir)..\MyAzureProject\Sites\$(ProjectName)” /S /Q
Post-Build Command Line:
%WinDir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe “$(ProjectPath)” /T:PipelinePreDeployCopyAllFilesToOneFolder /P:Configuration=$(ConfigurationName);PreBuildEvent=”";PostBuildEvent=”";PackageAsSingleFile=false;_PackageTempDir=”$(ProjectDir)..\MyAzureProject\Sites\$(ProjectName)”
The post-build command will do a local file system publish of the website into the MyAzureProject\Sites\WebsiteProjectName directory each time you build. The pre-build command ensures that you don’t accidentally have stale outputs should the website project not build correctly.
2) Right-click the Azure project and choose Project Dependencies. Make sure all of the website projects are selected. This will ensure that the website projects are always built (and published locally) before creating the Azure deployment package.
3) Edit the ServiceDefinition.csdef file as Wade describes in the section “Run Two Different Projects in the Web Role.” The value for each physicalDirectory attribute should be “Sites\WebsiteProjectName” – which is the local publish destination we specified for the website project in step 1.
Now when you publish your Azure package from MyAzureProject, all the included sites will auto-magically build and publish themselves, and all necessary files (and nothing more) will be included in the package. Additionally, if any included website fails to build, then the package creation will also fail (rather than silently “succeeding” and creating a package with missing or stale outputs).
Sorry, the third line should say: when using a <Site physicalDirectory=”…”> element…
[...] If your web project that is associated with the web role, contains virtual directories (aka virtual applications in IIS 7.x) you will need to add special configuration to the cloud project’s ServiceDefinition.csdef, here is a great post “Running Multiple Websites in a Windows Azure Web Role” [...]
[...] As Neil mentioned, this is also interesting when you are thinking “low end”. It allows you to run multiple web sites from one Web Role http://www.wadewegner.com/2011/02/running-multiple-websites-in-a-windows-azure-web-role/  [...]
I completely agree with you.
[...] http://www.wadewegner.com/2011/02/running-multiple-websites-in-a-windows-azure-web-role/ [...]
[...] http://www.wadewegner.com/2011/02/running-multiple-websites-in-a-windows-azure-web-role/ [...]
In visual studio I need to have all the website I want in the web role in just one solution? Hard to work with this. Thanks.
[...] I am using Web.Config transformations to deploy my application to Azure. I also have 2 sites in my service, a public website, and private WCF site endpoint. I am deploying multiple sites to a single role. [...]
[...] Running Multiple Websites in a Windows Azure Web Role: This isn’t a recent article (Feb. 2011), but it’s one that helped in answering questions I was asked this week. [...]
Its nice one. Actually I am working in 1 Winzaure cloud project. I need your assistance guidance in it.
First question, I configured all necessary property for symbolic link at cloud. Still its not working well. I’m using PHP 5.3.6 Version. Can u guide me. how I can enable symbolic link.
Second question . When we upload/create cloud. We get administrator or guest environment.
I tested my code on Window Server 2008. Its working well there but not in cloud.
Thanks
Mark