AWS Lambda

LambdaのProvisioned Concurrencyに対するApplication Auto Scalingをスケジュールに応じて上限下限を調整するステートマシンを作ってみた

はじめに

こんにちは、omkです。
AWS Summit Tokyo 2023面白かったですね。各社のブースや展示を見て回りましたが、すげーっと思わせる構成だったり自分もやってみたいと思わせるサービスだったりが盛り沢山でモチベーションが大変刺激されております(家に帰って即M5StackとAWS IoTをイジイジしてました)。

それはさておき、Lambdaの「Provisioned Concurrency」に対する「Application Auto Scaling」をスケジュールに応じて上限下限を調整するステートマシンを作ってみました。
内容を以下にまとめます。

Application Auto Scalingとは

LambdaなどのEC2以外のサービスの実行をスケールできます。
ターゲットを登録してターゲットの使用状況に応じてCloudWatchAlarmのアラーム発砲に基づいてスケールします。

詳しくは以下を参照ください。
https://docs.aws.amazon.com/ja_jp/autoscaling/application/userguide/what-is-application-auto-scaling.html

前提

以下の対応として「Provisioned Concurrency」に対する「Application Auto Scaling」を導入しています。

  • スパイクが想定される時刻に「Provisioned Concurrency」をの上限を増やす・一定数暖気しておく目的で上限・下限を設定する
  • それ以外の時刻は費用削減のために最低限の実行数に設定する

やってみた

以下のステートマシンが作成されました。

{
  "Comment": "A description of my state machine",
  "StartAt": "DescribeScalableTargets",
  "States": {
    "DescribeScalableTargets": {
      "Type": "Task",
      "Next": "RegisterScalableTarget",
      "Parameters": {
        "ServiceNamespace": "lambda",
        "ResourceIds": [
          "function:{ファンクション名}:{エイリアス}"
        ]
      },
      "Resource": "arn:aws:states:::aws-sdk:applicationautoscaling:describeScalableTargets",
      "ResultPath": "$.before",
      "ResultSelector": {
        "ScalableTarget.$": "$.ScalableTargets[0]"
      }
    },
    "RegisterScalableTarget": {
      "Type": "Task",
      "Parameters": {
        "ResourceId": "function:{ファンクション名}:{エイリアス}",
        "ScalableDimension": "lambda:function:ProvisionedConcurrency",
        "ServiceNamespace": "lambda",
        "MinCapacity.$": "$.min",
        "MaxCapacity.$": "$.max"
      },
      "Resource": "arn:aws:states:::aws-sdk:applicationautoscaling:registerScalableTarget",
      "Next": "Choice",
      "ResultPath": "$.after"
    },
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.min",
          "NumericLessThanPath": "$.before.ScalableTarget.MinCapacity",
          "Next": "PutProvisionedConcurrencyConfig"
        }
      ],
      "Default": "Success"
    },
    "PutProvisionedConcurrencyConfig": {
      "Type": "Task",
      "Parameters": {
        "FunctionName": "{ファンクション名}}",
        "ProvisionedConcurrentExecutions.$": "$.min",
        "Qualifier": "{エイリアス}"
      },
      "Resource": "arn:aws:states:::aws-sdk:lambda:putProvisionedConcurrencyConfig",
      "Next": "Success"
    },
    "Success": {
      "Type": "Succeed"
    }
  }
}

流れとしては以下です。

  • 現状の値を取得
  • Application Auto Scalingのターゲットを設定(変更)
  • 変更前の下限値と変更後(変更要求)の下限値を比較して前者が小さい場合はLambdaの同時実行数を下限値に設定

という流れです。
設定する上限値・下限値はステートマシンの実行時に以下の構成で引数として与えられる前提です。

{
    "min": n,
    "max": N
}

で、これをEventBridge Schedulerで特定の時刻に「StartExecution」でキックするように設定したら完成です。
ペイロードで { "min": 1, "max": 3 } のように値を渡します。

なぜなに

どのようにしてこの構成になったのかざっくり簡単に説明します。
同じことを検討される際の参考になれば幸いです。

なぜ「Application Auto Scaling」の「Scheduled Action」を用いないか

Application Auto ScalingにはScheduled Actionという機能があり、今回つくったものと大体同じことができるものが用意されています。

ただし、Application Auto Scalingはコンソールパネルにその項目が存在しないため、基本的にリソースはCLIで確認することになります。
Scheduled Actionを設定したとしても存在を見落とす可能性が高く、設定変更も容易でないと考えたため、存在がわかりやすく、パラメータを渡しやすいEventBridge Schedulerを採用しました。

なぜ「EventBridge Scheduler」だけで完結させないか

EventBridge Schedulerを利用すればユニバーサルターゲットで簡単にAPIをコール出来ます。
「Application Auto Scaling」の「RegisterScalableTarget」もその対象であるため、EventBridge Scheduler単体でも上下限を調整することは可能です。

わざわざ「Step Functions」を利用した理由としては、「RegisterScalableTarget」で上限と下限の値を設定した際に、現在の「Provisioned Concurrency」の値が下限上限の範囲外の場合は自動で値が変更されますが、範囲内の場合はそのままであるため、文字通り上限と下限が変更されただけで実際に「Provisioned Concurrency」が変更されず調整する意味が薄れるためです。

今回のケースにおいて下限を下げるということの目的は費用の削減であったため、実際に同時実行数が下がらないと目的を達成できませんでした。
そのため、Application Auto Scalingに対して上限下限を変更するAPIとLambdaの同時実行数を変更するAPIの2種類を操作する必要があり、EventBridge Schedulerでは1つしかAPIをコール出来ないため、Step Functionsを利用することとなりました。

単純に上限下限を設定するだけであれば「EventBridge Scheduler」だけで可能です。
また、時間が来たらいきなり下限に設定されることで困るケースもあると思いますのでそのあたりは注意です。

おわりに

この件に関してはまだ痒いところに手が届かない感じがありますが、EventBridge SchedulerとStep Functionsのおかげでなにかとやりやすくなってますね。助かる~。

以上、最後までお付き合いありがとうございました。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA