Machine Key Generator
Very useful:
You add endpoints to a role in Visual Studio using this editor:
The Service Definition file will be automatically set to this:
<?xml version="1.0" encoding="utf-8"?> <ServiceDefinition name="WindowsAzureProject1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"> <WebRole name="WCFServiceWebRole1" vmsize="Small"> <Sites> <Site name="Web"> <Bindings> <Binding name="Endpoint1" endpointName="Endpoint1" /> <Binding name="Endpoint2" endpointName="Endpoint2" /> </Bindings> </Site> </Sites> <Endpoints> <InputEndpoint name="Endpoint1" protocol="http" port="80" /> <InputEndpoint name="Endpoint2" protocol="http" port="9000" /> </Endpoints> </WebRole> </ServiceDefinition>
Notice how both endpoints are bound to the website. This results in the website in IIS having bindings set up for both port numbers. However, you can remove the binding, the port will now be free for you to use for whatever you want. The interesting thing is that once un-bound, you can set the protocol to TCP.
<?xml version="1.0" encoding="utf-8"?> <ServiceDefinition name="WindowsAzureProject1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition"> <WebRole name="WCFServiceWebRole1" vmsize="Small"> <Sites> <Site name="Web"> <Bindings> <Binding name="Endpoint1" endpointName="Endpoint1" /> </Bindings> </Site> </Sites> <Endpoints> <InputEndpoint name="Endpoint1" protocol="http" port="80" /> <InputEndpoint name="Endpoint2" protocol="tcp" port="9000" /> </Endpoints> </WebRole> </ServiceDefinition>
This allows you to provision a second site (or application) on an alternative port number on the same box. You would just need to unpack your application and copy it somewhere using a startup task.
This is the command to create a new site in IIS:
appcmd add site /name: sitename /id:2 /physicalPath: %systemdrive%\inetpub\wwwroot /bindings:http/*:9000:MYAPP.cloudapp.net
It is now possible to backup and restore your SQL Azure database using the BACPAC format, from inside the Azure Management Portal. The portal allows you to select a database and export it to a blob. Restoring is a mater of choosing which blob should restore to which database. Backups do not have transactional consistency, so it may be best to back up a copy of your live database.
You can copy a database with this command:
CREATE DATABASE destination_database_name AS COPY OF [source_server_name.]source_database_name
I quite like the user interface of the Data Sync feature in Azure (currently CTP).
Very visual, and easy to see what’s going on.
As previously discussed, cloud applications should be written to be stateless, and use the standard Azure round-robin load balancer. However, some applications need a sticky session, in which case, one option you have is to roll your own software load balancer. However the Application Request Routing (a software load balancer) module of IIS presents you with another option. The advantages of using ARR are
The topology of your deployment will look something like this:
The ARR Role will have ARR and the Web Farm Framework installed, and expose a public facing endpoint. It will be responsible for routing traffic to the Web Role instances. The Web Role will have an internal endpoint, but is otherwise unchanged.
Download ARR and The Web Farm Framework IIS modules:
http://www.iis.net/download/ApplicationRequestRouting
http://www.iis.net/download/WebFarmFramework
Create a Web Role project in your solution, copy the MSI files you downloaded into the solution, and set them to ‘copy if newer’. This will ensure they are packaged up with your application.
Add an elevated startup task (see how here) which will install these components. Your Startup.cmd file should contain these lines:
webfarm_amd64_en-US.msi /quiet requestRouter_amd64_en-US.msi /quiet
In your service definition file, set your role to run elevated (see how here).
In your WebRole class (which inherits from RoleEntryPoint) add an override to the ‘Run’ method. The first thing to do is to update the binding configuration of the virtual directory to include the correct host name, and change the physical path of the default site to wwwroot, instead of the web pages packaged with your role. This can be done using the Microsoft.Web.Administration namespace:
using (ServerManager serverManager = new ServerManager()) { bool voteCommit = false; Configuration config = serverManager.GetApplicationHostConfiguration(); ConfigurationSection sitesSection = config.GetSection("system.applicationHost/sites"); ConfigurationElementCollection sitesCollection = sitesSection.GetCollection(); ConfigurationElement siteElement = sitesCollection[0]; ConfigurationElementCollection bindingsCollection = siteElement.GetCollection("bindings"); ConfigurationElement bindingElement = FindElement(bindingsCollection, "binding", "protocol", @"http"); if (bindingElement["bindingInformation"] as string != @"*:80:yourservice.cloudapp.net") { bindingElement["bindingInformation"] = @"*:80:yourservice.cloudapp.net"; } ConfigurationElementCollection siteCollection = siteElement.GetCollection(); ConfigurationElement applicationElement = FindElement(siteCollection, "application", "path", @"/"); ConfigurationElementCollection applicationCollection = applicationElement.GetCollection(); ConfigurationElement virtualDirectoryElement = FindElement(applicationCollection, "virtualDirectory", "path", @"/"); if (virtualDirectoryElement["physicalPath"] as string != @"%SystemDrive%\inetpub\wwwroot") { virtualDirectoryElement["physicalPath"] = @"%SystemDrive%\inetpub\wwwroot"; } serverManager.CommitChanges(); }
You then need to create a web farm, and add the redirection rule to point incoming requests to your farm. The web farm in this case is configured to use client affinity (sticky sessions).
using (ServerManager serverManager = new ServerManager()) { // create the server farm Configuration config = serverManager.GetApplicationHostConfiguration(); ConfigurationSection webFarmsSection = config.GetSection("webFarms"); ConfigurationElementCollection webFarmsCollection = webFarmsSection.GetCollection(); var el = FindElement(webFarmsCollection, "webFarm", "name", farmName); if (null != el) return; ConfigurationElement webFarmElement = webFarmsCollection.CreateElement("webFarm"); webFarmElement["name"] = farmName; webFarmsCollection.Add(webFarmElement); ConfigurationElement applicationRequestRoutingElement = webFarmElement.GetChildElement("applicationRequestRouting"); ConfigurationElement affinityElement = applicationRequestRoutingElement.GetChildElement("affinity"); affinityElement["useCookie"] = true; // create the rewrite rule Configuration config = serverManager.GetApplicationHostConfiguration(); ConfigurationSection globalRulesSection = config.GetSection("system.webServer/rewrite/globalRules"); ConfigurationElementCollection globalRulesCollection = globalRulesSection.GetCollection(); ConfigurationElement ruleElement = globalRulesCollection.CreateElement("rule"); ruleElement["name"] = serverFarm; ruleElement["patternSyntax"] = @"Wildcard"; ruleElement["stopProcessing"] = true; ConfigurationElement matchElement = ruleElement.GetChildElement("match"); matchElement["url"] = "*"; matchElement["ignoreCase"] = true; ConfigurationElement actionElement = ruleElement.GetChildElement("action"); actionElement["type"] = @"Rewrite"; actionElement["url"] = string.Concat(@"http://", serverFarm, @"/{R:0}"); globalRulesCollection.Add(ruleElement); serverManager.CommitChanges(); }
These tasks need only to be run once.
You should then set up a simple loop, which will inspect the instances running, and modify the farm according to changes in the topology. Adding a server is straight forward, and can be done with some code like this:
public static void AddServer(string farmName, string ipAddress, int portNumber) { using (ServerManager serverManager = new ServerManager()) { Configuration config = serverManager.GetApplicationHostConfiguration(); ConfigurationSection webFarmsSection = config.GetSection("webFarms"); ConfigurationElementCollection webFarmsCollection = webFarmsSection.GetCollection(); ConfigurationElement webFarmElement = FindElement(webFarmsCollection, "webFarm", "name", farmName); ConfigurationElementCollection webFarmCollection = webFarmElement.GetCollection(); var server = FindElement(webFarmCollection, "server", "address", ipAddress); if (null != server) { // server already exists return; } ConfigurationElement serverElement = webFarmCollection.CreateElement("server"); serverElement["address"] = ipAddress; ConfigurationElement applicationRequestRoutingElement = serverElement.GetChildElement("applicationRequestRouting"); applicationRequestRoutingElement["httpPort"] = portNumber; webFarmCollection.Add(serverElement); serverManager.CommitChanges(); } }
You would need additional code to for servers being, removed, and IP address changes.
Simple!
Anonymous,
Arvin,
Richard, and 1 other are discussing. Toggle Comments
Rich – the msi install process no longer appears to work. The web farm framework throws an alert stating that it needs to be installed via the Web Platform Installer. See http://things.smarx.com/#Install Application Request Routing for the script, but note that the line:
webpicmdline /accepteula /Products:ARRv2
…should read:
webpicmdline /accepteula /Products:ARR
…to install ARR 2.5
Interesting, I’ll check it out and update.
Cheers.
Grrrr – WordPress screwed my url – try:
http://tinyurl.com/c972j3c
There’s a way to install ARR without using Web platform Installer: http://blogs.iis.net/wonyoo/archive/2011/04/20/how-to-install-application-request-routing-arr-2-5-without-web-platform-installer-webpi.aspx. Also you may want to set your ApplicationPool Idle time to 0 so your applciation pool is always On.
Thanks Arvin, great tip.
No problem! One question for you, have you tried importing your rules from XML using ServerManager class? In AppCmd, I used this one to clear and Import:
appcmd.exe clear config -section:webFarms /commit:apphost
type rules.xml | appcmd.exe set config /in
sorry, the command to clear is appcmd.exe clear config -section:system.webServer/rewrite/globalRules
Hi Arvin, not something I have tried personally, but it looks useful – thanks for another useful tip!
You can set the date on an Azure instance, however, it quickly sets it back again. See this console output as an example:
E:\>date 12/10/2004 E:\>date /t Fri 12/10/2004 E:\>date /t Thu 10/13/2011
Even stopping the Windows Time service has no effect.
It looks like Azure always wants to keep up-to-date.
It surprised me to find that the ‘Run’ method on your implementation of ‘RoleEntryPoint’ gets called on Azure Web Roles, in just the same way as Worker Roles.
public class WebRole : RoleEntryPoint { public override void Run() { // Do something } }
This means that if you have currently have separate web roles and worker roles, you could combine these into a single role. The advantage with this approach is that load will then be spread evenly across all of your instances, and you make better use of the compute time you’ve purchased.
What is also interesting, is that run is called on a copy of your assembly in this folder:
e:\approot\bin
Whereas IIS is pointing to another copy of your application in this folder:
e:\siteroot\0
On Azure, the name of the default website changes according to the name and number of the instance. If you need to include the name of the site in any appcmd scripts, you can get it in a bat file like this:
%systemroot%\system32\inetsrv\appcmd list sites /text:site.name > temp.txt set /p sitename= < temp.txt echo %sitename%
Reply
You must be logged in to post a comment.