AWS Cloud Operations & Migrations Blog

How to create an Amazon EC2 Auto Scaling policy based on a memory utilization metric (Windows)

In the first of this two-part series, I showed you how to create an Amazon EC2 Auto Scaling policy based on a memory utilization metric for Linux OS. In this second post, I walk through how to create Amazon EC2 Auto Scaling policy based on memory utilization metric for Windows OS.

I will use a launch template to create an Amazon EC2 Auto Scaling group that installs the CloudWatch unified agent and configures it with custom metrics aggregated on the AutoScalingGroupName dimension using AWS Systems Manager Parameter Store. Then I’ll create a scaling policy on the CloudWatch agent custom metric.

Amazon EC2 launch template with user data to install the CloudWatch data and configure the agent using the Systems Manager Parameter Store. The scaling policy is based on the CloudWatch custom metric alarm triggering.

Figure 1: Solution flow

Walkthrough

In this walkthrough, you will complete the following steps.

  1. Create a Systems Manager parameter with the CloudWatch agent configuration to create an aggregated metric on memory usage percentage.
  2. Create an AWS Identity and Access Management (IAM) role to use with the CloudWatch agent.
  3. Install and configure the CloudWatch agent on your Auto Scaling fleet. I use Amazon EC2 user data in the Amazon EC2 Auto Scaling launch templates with AWS Systems Manager Parameter Store.
  4. Create an Amazon EC2 Auto Scaling group.
  5. Create an Amazon EC2 Auto Scaling policy that uses target tracking scaling policies or step scaling policies.

You can use the AWS CloudFormation template provided in this post to:

  • Create a Systems Manager parameter.
  • Create an Amazon EC2 Auto Scaling launch template with the Microsoft Windows Server 2016 Base AMI
  • Create an Amazon EC2 Auto Scaling group and target tracking policy on your Windows fleet.

Clickable launch stack button

Prerequisites

You need an AWS account with the IAM permissions required to access Amazon CloudWatch, Amazon EC2, Amazon EC2 Auto Scaling, and AWS Systems Manager.

Set up Systems Manager Parameter store

Installing the CloudWatch agent on a server running Windows Server enables you to collect the metrics associated with the counters in Windows Performance Monitor. You can have subsections for each Windows performance object, such as Memory, Processor, and LogicalDisk.

In the Memory object, the only available usage percentage metric is % Committed Bytes In Use which represent used memory from RAM and swap/page file. To get only the RAM used percentage, I will create a one-minute scheduled task that runs a PowerShell script to calculate the RAM used percentage and sends it to the CloudWatch agent with the StatsD protocol.

I configured the StatsD protocol to listen for UDP port 8125, which will receive the RAM used percentage from the PowerShell script. The agent will aggregate % Committed Bytes In Use and statsd metrics on the AutoScalingGroupName dimension. I also configure the agent to send these metrics for each instance with the InstanceId dimension. If you don’t want these metrics for each instance, then remove append_dimensions for InstanceId.

Note that each metric the agent is pushing is a custom metric. For more information on pricing for custom metrics, please see the CloudWatch pricing page.

{
    "metrics": {
    "append_dimensions": {
            "AutoScalingGroupName": "${aws:AutoScalingGroupName}",
            "InstanceId": "${aws:InstanceId}"
    },
    "aggregation_dimensions" : [["AutoScalingGroupName"]],
    "metrics_collected": {
      "Memory": {
        "measurement": [
          "% Committed Bytes In Use"
        ],
        "metrics_collection_interval": 60
      },
      "statsd": {
        "metrics_aggregation_interval": 60,
        "metrics_collection_interval": 10,
        "service_address": ":8125"
      }
    }
  }
}

