Lambda Provisioned ConcurrencyのオートスケーリングをCDKで設定してみた(ターゲット追跡編)

Lambda Provisioned ConcurrencyのオートスケーリングをCDKで設定してみた(ターゲット追跡編)
この記事をシェアする

はじめに

こんにちは、スカイアーチHRソリューションズのsugawaraです。

前回の記事『Lambda Provisioned ConcurrencyのオートスケーリングをCDKで設定してみた』では、スケジュールされたProvisioned Concurrencyを設定しました。今回はターゲット追跡のProvisioned ConcurrencyをCDKで設定してきます。

ターゲット追跡とは、Application Auto Scalingを用いてスケーリングポリシーを定義し、 それに基づいてCloudWatch アラームを作成するLambdaのオートスケールの一種です。Provisioned Concurrencyの使用率やリクエスト数についてのメトリクスでアラームを作成できます。アラームがアクティブになると、Application Auto Scaling はProvisioned Concurrencyの割り当てられる数を自動的に調整します。トラフィックパターンが予測できないようなときに有効なアプローチです。

こちらに関してもコンソール画面からの操作は対応しておらず、現状はCLIで実行するようになっています。
そのため、今回もCDKで設定したいと思います。

なぜProvisioned Concurrencyが必要かなどは、以前の記事を見ていただければと思います。

環境

  • WSL2
  • CDK 2.138.1
  • Node.js 20.12.1

構築の流れ

CDKの初期化

まずWSL上に作業フォルダtarget_track_provisioned_concurrencyを作成し、そのディレクトリにてCDKの準備をします。

$ mkdir lambda_target_track_provisioned_concurrency
$ cd lambda_target_track_provisioned_concurrency
$ cdk init app --language typescript

作成されたフォルダは下記のような構成になっています。

├── bin
│   └── lambda_target_track_provisioned_concurrency.ts  // ここにenvを定義
├── cdk.json
├── jest.config.js
├── lib
│   └── lambda_target_track_provisioned_concurrency.ts  // ここにAWSリソースを定義
├── package.json
├── package-lock.json
├── README.md
├── test
│   └── lambda_target_track_provisioned_concurrency.test.ts
└── tsconfig.json

CDKコードの全体像

下記がスタックファイルの全体となります。今回はバージョンに加えてエイリアスも指定しています。そのため、エイリアスに対してオートスケーリングの設定をしています。なお、エイリアス部分はcdk deploy時に失敗してしまうため、addDependencyでデプロイの順序に依存関係を追加しています。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as applicationautoscaling from 'aws-cdk-lib/aws-applicationautoscaling'

export class LambdaTargetTrackProvisionedConcurrencyStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const lambdaFunction = new lambda.Function(this, 'LambdaFunction', {
      functionName: "TargetTrackingProvisionedConcurrencyTestLambda",
      description: "ターゲット追跡型のプロビジョニング済み同時実行の検証用Lambda",
      runtime: lambda.Runtime.PYTHON_3_12,
      handler: 'lambda_function.lambda_handler',
      code: lambda.Code.fromAsset(`../target_tracking_provisioned_concurrency/lib`),
      timeout: cdk.Duration.seconds(600),
      memorySize: 256,
    });

    // バージョンの設定
    const lambdaVersion = new lambda.Version(this, 'LambdaVersion', {
      lambda: lambdaFunction,
      description: 'Version for Target Tracking Provisioned Concurrency',
    });

    // エイリアスの設定
    const alias = new lambda.Alias(this, 'LambdaAlias', {
      aliasName: 'Load-Test',
      version: lambdaVersion,
    });

    // オートスケーリングのターゲットの設定
    const scalingTarget = new applicationautoscaling.ScalableTarget(this, 'ScalableTarget', {
      serviceNamespace: applicationautoscaling.ServiceNamespace.LAMBDA,
      minCapacity: 1,
      maxCapacity: 50,
      resourceId: `function:${lambdaFunction.functionName}:${alias.aliasName}`,
      scalableDimension: 'lambda:function:ProvisionedConcurrency',
    });

    // デプロイ順序の依存関係を追加
    scalingTarget.node.addDependency(alias);

    // ターゲット追跡スケーリングの追加
    scalingTarget.scaleToTrackMetric('TargetTracking', {
      targetValue: 0.5,
      predefinedMetric: applicationautoscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION,
      scaleInCooldown: cdk.Duration.seconds(10),
      scaleOutCooldown: cdk.Duration.seconds(10),
    });
  }

ターゲット追跡のオートスケーリングですが、今回はProvisioned Concurrencyの使用率50%でオートスケールする設定にしています。

    // ターゲット追跡スケーリングの追加
    scalingTarget.scaleToTrackMetric('TargetTracking', {
      targetValue: 0.5,
      predefinedMetric: applicationautoscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION,
      scaleInCooldown: cdk.Duration.seconds(10),
      scaleOutCooldown: cdk.Duration.seconds(10),
    });

