Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Windows Azure, PHP, ...

About the author

Maarten Balliauw is currently employed as a Technical Evangelist at JetBrains. His interests are mainly web applications developed in ASP.NET (C#) or PHP and the Windows Azure cloud platform.
More about me More about me
Send mail E-mail me


ASP.NET MVC Quickly Pro NuGet Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn
Maarten Balliauw - MVP - Most Valuable Professional
Maarten Balliauw - ASPInsider

Search

Archive

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright Maarten Balliauw 2013


Windows Azure and scaling: how? (.NET)

One of the key ideas behind cloud computing is the concept of scaling.Talking to customers and cloud enthusiasts, many people seem to be unaware about the fact that there is great opportunity in scaling, even for small applications. In this blog post series, I will talk about the following:

Creating and uploading a management certificate

In order to be able to programmatically (and thus possibly automated) scale your Windows Azure service, one prerequisite exists: a management certificate should be created and uploaded to Windows Azure through the management portal at http://windows.azure.com. Creating a certificate is easy: follow the instructions listed on MSDN. It’s as easy as opening a Visual Studio command prompt and issuing the following command:

1 makecert -sky exchange -r -n "CN=<CertificateName>" -pe -a sha1 -len 2048 -ss My "<CertificateName>.cer"

Too anxious to try this out? Download my certificate files (management.pfx (4.05 kb) and management.cer (1.18 kb)) and feel free to use it (password: phpazure). Beware that it’s not safe to use in production as I just shared this with the world (and you may be sharing your Windows Azure subscription with the world :-)).

Uploading the certificate through the management portal can be done under Hosted Services > Management Certificates.

Management Certificate Windows Azure

Building a small command-line scaling tool

In order to be able to scale automatically, let’s build a small command-line tool. The idea is that you will be able to run the following command on a console to scale to 4 instances:

1 AutoScale.exe "management.cer" "subscription-id0" "service-name" "role-name" "production" 4

Or down to 2 instances:.

1 AutoScale.exe "management.cer" "subscription-id0" "service-name" "role-name" "production" 2

Now let’s get started. First of all, we’ll be needing the Windows Azure service management client API SDK. Since there is no official SDK, you can download a sample at http://archive.msdn.microsoft.com/azurecmdlets. Open the solution, compile it and head for the /bin folder: we’re interested in Microsoft.Samples.WindowsAzure.ServiceManagement.dll.

Next, create a new Console Application in Visual Studio and add a reference to the above assembly. The code for Program.cs will start with the following:

1 class Program 2 { 3 private const string ServiceEndpoint = "https://management.core.windows.net"; 4 5 private static Binding WebHttpBinding() 6 { 7 var binding = new WebHttpBinding(WebHttpSecurityMode.Transport); 8 binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate; 9 binding.ReaderQuotas.MaxStringContentLength = 67108864; 10 11 return binding; 12 } 13 14 static void Main(string[] args) 15 { 16 } 17 }

This constant and WebHttpBinding() method will be used by the Service Management client to connect to your Windows Azure subscription’s management API endpoint. The WebHttpBinding() creates a new WCF binding that is configured to use a certificate as the client credential. Just the way Windows Azure likes it.

I’ll skip the command-line parameter parsing. Next interesting thing is the location where a new management client is created:

1 var managementClient = Microsoft.Samples.WindowsAzure.ServiceManagement.ServiceManagementHelper.CreateServiceManagementChannel( 2 WebHttpBinding(), new Uri(ServiceEndpoint), new X509Certificate2(certificateFile));

Afterwards, the deployment details are retrieved. The deployment’s configuration is in there (base64-encoded), so the only thing to do is read that into an XDocument, update the number of instances and store it back:

1 var deployment = managementClient.GetDeploymentBySlot(subscriptionId, serviceName, slot); 2 string configurationXml = ServiceManagementHelper.DecodeFromBase64String(deployment.Configuration); 3 4 var serviceConfiguration = XDocument.Parse(configurationXml); 5 6 serviceConfiguration 7 .Descendants() 8 .Single(d => d.Name.LocalName == "Role" && d.Attributes().Single(a => a.Name.LocalName == "name").Value == roleName) 9 .Elements() 10 .Single(e => e.Name.LocalName == "Instances") 11 .Attributes() 12 .Single(a => a.Name.LocalName == "count").Value = instanceCount; 13 14 var changeConfigurationInput = new ChangeConfigurationInput(); 15 changeConfigurationInput.Configuration = ServiceManagementHelper.EncodeToBase64String(serviceConfiguration.ToString(SaveOptions.DisableFormatting)); 16 17 managementClient.ChangeConfigurationBySlot(subscriptionId, serviceName, slot, changeConfigurationInput);

