The Challenge

As developers, we deal with lots of complexity, and this is a good thing. It forces us to be creative, and sometimes to go beyond our known universe to overcome challenges.

Microsoft Azure is designed to help us make the right choices. It imposes performance targets through a multitude of mechanisms like throttling and quotas. One of which, I’m sure you have come to know, is that we cannot scale a Cloud Service to zero instances. Let’s stop for a moment and think about this limitation for a second. How would you creatively overcome this challenge?

Being Creative

Unlike Virtual Machines, Cloud Services incur costs when they are stopped. In order to stop paying, we must delete the deployment. These are the constraints that we must compose with.

Deleting a deployment comes with added complexity. It introduces significant latency, because it takes a few minutes for a Cloud Service to reach a ready state. Therefore, scenarios that are time sensitive are not satisfied by these workflows.

In a post about scaling Cloud Services, I shared a PowerShell runbook that can deploy packages from Azure Storage based on a schedule. It was originally built to deploy a scaled-up Cloud Service package to satisfy peak loads and to deploy a scaled-down Cloud Service package for the quite hours. This same script can be used as a starting point for the following scenarios.

Plan for a grace period between job executions, so that deployments have time to complete. As Cloud Services transition, they rarely provide any value, so try to come up with a good scheduling strategy. This will help you maximize your investment.

Scenario: Deploying a Package based on Queue Length

This scenario is depicted in the diagram above. Using Azure Automation, and a scheduled job, we can monitor the depth of a queue. When the queue reaches the targeted depth, the runbook deploys the Cloud Service.

The deployed Cloud Service is responsible for processing messages from the monitored queue.

Every time the job executes, it monitors the depth of a queue. If the queue is below the targeted depth, the runbook deletes the deployment.

Step By Step

  1. Trigger Azure Automation deployment job.
  2. Check whether queue depth is greater than target.
  3. Check whether there is an active deployment.
  4. Download Cloud Service Configurations and deploy the Cloud Service package from Azure Storage.
  5. The deployed Cloud Service is functional and operating as expected.
  6. Trigger Azure Automation deployment job.
  7. Check whether queue depth is below the target.
  8. Check whether there is an active deployment.
  9. Delete the active deployment.

Scenario: Deploying a Package based on a Schedule

Scaling-down-to-zero-second-scenario
In this scenario, we use Azure Scheduler to deploy a Cloud Service to a Production slot. This can be quite useful for end of day processing. Scaling down to zero occurs based on a predefined schedule. This triggers an Azure Automation job that checks whether a Cloud Service is currently deployed. If it discovers an active deployment, it deletes it.

Step By Step

  1. Trigger Azure Automation deployment job.
  2. Check whether there is an active deployment.
  3. Download Cloud Service Configurations and deploy the Cloud Service package from Azure Storage.
  4. The deployed Cloud Service is functional and operating as expected.
  5. Trigger Azure Automation delete deployment job.
  6. Check whether there is an active deployment.
  7. Delete the active deployment.

Deploying a Cloud Service

Taken from my previous post about scaling Cloud Services, this script can be taken as a starting point for your deployment efforts. In practice, this runbook deploys a Cloud Service package and its configuration from Azure Storage to a production slot.

Please refer to my previous post for detailed instructions and for a complete example.

