AWS предоставляет решение для масштабирования на основе очереди SQS: https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-using-sqs-queue.html
Основная идея
- Создание пользовательской метрики CloudWatch
sqs-backlog-per-task
с использованием формулы: sqs-backlog-per-task = sqs-messages-number / running-task-number
. - Создание политики масштабирования отслеживания целей на основе метрики
backlogPerInstance
.
Детали реализации
Пользовательская метрика
В моем случае вся инфраструктура(Fargate, SQS и другие ресурсы) описывается в стеке CloudFormation.Поэтому для расчета и регистрации пользовательской метрики я решил использовать функцию AWS Lambda, которая также описана в стеке CloudFormation и развернута вместе со всей инфраструктурой.
Ниже вы можете найти фрагменты кода для функции AWS Lambda для регистрацииследующие пользовательские метрики:
sqs-backlog-per-task
- используется для масштабирования running-task-number
- используется для оптимизации и отладки масштабирования
AWS Lambdaфункция, описанная в синтаксисе AWS SAM в стеке CloudFormation (infra.yml):
CustomMetricLoggerFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: custom-metric-logger
Handler: custom-metric-logger.handler
Runtime: nodejs8.10
MemorySize: 128
Timeout: 3
Role: !GetAtt CustomMetricLoggerFunctionRole.Arn
Environment:
Variables:
ECS_CLUSTER_NAME: !Ref Cluster
ECS_SERVICE_NAME: !GetAtt Service.Name
SQS_URL: !Ref Queue
Events:
Schedule:
Type: Schedule
Properties:
Schedule: 'cron(0/1 * * * ? *)' # every one minute
Лямбда-код AWS для вычисления и ведения журнала (custom-metric-logger.js):
var AWS = require('aws-sdk');
exports.handler = async () => {
try {
var sqsMessagesNumber = await getSqsMessagesNumber();
var runningContainersNumber = await getRunningContainersNumber();
var backlogPerInstance = sqsMessagesNumber;
if (runningContainersNumber > 0) {
backlogPerInstance = parseInt(sqsMessagesNumber / runningContainersNumber);
}
await putRunningTaskNumberMetricData(runningContainersNumber);
await putSqsBacklogPerTaskMetricData(backlogPerInstance);
return {
statusCode: 200
};
} catch (err) {
console.log(err);
return {
statusCode: 500
};
}
};
function getSqsMessagesNumber() {
return new Promise((resolve, reject) => {
var data = {
QueueUrl: process.env.SQS_URL,
AttributeNames: ['ApproximateNumberOfMessages']
};
var sqs = new AWS.SQS();
sqs.getQueueAttributes(data, (err, data) => {
if (err) {
reject(err);
} else {
resolve(parseInt(data.Attributes.ApproximateNumberOfMessages));
}
});
});
}
function getRunningContainersNumber() {
return new Promise((resolve, reject) => {
var data = {
services: [
process.env.ECS_SERVICE_NAME
],
cluster: process.env.ECS_CLUSTER_NAME
};
var ecs = new AWS.ECS();
ecs.describeServices(data, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.services[0].runningCount);
}
});
});
}
function putRunningTaskNumberMetricData(value) {
return new Promise((resolve, reject) => {
var data = {
MetricData: [{
MetricName: 'running-task-number',
Value: value,
Unit: 'Count',
Timestamp: new Date()
}],
Namespace: 'fargate-sqs-service'
};
var cloudwatch = new AWS.CloudWatch();
cloudwatch.putMetricData(data, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
function putSqsBacklogPerTaskMetricData(value) {
return new Promise((resolve, reject) => {
var data = {
MetricData: [{
MetricName: 'sqs-backlog-per-task',
Value: value,
Unit: 'Count',
Timestamp: new Date()
}],
Namespace: 'fargate-sqs-service'
};
var cloudwatch = new AWS.CloudWatch();
cloudwatch.putMetricData(data, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
Политика масштабирования отслеживания целей
Затем, основываясь на метрике sqs-backlog-per-task
, я создала Политику масштабирования отслеживания целей в своем шаблоне Cloud Formation.
ЦельПолитика масштабирования слежения на основе метрики sqs-backlog-per-task
(infra.yml):
ServiceScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: service-scaling-policy
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ServiceScalableTarget
TargetTrackingScalingPolicyConfiguration:
ScaleInCooldown: 60
ScaleOutCooldown: 60
CustomizedMetricSpecification:
Namespace: fargate-sqs-service
MetricName: sqs-backlog-per-task
Statistic: Average
Unit: Count
TargetValue: 2000
В результате AWS Application Auto Scaling создает и управляетs тревоги CloudWatch, которые запускают политику масштабирования и рассчитывают корректировку масштабирования на основе метрики и целевого значения.Политика масштабирования добавляет или удаляет емкость по мере необходимости, чтобы показатель оставался на заданном целевом значении или приближался к нему.Помимо поддержания показателя близким к целевому значению, политика масштабирования целевого отслеживания также приспосабливается к изменениям в показателе из-за изменения схемы загрузки.