To create a parameter

  1. Open the AWS Systems Manager console.
  2. In the navigation pane, choose Parameter Store.
  3.  Choose Create parameter.
  4. In Name, enter a hierarchy and name (for example, /Test/helloWorld). For more information, see Working with parameter hierarchies.
  5. In Description, enter a description that identifies this parameter as a test parameter.
  6. For Parameter tier, choose Standard. For more information about advanced parameters, see Managing parameter tiers.
  7. For Type, choose String.
  8. Leave Data type at its default.
  9. In Value, add the CloudWatch agent configuration JSON.
  10. Choose Create parameter.
  11. In the parameters list, choose the name of the parameter you just created. Verify the details on the Overview tab.

Creating an IAM role to use with the CloudWatch agent

Access to AWS resources requires permissions. You can create IAM roles and users that include the permissions that you need for the CloudWatch agent to write metrics to CloudWatch and for the CloudWatch agent to communicate with Amazon EC2 and AWS Systems Manager. You use IAM roles on Amazon EC2 instances. You use IAM users with on-premises servers.

 

To create the IAM role for each server to run the CloudWatch agent

  1. Sign in to the AWS Management Console and open the IAM console.
  2. In the navigation pane, choose Roles, and then choose Create role.
  3. Under Select type of trusted entity, choose AWS service.
  4. Under Common use cases, choose EC2, and then choose Next: Permissions.
  5. In the list of policies, select the checkbox next to CloudWatchAgentServerPolicy. If necessary, use the search box to find the policy.
  6. Choose Next: Tags.
  7. (Optional) Add one or more tag-key value pairs to organize, track, or control access for this role, and then choose Next: Review.
  8. For Role name, enter a name for your new role (for example, CloudWatchAgentServerRole).
  9. (Optional) For Role description, enter a description.
  10. Confirm that CloudWatchAgentServerPolicy appears next to Policies.
  11. Choose Create role.

Create a launch template for an Auto Scaling group with user data

Before you can create an Auto Scaling group using a launch template, you must create a launch template that includes the parameters required to launch an EC2 instance, such as the ID of the Amazon Machine Image (AMI) and an instance type. We strongly recommend that you do not use launch configurations. They do not provide full functionality for Amazon EC2 Auto Scaling or Amazon EC2. We provide information about launch configurations for customers who have not yet migrated from launch configurations to launch templates.

To create your launch template

  1. Open the Amazon EC2 console.
  2. On the navigation pane, under INSTANCES, choose Launch Templates.
  3. Choose Create launch template. Enter a name and description for the initial version of the launch template.
  4. Under Auto Scaling guidance, select the checkbox to have Amazon EC2 provide guidance to help create a template to use with Amazon EC2 Auto Scaling.
  5. Under Launch template contents, fill out each required field and any optional fields to use as your instance launch specification.
  6. Expand Advanced details to view the fields.
  7. For IAM instance profile, you can specify an IAM instance profile to associate with the instances. When you choose an instance profile, you associate the corresponding IAM role, CloudWatchAgentServerRole, created for the EC2 instances.
  8. For User data, you can specify user data to configure an instance during launch, or to run a configuration script after the instance starts.
  9. Choose Create launch template.

User data script

Create a PowerShell user data script to do the following:

  1. Install the CloudWatch agent package on your Windows fleet and configure the agent using the Parameter Store.
  2. Create a PowerShell script to do the following:
    • Calculate the used memory percentage using a PowerShell command.
    • Using a UDP connection, send the calculated value with the MemoryUtilization metric name and type as gauges to the CloudWatch agent’s statsd UDP port. Gauges are instantaneous measurements of a value, like the speedometer in a car. They can increase, decrease, or be set to a certain value. They are a good option if you need to instrument, for example, the current load of the system.
  3. Create a scheduled task to run the PowerShell script every minute.
<powershell>
#Download agent MSI for windows
Invoke-WebRequest https://s3.amazonaws.com/amazoncloudwatch-agent/windows/amd64/latest/amazon-cloudwatch-agent.msi -OutFile c:\amazon-cloudwatch-agent.msi

#Install agent and wait for the installation to finish
msiexec /i c:\amazon-cloudwatch-agent.msi /quiet | Out-Null

#Remove installed MSI
Remove-Item 'c:\amazon-cloudwatch-agent.msi'

