web-dev-qa-db-ja.com

SQS ApproximateNumberOfMessagesVisibleに基づく自動スケールFargateサービス

SQSキューのサイズに基づいてaws fargateコンテナーをスケールアウトしたいと思います。コンテナーのCPUまたはメモリの使用量に基づいてのみスケーリングできるようです。キューのサイズに基づいてスケールアウトおよびインするポリシーを作成する方法はありますか?他のクラウドウォッチメトリックに基づいてスケーリングできる人はいますか?

10
quasar

はい、できます。ステップスケーリングポリシーを使用する必要があり、SQSキューの深さ(ApproximateNumberOfMessagesVisible)に対して既にアラームが作成されている必要があります。

CloudWatchに移動し、新しいアラームを作成します。このアラームをsqs-queue-depth-highと呼び、表示されるメッセージのおおよその数が1000になったときにトリガーします。

これが完了したら、ECSに移動して、自動スケーリングするサービスに移動します。サービスの[更新]をクリックします。スケーリングポリシーを追加し、ステップトラッキングの種類を選択します。新しいアラームを作成するオプション(CPUまたはMemoryUtilizationのどちらかのみを選択できる)があるか、既存のアラームを使用するオプションがあることがわかります。

「Use existing alarm」フィールドにsqs-queue-depth-highと入力してEnterキーを押すと、名前が有効であることを知らせる緑色のチェックマークが表示されます(つまり、アラームが存在します)。ステップポリシーを今すぐ調整できる新しいドロップダウンが表示されます。

これは、すべてのメトリックアラームおよびECSサービスで機能します。たとえば、複数の環境でこのセットアップをスケールアウトしようとしている場合、または2つのステップよりも高度なものにしたい場合は、自分で試して、CloudFormationまたはTerraformを使用して管理を支援してください。 5つのステップのアラームを10のサービスにわたって調整する必要があることほど悪いことはありません。

16
bluescores

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スタックで記述されています。したがって、カスタム指標を計算してログに記録するために、CloudFormationスタックにも記述され、インフラストラクチャ全体と一緒にデプロイされたAWS Lambda関数を使用することにしました。

以下に、次のカスタム指標をログに記録するためのAWS Lambda関数のコードスニペットを示します。

  • sqs-backlog-per-task-スケーリングに使用
  • running-task-number-スケーリングの最適化とデバッグに使用されます

CloudFormationスタックのAWS SAM構文で説明されているAWS Lambda関数(infrastructure.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 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メトリックに基づいて、クラウドフォーメーションテンプレートにターゲットトラッキングスケーリングポリシーを作成しました。

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アラームを作成および管理し、メトリクスとターゲット値に基づいてスケーリング調整を計算します。スケーリングポリシーは、必要に応じて容量を追加または削除し、メトリックを指定されたターゲット値に、またはその近くに維持します。指標をターゲット値に近づけるだけでなく、ターゲットトラッキングスケーリングポリシーは、負荷パターンの変化による指標の変化にも対応します。

9

私は、このトピックを正確に実行するためのDockerコンテナーを含むブログ記事を書きました。記事は次の場所にあります https://allaboutaws.com/how-to-auto-scale-aws-ecs-containers-sqs-queue-metrics

事前ビルドコンテナーは、DockerHubで入手できます。 https://hub.docker.com/r/sh39sxn/ecs-autoscaling-sqs-metrics

ファイルはGitHubで入手できます: https://github.com/sh39sxn/ecs-autoscaling-sqs-metrics

お役に立てば幸いです。

1
bsj4sla