また、libフォルダ配下には下記のlambda_function.pyを作成します。

import json

def lambda_handler(event, context):
  # TODO implement
  return {
    'statusCode': 200,
    'body': json.dumps('Hello from Lambda!')
  }

上記のCDKコードでデプロイします。lambda_target_track_provisioned_concurrencyディレクトリにて下記のコマンドを実行します。

$ cdk diff
$ cdk deploy

デプロイが完了したら、Lambdaコンソールにて設定の確認をします。エイリアスが設定されていることがわかります。

Application Auto Scaling設定はコンソール画面で確認ができないため、下記のCLIで見てみます。CDKのコードで書いたような設定になっています。また、CDKがCloudWatch Alarmもよしなにやってくれていることがわかります。

aws application-autoscaling describe-scaling-policies \
--service-namespace lambda
{
    "ScalingPolicies": [
        {
            "PolicyARN": "arn:aws:autoscaling:ap-northeast-1:008458347550:scalingPolicy:2cc56c12-0b71-429e-b39e-8fb4af70dd70:resource/lambda/function:TargetTrackingProvisionedConcurrencyTestLambda:Load-Test:policyName/LambdaTargetTrackProvisionedConcurrencyStackScalableTargetTargetTracking177ED8D9",
            "PolicyName": "LambdaTargetTrackProvisionedConcurrencyStackScalableTargetTargetTracking177ED8D9",
            "ServiceNamespace": "lambda",
            "ResourceId": "function:TargetTrackingProvisionedConcurrencyTestLambda:Load-Test",
            "ScalableDimension": "lambda:function:ProvisionedConcurrency",
            "PolicyType": "TargetTrackingScaling",
            "TargetTrackingScalingPolicyConfiguration": {
                "TargetValue": 0.5,
                "PredefinedMetricSpecification": {
                    "PredefinedMetricType": "LambdaProvisionedConcurrencyUtilization"
                },
                "ScaleOutCooldown": 10,
                "ScaleInCooldown": 10
            },
            "Alarms": [
                {
                    "AlarmName": "TargetTracking-function:TargetTrackingProvisionedConcurrencyTestLambda:Load-Test-AlarmHigh-b7f7064d-95a0-4a08-a5a3-ebf0f9e95b54",
                    "AlarmARN": "arn:aws:cloudwatch:ap-northeast-1:008458347550:alarm:TargetTracking-function:TargetTrackingProvisionedConcurrencyTestLambda:Load-Test-AlarmHigh-b7f7064d-95a0-4a08-a5a3-ebf0f9e95b54"
                },
                {
                    "AlarmName": "TargetTracking-function:TargetTrackingProvisionedConcurrencyTestLambda:Load-Test-AlarmLow-9c70af13-d33a-40bc-a882-3654e56c784c",
                    "AlarmARN": "arn:aws:cloudwatch:ap-northeast-1:008458347550:alarm:TargetTracking-function:TargetTrackingProvisionedConcurrencyTestLambda:Load-Test-AlarmLow-9c70af13-d33a-40bc-a882-3654e56c784c"
                }
            ],
            "CreationTime": "2024-05-10T16:22:12.630000+09:00"
        }
    ]
}

CloudWatch Alarmの詳細はコンソール画面から確認ができます。なお、デフォルトではスケールアウトは3分以内の3データポイント、スケールインは15分以内の15データポイントとなっているようです。

負荷をかけてみる

Provisioned Concurrencyの使用率に応じてオートスケールするのかどうかを確認するために、簡単に負荷をかけてみます。スクリプトを用意して、同時実行させてみます。

しばらくすると、下記のようにアラーム状態となります。

Lambdaのコンソール画面を見てみると、Provisioned Concurrencyの値が変更中になっているようです。

さらに少し待っていると、ステータス準備完了となりました。最終的にはProvisioned Concurrencyは4まで上がりました。これも時間が経てば徐々にスケールインされるはずです。

これでProvisioned Concurrencyの使用率に応じてオートスケールさせることができました!

おわりに

前回に引き続き、Lambda Provisioned Concurrencyのオートスケールを実施していきました。ターゲット追跡となると、実際にどのメトリクスを対象とし、しきい値はどのくらいにするかなどといったアラームの詳細の作り込みが必要そうです。

また、CloudWatchへメトリクスが流れるのがリアルタイムではないため、若干のタイムラグが気になるところです。ただ、急激なスパイクがないなどの場合には利用できるかもしれません。

一旦Lambdaのオートスケーリングはこれで終わりにしたいと思います!

この記事をシェアする
著者:sugawara
元高校英語教員。2023 Japan AWS All Certifications Engineers。IaCやCI/CD、Platform Engineeringに興味あり。