Skip to content

Commit fae393d

Browse files
authored
Merge pull request #2756 from Saborni/main
[New Pattern] SQS - ECS - Target tracking autoscaling
2 parents a82d0b3 + 457005b commit fae393d

14 files changed

+767
-0
lines changed

sqs-ecs-autoscaling/Dockerfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM python:3-alpine3.22
2+
3+
WORKDIR /app
4+
5+
COPY requirements.txt .
6+
7+
RUN pip install --no-cache-dir -r requirements.txt
8+
9+
COPY consumer/app.py .
10+
11+
CMD ["python", "app.py"]

sqs-ecs-autoscaling/README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Implementing Target tracking scaling policies for an Amazon ECS Fargate application
2+
3+
This pattern creates a serverless architecture with an SQS queue consumed by an ECS Fargate service that automatically scales based on queue depth. It includes a VPC with private subnets, VPC endpoints for ECR, S3, CloudWatch Logs, and SQS, enabling the Fargate tasks to process messages without internet access. The target tracking scaling policy adjusts the number of tasks (1-10) based on the number of messages visible in the queue `ApproximateNumberOfMessagesVisible`, targeting 5 messages.
4+
5+
This pattern demonstrates how to effectively decouple your application components and tune your application scalability on the basis of incoming traffic flow.
6+
7+
Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/sqs-ecs-autoscaling](https://serverlessland.com/patterns/sqs-ecs-autoscaling)
8+
9+
Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.
10+
11+
## Requirements
12+
13+
* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
14+
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
15+
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
16+
* [Docker](https://docs.docker.com/engine/install/)
17+
* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed
18+
19+
20+
## Deployment Instructions
21+
22+
1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
23+
```
24+
git clone https://github.com/aws-samples/serverless-patterns
25+
```
26+
1. Change directory to the pattern directory:
27+
```
28+
cd sqs-ecs-autoscaling
29+
```
30+
31+
1. First of all, execute the build-image.sh script, that will create an ECR repository (if not existing), a docker image of your application. It will also push the built image to the newly created registry:
32+
```
33+
chmod +x build-image.sh
34+
sh build-image.sh
35+
```
36+
37+
1. Once the above build is successful, from the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file:
38+
```
39+
sam deploy --guided
40+
```
41+
42+
1. During the prompts:
43+
* Enter a stack name
44+
* Enter the desired AWS Region
45+
* Allow SAM CLI to create IAM roles with the required permissions.
46+
47+
Once you have run `sam deploy -guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults.
48+
49+
1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing.
50+
51+
## Testing
52+
53+
1. Once the deployment is completed successfully, you need to test out how your application scales on the basis of incoming traffic flow. There is a stress testing script `stress-test-app.sh` which upon execution pushes messages in parallel and in bulk to your SQS queue:
54+
```
55+
chmod +x stress-test-app.sh
56+
STACK_NAME=${SAM_STACK_NAME} ./stress-test-app.sh
57+
```
58+
Replace the `SAM_STACK_NAME` with the name of the stack you have used during sam deployment.
59+
While you perform the testing, observe the scaling activities in the ECS console.
60+
61+
![auto-scaling](./images/auto-scaling.png)
62+
63+
2. Observe the CloudWatch logs of the application and the metrics of your cluster using [CloudWatch Container Insights](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ContainerInsights.html). Use the following command to turn on Container Insights with enhanced observability.
64+
65+
Set the containerInsights account setting to enhanced
66+
```
67+
aws ecs update-cluster-settings --cluster CLUSTER_NAME --settings name=containerInsights,value=enhanced
68+
```
69+
70+
By default, the put-account-setting applies only to the currently authenticated user. To enable the setting account-wide for all users and roles, use the root user as in the following example.
71+
```
72+
aws ecs put-account-setting --name containerInsights --value enhanced --principal-arn arn:aws:iam::accountID:root
73+
```
74+
After you set this account setting, all new clusters automatically use Container Insights with enhanced observability. Use the update-cluster-settings command to add Container Insights with enhanced observability to existing cluster, or to upgrade clusters that currently use Container Insights to Container Insights with enhanced observability.
75+
76+
```
77+
aws ecs update-cluster-settings --cluster CLUSTER_NAME --settings name=containerInsights,value=enhanced
78+
```
79+
80+
Replace CLUSTER_NAME with your ECS cluster name. Please refer to [this](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/deploy-container-insights-ECS-cluster.html) document to know how to enable it from AWS Console.
81+
82+
3. Once you have enabled container insights, re-run the stress testing script. In order to view the Container Insights, click the link as shown your ECS Cluster console.
83+
84+
![Insights-1](./images/insights-1.png)
85+
86+
Now, you can observe your Cluster summary and performance metrics at Cluster, Service, Task as well as at Container-level.
87+
88+
![Insights-2](./images/insights-2.png)
89+
90+
Observe the container level metrics below:
91+
92+
![Insights-3](./images/Insights-3.png)
93+
94+
Once the testing is completing, observe how the task count varies with the `ApproximateNumberOfMessagesVisible` metric of your SQS queue:
95+
96+
![Task-Count](./images/task-count.png)
97+
98+
99+
## Cleanup
100+
101+
1. For deleting the stack you can use sam delete from SAM CLI -
102+
```
103+
sam delete
104+
```
105+
106+
2. The delete the repostory:
107+
```
108+
aws ecr delete-repository --repository-name ${ECR_REPOSITORY_NAME} --force --region ${AWS_REGION}
109+
```
110+
111+
Replace `ECR_REPOSITORY_NAME` with the one that you have created. Also, use the correct value for `AWS_REGION`.
112+
113+
114+
----
115+
Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
116+
117+
SPDX-License-Identifier: MIT-0

sqs-ecs-autoscaling/build-image.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Configuration
5+
AWS_REGION=${AWS_REGION:-us-east-1}
6+
ECR_REPOSITORY_NAME=${ECR_REPOSITORY_NAME:-sqs-ecs-app}
7+
IMAGE_TAG=${IMAGE_TAG:-1.0}
8+
9+
# Check if running on macOS
10+
if [[ "$(uname)" == "Darwin" ]]; then
11+
echo "Running on macOS..."
12+
# Check for Apple Silicon
13+
if [[ "$(uname -m)" == "arm64" ]]; then
14+
echo "Apple Silicon detected, using platform flag for Docker build..."
15+
PLATFORM_FLAG="--platform linux/amd64"
16+
else
17+
PLATFORM_FLAG=""
18+
fi
19+
else
20+
PLATFORM_FLAG=""
21+
fi
22+
23+
# Create ECR repository if it doesn't exist
24+
echo "Creating ECR repository if it doesn't exist..."
25+
aws ecr describe-repositories --repository-names ${ECR_REPOSITORY_NAME} --region ${AWS_REGION} || \
26+
aws ecr create-repository --repository-name ${ECR_REPOSITORY_NAME} --region ${AWS_REGION}
27+
28+
# Get ECR login
29+
echo "Logging in to ECR..."
30+
aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin $(aws sts get-caller-identity --query Account --output text).dkr.ecr.${AWS_REGION}.amazonaws.com
31+
32+
# Build the Docker image
33+
echo "Building Docker image..."
34+
docker build ${PLATFORM_FLAG} -t ${ECR_REPOSITORY_NAME}:${IMAGE_TAG} .
35+
36+
# Tag the image for ECR
37+
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
38+
ECR_REPO_URI=${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPOSITORY_NAME}
39+
docker tag ${ECR_REPOSITORY_NAME}:${IMAGE_TAG} ${ECR_REPO_URI}:${IMAGE_TAG}
40+
41+
# Push the image to ECR
42+
echo "Pushing image to ECR..."
43+
docker push ${ECR_REPO_URI}:${IMAGE_TAG}
44+
45+
echo "Image URI: ${ECR_REPO_URI}:${IMAGE_TAG}"
46+
47+
sed -i '' "s|ECR_REPO_URI|$ECR_REPO_URI|g" template.yaml
48+
sed -i '' "s|IMAGE_TAG|$IMAGE_TAG|g" template.yaml
49+
50+
echo "Done!"

sqs-ecs-autoscaling/consumer/app.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
3+
# SPDX-License-Identifier: MIT-0
4+
5+
import os
6+
import boto3
7+
import json
8+
import logging
9+
import time
10+
from botocore.exceptions import ClientError
11+
12+
# Configure logging
13+
logging.basicConfig(level=logging.INFO)
14+
logger = logging.getLogger(__name__)
15+
16+
# Initialize AWS SQS client
17+
sqs = boto3.client('sqs')
18+
queue_url = os.environ.get('QUEUE_URL')
19+
20+
if not queue_url:
21+
raise ValueError("QUEUE_URL environment variable is required")
22+
23+
def process_message(message_body):
24+
"""
25+
Process the message from the queue.
26+
Replace this with your actual message processing logic.
27+
"""
28+
try:
29+
data = json.loads(message_body)
30+
logger.info(f"Processing message: {data}")
31+
# Add your processing logic here
32+
33+
# Simulate some work
34+
time.sleep(1)
35+
36+
return True
37+
except Exception as e:
38+
logger.error(f"Error processing message: {e}")
39+
return False
40+
41+
def main():
42+
logger.info(f"Starting SQS consumer for queue: {queue_url}")
43+
44+
while True:
45+
try:
46+
# Receive messages from SQS queue
47+
response = sqs.receive_message(
48+
QueueUrl=queue_url,
49+
MaxNumberOfMessages=10, # Fetch up to 10 messages at once
50+
WaitTimeSeconds=20, # Long polling
51+
VisibilityTimeout=30 # 30 seconds to process each message
52+
)
53+
54+
messages = response.get('Messages', [])
55+
56+
if not messages:
57+
logger.debug("No messages received")
58+
continue
59+
60+
logger.info(f"Received {len(messages)} messages")
61+
62+
for message in messages:
63+
message_body = message['Body']
64+
receipt_handle = message['ReceiptHandle']
65+
66+
if process_message(message_body):
67+
# Delete message after successful processing
68+
try:
69+
sqs.delete_message(
70+
QueueUrl=queue_url,
71+
ReceiptHandle=receipt_handle
72+
)
73+
logger.info("Message processed and deleted successfully")
74+
except ClientError as e:
75+
logger.error(f"Error deleting message: {e}")
76+
else:
77+
logger.error("Failed to process message")
78+
79+
except ClientError as e:
80+
logger.error(f"Error receiving messages: {e}")
81+
time.sleep(5) # Wait before retrying
82+
except Exception as e:
83+
logger.error(f"Unexpected error: {e}")
84+
time.sleep(5) # Wait before retrying
85+
86+
if __name__ == "__main__":
87+
main()
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"title": "SQS - Amazon ECS Fargate - Target Tracking Scaling Policy",
3+
"description": "This pattern creates an ECS Fargate application that consumes an SQS queue and it scales on the basis of queue size",
4+
"language": "Python",
5+
"level": "400",
6+
"framework": "AWS SAM",
7+
"introBox": {
8+
"headline": "How it works",
9+
"text": [
10+
"This sample project demonstrates how target tracking scaling policy works with an ECS Fargate application",
11+
"We have a python application under consumer/app.py that consumes messages from an SQS queue. The application is containerised and the image is pushed to an ECR registry.",
12+
"On the basis of a target value of the SQS metric - ApproximateNumberOfMessagesVisible, the count of tasks of the cluster will increase.",
13+
"Again, when the actual value drops below the target value, the count of tasks comes down to the initial value."
14+
]
15+
},
16+
"gitHub": {
17+
"template": {
18+
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sqs-ecs-autoscaling",
19+
"templateURL": "serverless-patterns/sqs-ecs-autoscaling",
20+
"projectFolder": "sqs-ecs-autoscaling",
21+
"templateFile": "sqs-ecs-autoscaling/template.yaml"
22+
}
23+
},
24+
"resources": {
25+
"bullets": [
26+
{
27+
"text": "Target tracking scaling policies for Amazon EC2 Auto Scaling",
28+
"link": "https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-scaling-target-tracking.html"
29+
},
30+
{
31+
"text": "Target tracking scaling policies for Application Auto Scaling",
32+
"link": "https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-target-tracking.html"
33+
},
34+
{
35+
"text": "Container Insights with enhanced observability now available in Amazon ECS",
36+
"link": "https://aws.amazon.com/blogs/aws/container-insights-with-enhanced-observability-now-available-in-amazon-ecs/"
37+
}
38+
]
39+
},
40+
"deploy": {
41+
"text": [
42+
"sam deploy"
43+
]
44+
},
45+
"testing": {
46+
"text": [
47+
"See the GitHub repo for detailed testing instructions."
48+
]
49+
},
50+
"cleanup": {
51+
"text": [
52+
"Delete the stack: <code>sam delete</code>."
53+
]
54+
},
55+
"authors": [
56+
{
57+
"name": "Saborni Bhattacharya",
58+
"image": "https://drive.google.com/file/d/1AZFquOkafEQRUlrT4hKOtIbt4Cq66SHd/view?usp=sharing",
59+
"bio": "Specialist SA at AWS EMEA",
60+
"linkedin": "https://www.linkedin.com/in/saborni-bhattacharya-5b523812a/"
61+
}
62+
]
63+
}
546 KB
Loading
344 KB
Loading
295 KB
Loading
391 KB
Loading
195 KB
Loading

0 commit comments

Comments
 (0)