# Configure the agent using the SSM Parameter
Invoke-Expression '& "C:\Program Files\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1" -a fetch-config -m ec2 -s -c ssm:CWAgent-Windows'

#Create script to run on schedule task every 1 minute to collect the Memory Usage Percentage and send it to StatsD in CloudWatch Unified Agent
$MultilineCode = @'

#IP and Port that StatsD listen to
$ipAddress=[System.Net.IPAddress]::Parse("127.0.0.1")
$port=8125

#Your custom metric name
$MetricName="MemoryUtilization"

#Calculate the percentage for the Memory Utilization (Used Percentage)
$ComputerMemory = Get-WmiObject -ComputerName $env:computername -Class win32_operatingsystem -ErrorAction Stop
$MemoryUtilization=[math]::Round(((($ComputerMemory.TotalVisibleMemorySize - $ComputerMemory.FreePhysicalMemory)*100)/ $ComputerMemory.TotalVisibleMemorySize), 2)

#Formulate the message to send to StatusD 'MetricName:Value|MetricType'
$Message="${MetricName}:${MemoryUtilization}|g"

#Create an UDP Connection endpoint and client
$endPoint=New-Object System.Net.IPEndPoint($ipAddress, $port)
$UDPClient=New-Object System.Net.Sockets.UdpClient

#Encode and send the data to UDP server
$encodedData=[System.Text.Encoding]::ASCII.GetBytes($Message)
$bytesSent=$UDPClient.Send($encodedData,$encodedData.length,$endPoint)

#Close UDP connection
$UDPClient.Close()

'@

$MultilineCode  | Out-File c:\MemoryUsageUtilizationMetric.ps1

#Create schedule task that run every 1 minute
schtasks /create /tn MemoryUtilization /tr "powershell -NoLogo -WindowStyle hidden -file c:\MemoryUsageUtilizationMetric.ps1" /sc minute /mo 1 /ru System

</powershell>

Verify created metrics

Verify that the aggregated MemoryUtilization metric is the same as the average math expression of the MemoryUtilization metrics for the Amazon EC2 Auto Scaling instances.

Comparing aggregated MemoryUtilization metric on Amazon EC2 Auto Scaling group with the average math expression of the MemoryUtilization metrics on individual instances in the Windows fleet.

Figure 2: MemoryUtilization

Compare Memory % Committed Bytes In Use with the RAM utilization metric

RAM utilization has a higher value because Memory ‘% Committed Bytes In Use’ uses both RAM and swap/page file for the total available memory.

Comparing Memory % Committed Bytes In Use with PowerShell custom RAM utilization metric, shows that Memory % Committed Bytes In Use has lower utilization because it uses a higher total of both RAM and swap/page file for the total available memory.

Figure 3: Comparison of MemoryUtilization and Memory % Committed Bytes In Use

Create an Auto Scaling group using a launch template

With launch templates, you can configure the Auto Scaling group to dynamically choose either the default version or the latest version of the launch template when a scale-out event occurs. For example, you can configure your Auto Scaling group to choose the current default version of a launch template. To change the configuration of the EC2 instances to be launched by the group, create or designate a new default version of the launch template. Alternatively, you can choose the specific version of the launch template that the group uses to launch EC2 instances. You can change these selections at any time by updating the group.

To create an Auto Scaling group using a launch template

  1. Open the Amazon EC2 console.
  2. On the navigation bar, choose the same AWS Region that you used when you created the launch template.
  3. On the navigation pane, under AUTO SCALING, choose Auto Scaling Groups.
  4. Choose Create an Auto Scaling group.
  5. On the Choose launch template or configuration page, do the following:
    1. For Auto Scaling group name, enter a name for your Auto Scaling group.
    2. For Launch Template, choose an existing launch template.
    3. For Launch template version, choose whether the Auto Scaling group uses the default, the latest, or a specific version of the launch template when scaling out.
    4. Verify that your launch template supports all of the options that you are planning to use, and then choose Next.
  6. On the Configure settings page, for Purchase options and instance types, choose Adhere to the launch template to use the EC2 instance type and purchase option that are specified in the launch template.
  7. Under Network, for VPC, choose the VPC for the security groups that you specified in your launch template.
  8. For Subnet, choose one or more subnets in the specified VPC. Use subnets in multiple Availability Zones for high availability. For more information about high availability with Amazon EC2 Auto Scaling, see Distributing Instances Across Availability Zones.
  9. Choose Next. Or, you can accept the rest of the defaults, and choose Skip to review.
  10. On the Review page, choose Create Auto Scaling group.

