8 min read

 

A hosted service may have a predictable pattern such as heavy use during the week and limited use at the weekend. Alternatively, it may have an unpredictable pattern identifiable through various performance characteristics. Windows Azure charges by the hour for each compute instance, so the appropriate number of instances should be deployed at all times.

The basic idea is that the number of instances for the various roles in the hosted service is modified to a value appropriate to a schedule or to the performance characteristics of the hosted service. We use the Service Management API to retrieve the service configuration for the hosted service, modify the instance count as appropriate, and then upload the service configuration.

In this recipe, we will learn how to use the Windows Azure Service Management REST API to autoscale a hosted service depending on the day of the week.

Getting ready

We need to create a hosted service. We must create an X.509 certificate and upload it to the Windows Azure Portal twice: once as a management certificate and once as a service certificate to the hosted service.

How to do it…

We are going to vary the instance count of a web role deployed to the hosted service by using the Windows Azure Service Management REST API to modify the instance count in the service configuration. We are going to use two instances of the web role from Monday through Friday and one instance on Saturday and Sunday, where all days are calculated in UTC. We do this as follows:

  1. Create a Windows Azure Project and add an ASP.Net Web Role to it.
  2. Add the following using statements to the top of WebRole.cs:

    using System.Threading;
    using System.Xml.Linq;
    using System.Security.Cryptography.X509Certificates;

    
    
  3. Add the following members to the WebRole class in WebRole.cs:

    XNamespace wa = “http://schemas.microsoft.com/windowsazure”;
    XNamespace sc = http://schemas.microsoft.com/
    ServiceHosting/2008/10/ServiceConfiguration”;

    String changeConfigurationFormat = https://management.core.
    windows.net/{0}/services/hostedservices/{1}/deploymentslots/{2}/
    ?comp=config”;
    String getConfigurationFormat = https://management.core.windows.
    net/{0}/services/hostedservices/{1}/deploymentslots/{2}”;
    String subscriptionId = RoleEnvironment.GetConfigurationSettingVal
    ue(“SubscriptionId”);
    String serviceName = RoleEnvironment.GetConfigurationSettingValue

    (“ServiceName”);
    String deploymentSlot = RoleEnvironment.GetConfigurationSettingVal
    ue(“DeploymentSlot”);
    String thumbprint = RoleEnvironment.GetConfigurationSettingValue

    (“Thumbprint”);
    String roleName = “WebRole1”;
    String instanceId = “WebRole1_IN_0”;

    
    
  4. Add the following method, implementing RoleEntryPoint.Run(), to the WebRole class:

    WebRole class:
    public override void Run()
    {
    Int32 countMinutes = 0;
    while (true)
    {
    Thread.Sleep(60000);
    if (++countMinutes == 20)
    {
    countMinutes = 0;
    if (
    RoleEnvironment.CurrentRoleInstance.Id == instanceId)
    {
    ChangeInstanceCount();
    }
    }
    }
    }

    
    
  5. Add the following method, controlling the instance count change, to the WebRole class:

    private void ChangeInstanceCount()
    {
    XElement configuration = LoadConfiguration();
    Int32 requiredInstanceCount =
    CalculateRequiredInstanceCount();
    if (GetInstanceCount(configuration) !=
    requiredInstanceCount)
    {
    SetInstanceCount(configuration, requiredInstanceCount);
    String requestId = SaveConfiguration(configuration);
    }
    }

    
    
  6. Add the following method, calculating the required instance count, to the WebRole class:

    private Int32 CalculateRequiredInstanceCount()
    {
    Int32 instanceCount = 2;
    DayOfWeek dayOfWeek = DateTime.UtcNow.DayOfWeek;
    if (dayOfWeek == DayOfWeek.Saturday ||
    dayOfWeek == DayOfWeek.Sunday)
    {
    instanceCount = 1;
    }
    return instanceCount;
    }

    
    
  7. Add the following method, retrieving the instance count from the service configuration, to the WebRole class:

    private Int32 GetInstanceCount(XElement configuration)
    {
    XElement instanceElement =
    (from s in configuration.Elements(sc + “Role”)
    where s.Attribute(“name”).Value == roleName
    select s.Element(sc + “Instances”)).First();

    Int32 instanceCount = (Int32)Convert.ToInt32(
    instanceElement.Attribute(“count”).Value);

    return instanceCount;
    }

    
    
  8. Add the following method, setting the instance count in the service configuration, to the WebRole class:

    private void SetInstanceCount(
    XElement configuration, Int32 value)
    {
    XElement instanceElement =
    (from s in configuration.Elements(sc + “Role”)
    where s.Attribute(“name”).Value == roleName
    select s.Element(sc + “Instances”)).First();

    instanceElement.SetAttributeValue(“count”, value);
    }

    
    
  9. Add the following method, creating the payload for the change deployment configuration operation, to the WebRole class:

    private XDocument CreatePayload(XElement configuration)
    {
    String configurationString = configuration.ToString();
    String base64Configuration =
    ConvertToBase64String(configurationString);

    XElement xConfiguration =
    new XElement(wa + “Configuration”, base64Configuration);
    XElement xChangeConfiguration =
    new XElement(wa + “ChangeConfiguration”, xConfiguration);

    XDocument payload = new XDocument();
    payload.Add(xChangeConfiguration);
    payload.Declaration =
    new XDeclaration(“1.0”, “UTF-8”, “no”);

    return payload;
    }

    
    
  10. Add the following method, loading the service configuration, to the WebRole class:

    private XElement LoadConfiguration()
    {
    String uri = String.Format(getConfigurationFormat,
    subscriptionId, serviceName, deploymentSlot);
    ServiceManagementOperation operation =
    new ServiceManagementOperation(thumbprint);
    XDocument deployment = operation.Invoke(uri);

    String base64Configuration = deployment.Element(
    wa + “Deployment”).Element(wa + “Configuration”).Value;
    String stringConfiguration =
    ConvertFromBase64String(base64Configuration);

    XElement configuration =
    XElement.Parse(stringConfiguration);
    return configuration;
    }

    
    
  11. Add the following method, saving the service configuration, to the WebRole class:

    private String SaveConfiguration(XElement configuration)
    {
    String uri = String.Format(changeConfigurationFormat,
    subscriptionId, serviceName, deploymentSlot);
    XDocument payload = CreatePayload(configuration);
    ServiceManagementOperation operation =
    new ServiceManagementOperation(thumbprint);
    String requestId = operation.Invoke(uri, payload);
    return requestId;
    }

    
    
  12. Add the following utility methods, converting a String to and from its base-64 encoded version, to the WebRole class:

    private String ConvertToBase64String(String value)
    {
    Byte[] bytes = System.Text.Encoding.UTF8.GetBytes(value);
    String base64String = Convert.ToBase64String(bytes);
    return base64String;
    }

    private String ConvertFromBase64String(String base64Value)
    {
    Byte[] bytes = Convert.FromBase64String(base64Value);
    String value = System.Text.Encoding.UTF8.GetString(bytes);
    return value;
    }

    
    
  13. Add the ServiceManagementOperation class described in the Getting ready section of the Creating a Windows Azure hosted service recipe to the WebRole1 project.
  14. Set the ConfigurationSettings element in the ServiceDefinition.csdef file to:

    <ConfigurationSettings>
    <Setting name=”DeploymentSlot” />
    <Setting name=”ServiceName” />
    <Setting name=”SubscriptionId” />
    <Setting name=”Thumbprint” />
    </ConfigurationSettings>

    
    
  15. Set the ConfigurationSettings element in the ServiceDefinition.cscfg file to the following:

    <ConfigurationSettings>
    <Setting name=”Microsoft.WindowsAzure.Plugins.Diagnostics.
    ConnectionString” alue=”DefaultEndpointsProtocol=https;AccountNam
    e=ACCOUNT_NAME;AccountKey=ACCOUNT_KEY” />
    <Setting name=”DeploymentSlot” value=”production” />
    <Setting name=”ServiceName” value=”SERVICE_NAME” />
    <Setting name=”SubscriptionId” value=”SUBSCRIPTION_ID” />
    <Setting name=”Thumbprint” value=”THUMBPRINT” />
    </ConfigurationSettings>

    
    