workflow Invoke-DeployPackage
{
   param
   (
      [parameter(Mandatory=$true, `
                 HelpMessage = 'The name given to the Windows PowerShell Credentials located in the Assets of this Azure Automation instance')]
      [String]
      $AzureCredentialsName,

      [parameter(Mandatory=$true, `
                 HelpMessage = 'The name of the Microsoft Azure Subscription that contains the resources your wish to deploy')]
      [String]
      $AzureSubscriptionName,

      [parameter(Mandatory=$true, `
                 HelpMessage = 'The name of the service you wish to deploy')]
      [String]
      $ServiceName,

      [parameter(Mandatory=$true, `
                 HelpMessage = 'The location of the service your with to deploy I.E. "East US"')]
      [String]
      $ServiceLocation,

      [parameter(Mandatory=$true, `
                 HelpMessage = 'The name of the Microsoft Azure storage account that contains the Cloud Service Packages')]
      [String]
      $StorageAccountName,

      [parameter(Mandatory=$true, `
                 HelpMessage = 'The name of the Blob container that contains the Cloud Service Packages')]
      [String]
      $StorageContainerName,

      [parameter(Mandatory=$true, HelpMessage = 'The name of the Cloud Service Package file')]
      [String]
      $PackageBlobName,

      [parameter(Mandatory=$true, HelpMessage = 'The name of the Cloud Service Configurations file')]
      [String]
      $ConfigurationBlobName
    )

    $VerbosePreference = 'Continue'

    # Mark the start time of the script execution
    $StartTime = Get-Date

    $DeploymentLable = $ServiceName + ' (' + $StartTime +')'

    Write-Verbose ('Connecting to Microsoft Azure')

    $Credentials = Get-AutomationPSCredential `
                       -Name $AzureCredentialsName

    Add-AzureAccount `
       -Credential $Credentials

    InlineScript{

        Write-Verbose ('Selecting Azure Subscription')

        Select-AzureSubscription -SubscriptionName $Using:AzureSubscriptionName

        $StorageAccount = (Get-AzureStorageAccount -StorageAccountName $Using:StorageAccountName).Label

        Write-Verbose ('Setting the Azure Subscription and Storage Accounts')

        Set-AzureSubscription `
            -SubscriptionName $Using:AzureSubscriptionName `
            -CurrentStorageAccount $StorageAccount

        Write-Verbose ('[Start] Validating Azure cloud service environment {0}' -f $Using:ServiceName)

        try
        {
            $CloudService = Get-AzureService `
                                -ServiceName $Using:ServiceName

            Write-Verbose ('cloud service {0} in location {1} exist!' -f $Using:ServiceName, $Using:ServiceLocation)
        }
        catch
        {
            #Create
            Write-Verbose ('[Start] creating cloud service {0} in location {1}' -f $Using:ServiceName, $Using:ServiceLocation)

            New-AzureService `
                -ServiceName $Using:ServiceName `
                -Location $Using:ServiceLocation

            Write-Verbose ('[Finish] creating cloud service {0} in location {1}' -f $Using:ServiceName, $Using:ServiceLocation)
        }

        Write-Verbose ('[Finish] Validating Azure cloud service environment {0}' -f $Using:ServiceName)

        $TempFileLocation = "C:\$Using:ConfigurationBlobName"

        Write-Verbose ('Downloading Service Configurations from Azure Storage')

        Get-AzureStorageBlobContent `
            -Container $Using:StorageContainerName `
            -Blob  $Using:ConfigurationBlobName `
            -Destination $TempFileLocation `
            -Force

        Write-Verbose('Downloaded Configuration File: '+ $TempFileLocation)

        Write-Verbose('Getting Package Url from Azure Storage: '+ $Using:PackageBlobName)

        $blob = $(Get-AzureStorageBlob -Blob $Using:PackageBlobName -Container $Using:StorageContainerName)

        $PackageUri = $blob.ICloudBlob.Uri.AbsoluteUri

        Write-Verbose('Package Url: '+ $PackageUri)

        try
        {
            Write-Verbose('Attempting to Update an Existing Deployment')
            Set-AzureDeployment `
                -Package $PackageUri `
                -Configuration $TempFileLocation `
                -Slot Production `
                -Mode Simultaneous `
                -Label $Using:DeploymentLable `
                -ServiceName  $Using:ServiceName `
                -Upgrade `
                -Force `
                -Verbose

        }catch
        {
            Write-Output $error

            Write-Verbose('Attempting to Deploy the service')

            New-AzureDeployment `
                -Package $PackageUri `
                -Configuration $TempFileLocation `
                -Slot Production `
                -Label $Using:DeploymentLable `
                -ServiceName  $Using:ServiceName `
                -Verbose
        }
    }
}

2 responses to Scaling #Azure Cloud Services to Zero

Trackbacks and Pingbacks:

  1. Dew Drop – March 17, 2015 (#1976) | Morning Dew - March 17, 2015

    […] Scaling #Azure Cloud Services to Zero (Alexandre Brisebois) […]

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.