Here’s the complete Program.cs code:

1 using System; 2 using System.Linq; 3 using System.Security.Cryptography.X509Certificates; 4 using System.ServiceModel; 5 using System.ServiceModel.Channels; 6 using System.Xml.Linq; 7 using Microsoft.Samples.WindowsAzure.ServiceManagement; 8 9 namespace AutoScale 10 { 11 class Program 12 { 13 private const string ServiceEndpoint = "https://management.core.windows.net"; 14 15 private static Binding WebHttpBinding() 16 { 17 var binding = new WebHttpBinding(WebHttpSecurityMode.Transport); 18 binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate; 19 binding.ReaderQuotas.MaxStringContentLength = 67108864; 20 21 return binding; 22 } 23 24 static void Main(string[] args) 25 { 26 // Some commercial info :-) 27 Console.WriteLine("AutoScale - (c) 2011 Maarten Balliauw"); 28 Console.WriteLine(""); 29 30 // Quick-and-dirty argument check 31 if (args.Length != 6) 32 { 33 Console.WriteLine("Usage:"); 34 Console.WriteLine(" AutoScale.exe <certificatefile> <subscriptionid> <servicename> <rolename> <slot> <instancecount>"); 35 Console.WriteLine(""); 36 Console.WriteLine("Example:"); 37 Console.WriteLine(" AutoScale.exe mycert.cer 39f53bb4-752f-4b2c-a873-5ed94df029e2 bing Bing.Web production 20"); 38 return; 39 } 40 41 // Save arguments to variables 42 var certificateFile = args[0]; 43 var subscriptionId = args[1]; 44 var serviceName = args[2]; 45 var roleName = args[3]; 46 var slot = args[4]; 47 var instanceCount = args[5]; 48 49 // Do the magic 50 var managementClient = Microsoft.Samples.WindowsAzure.ServiceManagement.ServiceManagementHelper.CreateServiceManagementChannel( 51 WebHttpBinding(), new Uri(ServiceEndpoint), new X509Certificate2(certificateFile)); 52 53 Console.WriteLine("Retrieving current configuration..."); 54 55 var deployment = managementClient.GetDeploymentBySlot(subscriptionId, serviceName, slot); 56 string configurationXml = ServiceManagementHelper.DecodeFromBase64String(deployment.Configuration); 57 58 Console.WriteLine("Updating configuration value..."); 59 60 var serviceConfiguration = XDocument.Parse(configurationXml); 61 62 serviceConfiguration 63 .Descendants() 64 .Single(d => d.Name.LocalName == "Role" && d.Attributes().Single(a => a.Name.LocalName == "name").Value == roleName) 65 .Elements() 66 .Single(e => e.Name.LocalName == "Instances") 67 .Attributes() 68 .Single(a => a.Name.LocalName == "count").Value = instanceCount; 69 70 var changeConfigurationInput = new ChangeConfigurationInput(); 71 changeConfigurationInput.Configuration = ServiceManagementHelper.EncodeToBase64String(serviceConfiguration.ToString(SaveOptions.DisableFormatting)); 72 73 Console.WriteLine("Uploading new configuration..."); 74 75 managementClient.ChangeConfigurationBySlot(subscriptionId, serviceName, slot, changeConfigurationInput); 76 77 Console.WriteLine("Finished."); 78 } 79 } 80 }

Now schedule this (when needed) and enjoy the benefits of scaling your Windows Azure service.

So you’re lazy? Here’s my sample project (AutoScale.zip (26.31 kb)) and the certificates used (management.pfx (4.05 kb) and management.cer (1.18 kb)).

Note: I use the .cer file here because I generated it on my machine. If you are using a certificate created on another machine, a .pfx file and it's key should be used.


Comments (14) -

Sergejus Lithuania |

Monday, March 21, 2011 7:01 PM

Sergejus

Maarten, I suppose next article will be on auto scaling (due to the name of your executable) Smile

maartenba Belgium |

Monday, March 21, 2011 8:04 PM

maartenba

I'm thinking about doing a series on that as well... But I need some convincing Smile

Bart Czernicki United States |

Tuesday, March 22, 2011 7:48 PM

Bart Czernicki

There was an article on techcrunch or engadget that talked about how Microsoft was patenting a dashboard for cloud elasticity.  Essentially, you had a little window with sliders and you could slide the RAM, CPUs, HD space to what you needed.

That is what is truly needed and combine that with a calendar scheduler for version 1.0 and a true dynamic elasticity option baked in with the Azure AppFabric...so when you start getting say more than 100 requests/second you scale to a second server or something like that.

maartenba Belgium |

Tuesday, March 22, 2011 9:40 PM

maartenba

Check http://www.paraleap.com  for part of that Smile

Christian Weyer Germany |

Tuesday, March 22, 2011 9:15 PM

Christian Weyer

Hey Maarten,

this does not really work this way - you need to pass in the .pfx into the Management API calls (do so via providing a password) as you need the private key on the calling side.
Just happened to me some weeks ago ... ;)

Cheers,
-Christian

maartenba Belgium |

Tuesday, March 22, 2011 9:41 PM

maartenba

Hi Christian,

This worked fine in this example, I also had the idea that PFX was always needed (and used it always before trying this sample) but apparently not in this case...

Best regards,
Maarten

Christian Weyer Germany |

Tuesday, March 22, 2011 9:49 PM

Christian Weyer

Hm...  but if I do not provide the .pfx I get an exception:

System.ServiceModel.Security.MessageSecurityException was unhandled
  Message=The HTTP request was forbidden with client authentication scheme 'Anonymous'.


Hm...

maartenba Belgium |

Wednesday, March 23, 2011 8:39 AM

maartenba

Just double-checked and scaled from 1 to 3 instances. The management portal indeed needs the PFX, but client-side the .cer is enough apparently.

maartenba |

Wednesday, March 23, 2011 8:39 AM

maartenba

Which is also true for the PHP version I'll be demonstrating later this week Smile

Christian Weyer Germany |

Wednesday, March 23, 2011 8:43 AM

Christian Weyer

Hm. It does not work for me with just the .cer in my code. I get the the above mentioned exception.

Larry United States |

Friday, April 01, 2011 3:24 PM

Larry

Doesn't work for me either,  Maarten.  It certainly works on your machine since it generated the .cer in the first place.  Without the private key my machine looks different to Azure.  Azure is asking "are you the machine that created the cert?"  Since my machine didn't create the cert it fails the test.

I am working on this problem.  I suspect it has to do with remote server management.
Larry

consoleart India |

Thursday, May 26, 2011 3:01 PM

consoleart

I used this code for creating the certificate
private static X509Certificate2 GetX509Certificate2(String subjectName)
        {
            X509Certificate2 x509Certificate2 = null;
            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            try
            {
                store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
                

                X509Certificate2Collection x509Certificate2Collection = store.Certificates.Find(X509FindType.FindByThumbprint, "CERTIFICATE THUMB PRINT HERE", false);
                x509Certificate2 = x509Certificate2Collection[0];
              

            }
            finally
            {
                store.Close();
            }
            return x509Certificate2;
        }

and use it in this place
var managementClient = Microsoft.Samples.WindowsAzure.ServiceManagement.ServiceManagementHelper.CreateServiceManagementChannel(
                WebHttpBinding(), new Uri(ServiceEndpoint), GetX509Certificate2("")); //GetX509... instead of New X509...

though iam stuck up with the problem
Retrieving current configuration...
Updating configuration value...
Uploading new configuration...

Unhandled Exception: System.ServiceModel.ProtocolException: The remote server re
turned an unexpected response: (400) Bad Request. ---> System.Net.WebException:
The remote server returned an error: (400) Bad Request.
   at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)

Sergey Ukraine |

Saturday, June 11, 2011 11:04 PM

Sergey

I have similar code and it works. However, I have one question which worries me a lot:
I would expect that when I'm upscale, current instances stay untouched and new once begin initialization, while it looks like already working instances are also upgraded and become temporary not available (it is more visible when I upscale from 1 to 2 - there is a break period when service is not available at all).
Do you have any ideas how to overcome this?

maartenba Belgium |

Tuesday, June 14, 2011 10:10 AM

maartenba

This is because the roles are informed of a configuration change. They are in "updating" state however will keep running. THis behaviour can be modified through the OnChanging event of the RoleEnvironment class.

Pingbacks and trackbacks (1)+

Comments are closed