Auto-scaling ECS instances based on traffic demands
TypeScriptTo set up an auto-scaling Amazon ECS service based on traffic demands, we will use several AWS resources that work together to ensure that our ECS service scales in or out dynamically in response to traffic. Here are the fundamental steps and AWS resources we'll compose using Pulumi in TypeScript:
-
ECS Cluster: This is the fundamental grouping of EC2 instances that will run our Docker containers. We can define an ECS cluster using the
aws.ecs.Cluster
resource. -
Auto Scaling Group (ASG): The
aws.autoscaling.Group
resource allows us to set a minimum and maximum number of EC2 instances that will be managed within the cluster. It connects with the ECS service and automatically adjusts the number of running instances as needed. -
Launch Configuration or Launch Template: This defines how the EC2 instances within the ASG should be configured. For example, we can specify the instance type, AMI, key pair, security groups, etc.
-
CloudWatch Alarms: We will set up CloudWatch alarms to monitor certain metrics (e.g., CPU utilization) that will trigger the auto-scaling actions.
-
Auto Scaling Policies: These policies, configured with the
aws.autoscaling.Policy
resource, are used to define how the ASG should scale. For instance, there can be a scale-out policy which increases the number of instances if the CPU usage goes above a certain threshold, and a scale-in policy to decrease the number of instances if the CPU usage drops below a certain point. -
ECS Service: The ECS service is defined by the
aws.ecs.Service
resource and it runs and maintains a specified number of instances of a task definition simultaneously in an ECS cluster.
Let's put these components together in a basic Pulumi TypeScript program that sets up an auto-scaling ECS service. The following program is descriptive enough to serve as a tutorial for a novice trying to learn Pulumi and AWS for auto-scaling:
import * as aws from "@pulumi/aws"; // Create an ECS cluster. const cluster = new aws.ecs.Cluster("app-cluster"); // Create a launch configuration for the ASG instances. const launchConfig = new aws.ecs.LaunchConfiguration("app-launch-config", { // You can specify the instance type, AMI, key pair, security groups, etc. imageId: "ami-xxxxxx", // Replace with a proper ECS AMI for your region instanceType: "t2.micro", // Choose an appropriate instance type }); // Create an Auto Scaling Group (ASG) with desired min and max sizes. const asg = new aws.autoscaling.Group("app-asg", { maxSize: 5, // Upper limit of the number of instances to scale out. minSize: 1, // Lower limit of the number of instances to remain. launchConfiguration: launchConfig.id, // Use the launch configuration. vpcZoneIdentifiers: ["subnet-xxxxxxxx", "subnet-yyyyyyyy"], // Subnets for the ASG instances. tag: [{ key: "Name", value: "pulumi-asg-instance", propagateAtLaunch: true, }], }, { dependsOn: [launchConfig] }); // Create a scale-out policy for the ASG. const scaleOutPolicy = new aws.autoscaling.Policy("app-scale-out", { scalingAdjustment: 1, adjustmentType: "ChangeInCapacity", cooldown: 300, autoscalingGroupName: asg.name, }); // Create a scale-in policy for the ASG. const scaleInPolicy = new aws.autoscaling.Policy("app-scale-in", { scalingAdjustment: -1, adjustmentType: "ChangeInCapacity", cooldown: 300, autoscalingGroupName: asg.name, }); // Set up CloudWatch alarms to trigger the scaling policies. const highCpuAlarm = new aws.cloudwatch.MetricAlarm("high-cpu-alarm", { comparisonOperator: "GreaterThanOrEqualToThreshold", evaluationPeriods: 2, metricName: "CPUUtilization", namespace: "AWS/EC2", period: 120, // 2 minutes statistic: "Average", threshold: 75, // Percentage alarmActions: [scaleOutPolicy.arn], dimensions: { AutoScalingGroupName: asg.name, }, }); const lowCpuAlarm = new aws.cloudwatch.MetricAlarm("low-cpu-alarm", { comparisonOperator: "LessThanOrEqualToThreshold", evaluationPeriods: 2, metricName: "CPUUtilization", namespace: "AWS/EC2", period: 120, // 2 minutes statistic: "Average", threshold: 25, // Percentage alarmActions: [scaleInPolicy.arn], dimensions: { AutoScalingGroupName: asg.name, }, }); // Create an ECS service with the desired task definition and count. const appService = new aws.ecs.Service("app-service", { cluster: cluster.arn, taskDefinition: "app-task", // Defined elsewhere, e.g., in another Pulumi stack or manually desiredCount: 2, // Start with 2 instances of the task launchType: "EC2", loadBalancers: [{ // Define the load balancer if needed targetGroupArn: "arn:aws:elasticloadbalancing:region:account-id:targetgroup/my-targets/6d0ecf831eec9f09", containerName: "app-container", // Defined in task definition containerPort: 8080, // The port application listens on }], placementStrategies: [{ // Define the service placement strategy if needed type: "spread", field: "attribute:ecs.availability-zone", }], }, { dependsOn: [asg] }); // Export the ECS cluster name, Auto Scaling Group name, and ECS Service name export const ecsClusterName = cluster.name; export const autoScalingGroupName = asg.name; export const ecsServiceName = appService.name;
This program defines a simple auto-scaling setup for an ECS service:
- ECS Cluster: We first declare an ECS cluster named
app-cluster
with the default configurations. - Launch Configuration: Defines the ECS AMI and instance type our ECS Instances will use.
- Auto Scaling Group (ASG): Configured with initial conditions like the size of the group (min and max number of instances), VPC subnets, and the use of our above launch configuration. Additionally, we set some tagging for our instances.
- Scaling Policies: Both the scale-out and scale-in policies above define how our application will respond to both high and low CPU usage conditions.
- CloudWatch Alarms: Two alarms, one to monitor high CPU usage (
highCpuAlarm
) and another to monitor low usage (lowCpuAlarm
), signaling our autoscaling policies to act accordingly. - ECS Service: Finally, the
app-service
represents our ECS service, bound to the created cluster, with a specific task definition. It will manage the desired count of tasks and use the provided load balancer configuration.
Please adjust your specific parameters such as
ami-xxxxxx
to match the Amazon Machine Image (AMI) appropriate for your use case,subnet-xxxxxxxx
andsubnet-yyyyyyyy
with your actual subnet IDs. You should have a task definition specified for the ECS service, and that's why we have'app-task'
as a placeholder for where you will include your task definition name.To create these resources using this Pulumi program, save the file with a
.ts
extension, and then runpulumi up
in the same directory after having installed Pulumi and configured your AWS credentials. The command will show you a preview of the resources that will be created, and you can confirm to proceed with the actual deployment.-