Служба автоматического масштабирования Fargate на основе SQS ApproximateNumberOfMessagesVisible - PullRequest
0 голосов
/ 09 октября 2018

Я хотел бы масштабировать свои контейнеры aws fargate в зависимости от размера очереди SQS.Похоже, что я могу масштабировать только на основе ЦП контейнера или использования памяти.Есть ли способ создать политику, которая будет масштабироваться в зависимости от размера очереди?Кто-нибудь смог масштабировать на основе других метрик Cloudwatch?

Ответы [ 2 ]

0 голосов
/ 05 июня 2019

AWS предоставляет решение для масштабирования на основе очереди SQS: https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-using-sqs-queue.html

Основная идея

  1. Создание пользовательской метрики CloudWatch sqs-backlog-per-task с использованием формулы: sqs-backlog-per-task = sqs-messages-number / running-task-number.
  2. Создание политики масштабирования отслеживания целей на основе метрики 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, которые запускают политику масштабирования и рассчитывают корректировку масштабирования на основе метрики и целевого значения.Политика масштабирования добавляет или удаляет емкость по мере необходимости, чтобы показатель оставался на заданном целевом значении или приближался к нему.Помимо поддержания показателя близким к целевому значению, политика масштабирования целевого отслеживания также приспосабливается к изменениям в показателе из-за изменения схемы загрузки.

0 голосов
/ 09 октября 2018

Да, вы можете сделать это.Вы должны использовать политику масштабирования шага, и вам необходимо создать уже созданный сигнал тревоги для глубины очереди SQS (ApproximateNumberOfMessagesVisible).

Перейдите в CloudWatch, создайте новый сигнал тревоги.Мы назовем этот сигнал тревоги sqs-queue-deep-high и включим его, когда приблизительное число видимых сообщений будет 1000.

После этого перейдите в службу поддержки ECS.Вы хотите, чтобы автомасштабировать.Нажмите Обновить для службы.Добавьте политику масштабирования и выберите вариант «Отслеживание шагов».Вы увидите, что есть возможность создать новый сигнал тревоги (который позволяет выбирать между ЦП или MemoryUtilization) или использовать существующий сигнал тревоги.

Тип sqs-queue-deep-high в поле «Использовать существующую тревогу» и нажмите клавишу ввода, вы должны увидеть зеленую галочку, указывающую, что имя действительно (т.е. тревога существует).Вы увидите новые выпадающие списки, в которых вы можете настроить политику шагов.

Это работает для любых метрических сигналов тревоги и служб ECS.Если вы собираетесь масштабировать эту настройку, например, для нескольких сред, или сделать ее более сложной, чем 2 шага, сделайте себе одолжение и воспользуйтесь CloudFormation или Terraform, чтобы помочь в управлении им.Нет ничего хуже, чем настроить 5-ступенчатый сигнал тревоги для 10 служб.

...