I recently had to come up with options for scaling a Azure HDInsight cluster out (adding worker nodes) or in (removing worker nodes) based on a time based schedule. At the time of writing HDInsight doesn’t allow you to pause or stop a cluster – you have to delete the cluster if you do not want to incur costs. One way of potentially reducing costs is to use workload specific clusters with Azure Blob Storage and Azure SQL Database for the metastore, then scaling down the cluster outside of core hours when there is no processing to be done.
There are a number of ways of doing this including:
- Using Azure Resource Manager (ARM) template that has a parameter for the number of worker nodes; then simply running the template at a scheduled time with the appropriate number of worker nodes.
- Using the PowerShell module to scale the cluster with Set-AzureHDInsightCluster cmdlet and either running the script as a scheduled task from a VM or using Azure Automation
This post is going to show how to use a script that can be run either via Azure Automation or via a scheduled task running on a VM, the solution consists of:
- An Azure Storage Account containing XML configuration files that includes information on the cluster, the subscription within which it resides, the number of worker nodes when scaling out the cluster, the number of worker nodes when scaling in the cluster, a list of email addresses of people that are to be notified when a scaling operation takes place
- A PowerShell script that uses the Azure PowerShell module to scale the cluster out and send email notifications
- Optionally an Azure Automation account from which to schedule the script to run
Azure Storage
Create a storage account (a LRS storage account is sufficient) and a private container, in the container for each cluster store a XML configuration file of the following format:
<ClusterConfiguration>
<SubscriptionName>MySubscriptionName</SubscriptionName>
<ResourceGroupName>MY-RG-0001</ResourceGroupName>
<ClusterName>vjt-hdi1</ClusterName>
<MinWorkers>1</MinWorkers>
<MaxWorkers>3</MaxWorkers>
</ClusterConfiguration>
Where:
- <SubscriptionName> is the subscription containing the cluster to be scaled out/in
- <ResourceGroupName> is the resource group containing the HDInsight cluster to scale
- <ClusterName> is the name of the cluster to scale
- <MinWorkers> is the number of worker nodes in the cluster when performing a scale IN operation
- <MaxWorkers> is the number of worker nodes in the cluster when performing a scale OUT operation
- <Notify> is a comma separated list of email addresses to which notifications are to be sent
NOTE: It is important that the XML configuration file is in UTF-8 format.
While one XML configuration file could conceivably contain the information for every cluster there are a number of benefits to having one configuration file per cluster:
- No need to write logic to parse the file and find the relevant part for the target cluster
- I can keep each cluster configuration in version control and separately modify them
- A mistake in the file is less likely to affect all clusters
PowerShell script
The HDInsight cluster scaling PowerShell script is available in my GitHub repository.
The script will need to be modified to suit your environment but the key elements of the script are described here. Since the script can be either run from Azure Automation or via a scheduled task from a Windows server, it supports sending emails from an internal mail server. The script assumes no authentication is required for the internal mail server (but it is trivial to add authentication support for the internal mail server).
Email Providers
Storing credentials on disk
runas /user:SVC-RTE-PSAutomation powershell.exe
$Password = Read-Host -assecurestring "Please enter your password"
$Password | ConvertFrom-SecureString
<credentials>
<credential>
<password>ReplaceWithActualPassword</password>
</credential>
</credentials>
Key Elements of the script
If( -not($PSPrivateMetadata.JobId) )
[byte[]] $myByteArray = New-Object -TypeName byte[] -ArgumentList ($BlobReference.Length)
$null = $BlobReference.ICloudBlob.DownloadToByteArray($myByteArray,0)
[xml] $BlobContent = [xml] ([System.Text.Encoding]::UTF8.GetString($myByteArray))
$HDInsightClusterDetails = Get-AzureRmHDInsightCluster | Where-Object {
$_.Name -eq $ClusterName
}
$ClusterSpec = Get-AzureRmResource -ResourceId $HDInsightClusterDetails.Id |
Select-Object -ExpandProperty Properties |
Select-Object -ExpandProperty computeProfile