How it works…

In steps 1 and 2, we set up the WebRole class. In step 3, we add private members to define the XML namespace used in processing the response and the String format used in generating the endpoint for the change deployment configuration and get deployment operations. We then initialize several values from configuration settings in the service configuration file deployed to each instance.

In step 4, we implement the Run() class . Every 20 minutes, the thread this method runs in wakes up and, only in the instance named WebRole1_IN_0, invokes the method controlling the instance count for the web role. This code runs in a single instance to ensure that there is no race condition with multiple instances trying to change the instance count simultaneously.

In step 5, we load the service configuration. If we detect that the instance count should change we modify the service configuration to have the desired instance count and then save the service configuration. Note that the service configuration used here is downloaded and uploaded using the Service Management API.

Step 6 contains the code where we calculate the needed instance count. In this example, we choose an instance count of 2 from Monday through Friday and 1 on Saturday and Sunday. All days are specified in UTC. This is the step where we should insert the desired scaling algorithm.

In step 7, we retrieve the instance count for the web role from the service configuration. In step 8, we set the instance count to the desired value in the service configuration.

In step 9, we create the payload for the change deployment configuration operation. We create a Configuration element and add a base-64 encoded copy of the service configuration to it. We add the Configuration element to the root ChangeConfiguration element which we then add to an XML document.

In step 10, we use the ServiceManagementOperation utility class , described in the Creating a Windows Azure hosted service recipe, to invoke the get deployment operation on the Service Management API. The Invoke() method creates an HttpWebRequest, adds the required X.509 certificate, and sends the request to the get deployment endpoint. We load the response into an XML document from which we extract the base-64 encoded service configuration. We then convert this into its XML format and load this into an XElement which we return.

In step 11, we use the ServiceManagementOperation utility class to invoke the change deployment configuration operation on the Service Management API. The Invoke() method creates an HttpWebRequest, adds the required X.509 certificate and the payload, and then sends the request to the change deployment configuration endpoint. It then parses the response to retrieve the request ID.

In step 12, we add two utility methods to convert to and from a base-64 encoded String.

In step 13, we add the ServiceManagementOperation utility class that we use to invoke operations against the Service Management API.

In steps 14 and 15, we define some configuration settings in the service definition file and specify them in the service configuration file. We provide values for the Windows Azure Storage Service account name and access key. We also provide the subscription ID for the Windows Azure subscription, as well as the service name for current hosted service. We also need to add the thumbprint for the X.509 certificate we uploaded as a management certificate to the Windows Azure subscription and a service certificate to the hosted service we are deploying this application into. Note that this thumbprint is the same as that configured in the Certificate section of the ServiceConfiguration.cscfg file. This duplication is necessary because the Certificate section of this file is not accessible to the application code.

Summary

Windows Azure charges by the hour for each compute instance, so the appropriate number of instances should be deployed at all times. Autoscaling with the Windows Azure Service Management REST API as shown in this article is a boon in terms of keeping track of number of deployments at any time.


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here