Create an Amazon EC2 Auto Scaling policy

We strongly recommend that you use a target tracking scaling policy to scale on a metric like average CPU utilization. Metrics that decrease when capacity increases and increase when capacity decreases can be used to proportionally scale out or in the number of instances using target tracking. This helps ensure that Amazon EC2 Auto Scaling closely follows the demand curve for your applications. For more information, see Target tracking scaling policies. You still have the option to use simple scaling policies or step scaling as an additional policy for a more advanced configuration. In most cases, step scaling policies are a better choice than simple scaling policies, even if you have only a single scaling adjustment.

To create a target tracking policy

You must use the AWS CLI to create a target tracking policy for a customized metric. Make sure the values for MetricName, Namespace, and Dimensions match your configured metric in the CloudWatch unified agent.

  1. Create a JSON configuration file that uses a target value of 40% on average of the MemoryUtilization metric.

vim config.json

{
    "TargetValue":40.0,
    "CustomizedMetricSpecification":{
       "MetricName":"MemoryUtilization",
       "Namespace":"ASG_Memory",
       "Dimensions":[
          {
             "Name":"AutoScalingGroupName",
             "Value":"[Your ASG Name]"
          }
       ],
       "Statistic":"Average",
       "Unit":"Percent"
    }
 }
  1. Run the put-scaling-policy CLI command using the config.json:
aws autoscaling put-scaling-policy --policy-name Memory40-target-tracking-scaling-policy --auto-scaling-group-name Memory --policy-type TargetTrackingScaling --target-tracking-configuration file://config.json

To create a step scaling policy

With step scaling, you choose scaling metrics and threshold values for the CloudWatch alarms that trigger the scaling process. You also define how your Auto Scaling group should be scaled when a threshold is breached for a specified number of evaluation periods.

To create a CloudWatch alarm

  1. Open the CloudWatch console.
  2. If necessary, change the AWS Region to the Region where your Auto Scaling group resides.
  3. In the navigation pane, choose Alarms, and then choose Create alarm.
  4. Choose Select metric.
  5. On the All metrics tab, enter the Auto Scaling group’s name in the search field. Then, choose the MemoryUtilization metric. The Specify metric and conditions page appears, showing a graph and other information about the metric.
  6. For Period, choose the evaluation period for the alarm (for example, 1 minute). When evaluating the alarm, each period is aggregated into one data point.
  7. Under Conditions, do the following:
    1. For Threshold type, choose Static.
    2. For Whenever MemoryUtilization is, specify whether you want the value of the metric to be greater than, greater than or equal to, less than, or less than or equal to the threshold to trigger the alarm. Then, under than, enter the threshold value that you want to trigger the alarm.
  8. Under Additional configuration, do the following:
    1. For Datapoints to alarm, enter the number of data points (evaluation periods) during which the metric value must meet the threshold conditions to trigger the alarm. For example, two consecutive periods of 5 minutes would take 10 minutes to trigger the alarm.
    2. For Missing data treatment, choose Treat missing data as bad (breaching threshold). For more information, see Configuring how CloudWatch alarms treat missing data.
  9. Choose Next.
  10. (Optional) Under Notification, you can choose or create the Amazon SNS topic you want to use to receive notifications. Otherwise, you can remove the notification now and add one later as needed.
  11. Choose Next.
  12. Enter a name (for example, Step-Scaling-AlarmHigh-AddCapacity) and an optional description for the alarm, and then choose Next.
  13. Choose Create alarm.

