基于SQS的近似数量的消息自动缩放Fargate服务可见

时间:2018-10-09 19:21:58

标签: amazon-web-services amazon-cloudwatch amazon-ecs autoscaling aws-fargate

我想根据SQS队列的大小扩展AWS Fargate容器。看来我只能根据容器的CPU或内存使用量进行扩展。有没有一种方法可以创建根据队列大小进行横向扩展和纵向扩展的策略?有人能够根据其他cloudwatch指标进行扩展吗?

4 个答案:

答案 0 :(得分:5)

是的,您可以这样做。您必须使用逐步缩放策略,并且已经为您的SQS队列深度(roximateNumberofMessagesVisible)创建了警报。

转到CloudWatch,创建一个新警报。我们将此警报称为 sqs-queue-depth-high ,并在可见消息的大约数量为1000时触发该警报。

完成后,转到ECS到要自动扩展的服务。单击该服务的更新。添加缩放策略,然后选择“逐步跟踪”选项。您会看到有一个创建新警报的选项(只能让您在CPU或MemoryUtilization中选择),或使用现有警报。

在“使用现有警报”字段中

键入 sqs-queue-depth-high 并按Enter,您应该看到一个绿色的选中标记,让您知道该名称有效(即,警报存在) 。您会看到新的下拉列表,您现在可以在其中调整步骤政策。

这适用于任何度量标准警报和ECS服务。如果您打算尝试扩展此设置,例如对于多个环境,或者使其变得比两个步骤都要复杂,请帮个忙,并使用CloudFormation或Terraform来帮助管理它。没有比调整10个服务的5步警报更糟糕的了。

答案 1 :(得分:3)

更新到 2021 年(可能之前……)

对于那些需要它但在 CDK 中的人

用例示例:

// Create the vpc and cluster used by the queue processing service
const vpc = new ec2.Vpc(stack, 'Vpc', { maxAzs: 2 });
const cluster = new ecs.Cluster(stack, 'FargateCluster', { vpc });
const queue = new sqs.Queue(stack, 'ProcessingQueue', {
  QueueName: 'FargateEventQueue'
});

// Create the queue processing service
new QueueProcessingFargateService(stack, 'QueueProcessingFargateService', {
  cluster,
  image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
  desiredTaskCount: 2,
  maxScalingCapacity: 5,
  queue
});

来自:

https://github.com/aws/aws-cdk/blob/master/design/aws-ecs/aws-ecs-autoscaling-queue-worker.md

答案 2 :(得分:2)

AWS提供了一种基于SQS队列扩展的解决方案:https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-using-sqs-queue.html

主要思想

  1. 使用以下公式创建CloudWatch自定义指标sqs-backlog-per-tasksqs-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-用于缩放优化和调试

CloudFormation堆栈(infrastructure.yml)中AWS SAM语法中描述的AWS Lambda函数:

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 Lambda Javascript代码(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模板中创建了Target Tracking Scaling策略。

基于sqs-backlog-per-task指标(infrastructure.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会创建和管理CloudWatch警报,这些警报会触发缩放策略并根据指标和目标值计算缩放调整。缩放策略会根据需要添加或删除容量,以将指标保持在指定的目标值或接近指定的目标值。除了使指标接近目标值外,目标跟踪缩放策略还根据负载模式的变化来调整指标的变化。

答案 3 :(得分:1)

我写了一篇有关该主题的博客文章,其中包括运行它的docker容器。 可以在以下位置找到该文章: https://allaboutaws.com/how-to-auto-scale-aws-ecs-containers-sqs-queue-metrics

可在DockerHub上使用prebuild容器: https://hub.docker.com/r/sh39sxn/ecs-autoscaling-sqs-metrics

文件在GitHub上可用: https://github.com/sh39sxn/ecs-autoscaling-sqs-metrics

希望对您有帮助。