To create step scaling policies

While you are configuring your scaling policies, you can create the alarms. Alternatively, you can use alarms that you created in the CloudWatch console, as described in the previous section.

To create a step scaling policy for scale out

  1. Open the Amazon EC2 console.
  2. On the navigation pane, under AUTO SCALING, choose Auto Scaling Groups.
  3. Select the checkbox next to your Auto Scaling group.

A split pane opens in the bottom of the Auto Scaling groups page, showing information about the group selected.

  1. Verify that the minimum and maximum size limits are appropriately set. For example, if your group is already at its maximum size, you need to specify a new maximum in order to scale out. Amazon EC2 Auto Scaling does not scale your group below the minimum capacity or above the maximum capacity. To update your group, on the Details tab, change the current settings for minimum and maximum capacity.
  2. On the Automatic scaling tab, in Scaling policies, choose Add policy.
  3. To define a policy for scale out (increase capacity), do the following:
    1. For Policy type, choose Step scaling.
    2. Enter a name for the policy.
    3. For CloudWatch alarm, choose your alarm. If you haven’t already created an alarm, choose Create a CloudWatch alarm and complete steps 4-13 to create an alarm that monitors CPU utilization. Set the alarm threshold to greater than or equal to 80 percent.
    4. Use Take the action to specify the change in the current group size that this policy will make when executed. You can add a specific number of instances or a percentage of the existing group size or set the group to an exact size.

For example, choose Add, enter 30 in the next field, and then choose percent of group. By default, the lower bound for this step adjustment is the alarm threshold and the upper bound is positive infinity.

    1. To add another step, choose Add step, and then define the amount by which to scale and the lower and upper bounds of the step relative to the alarm threshold.
    2. To set a minimum number of instances to scale, update the number field in Add capacity units in increments of at least 1 capacity units.
    3. Specify an instance warm-up value for Instances need, which allows you to control the amount of time until a newly launched instance can contribute to the CloudWatch metrics.
  1. Choose Create.

To create a step scaling policy for scale in

  1. Choose Add policy to continue where you left off after creating a policy for scale out.
  2. To define a policy for scale in (decrease capacity), do the following:
    1. For Policy type, choose Step scaling.
    2. Enter a name for the policy.
    3. For CloudWatch alarm, choose your alarm. If you haven’t already created an alarm, choose Create a CloudWatch alarm and complete steps 4-13 to create an alarm that monitors CPU utilization. Set the alarm threshold to less than or equal to 40 percent.
    4. Use Take the action to specify the change in the current group size that this policy will make when executed. You can remove a specific number of instances or a percentage of the existing group size or set the group to an exact size.

For example, choose Remove, enter 2 in the next field, and then choose capacity units. By default, the upper bound for this step adjustment is the alarm threshold and the lower bound is negative infinity.

    1. To add another step, choose Add step, and then define the amount by which to scale and the lower and upper bounds of the step relative to the alarm threshold.
  1. Choose Create.

Cleanup

To avoid ongoing charges to your account, delete the resources you created in this walkthrough.

If you used the AWS CloudFormation template to create resources, then delete the stack.

Conclusion

In this blog post, I showed you how to create an Amazon EC2 Auto Scaling policy on a custom CloudWatch metric, such as memory usage percentage. I also showed you how to configure the CloudWatch agent to send the memory utilization metric for Windows OS. For more information, see CloudWatch Agent Configuration File in the Amazon CloudWatch User Guide.

About the author

Ahmed Magdy

Ahmed Magdy Wahdan

Ahmed Magdy Wahdan is a Cloud Support Engineer and CloudWatch SME for Amazon Web Services. He helps global customers design, deploy, and troubleshoot large-scale networks built on AWS. He specializes in CloudWatch, Elastic Load Balancing, Auto Scaling, and Amazon VPC. In his spare time, he loves to free-dive and make desserts.