Traffine I/O

日本語

2022-09-13

AWS LambdaにおけるProvisioned Concurrency

AWS LambdaにおけるProvisioned Concurrency

Provisioned Concurrencyは、AWS Lambdaの機能の1つであり、Lambda関数の特定数のインスタンスを事前にウォームアップすることで、コールドスタートに関連するレイテンシを低減することができます。コールドスタートとは、Lambda関数が初めて初期化されるとき、または利用可能なインスタンス数を超える急激なリクエストのスパイクが発生したときに発生します。Provisioned Concurrencyを使用すると、プリウォームされたインスタンスのプールを維持して、コールドスタートのレイテンシを最小限に抑え、一貫した高速な応答時間を確保することができます。

AWS LambdaでProvisioned Concurrencyを使用する主な利点は次のとおりです。

  • レイテンシの低減
    Lambda関数のインスタンスを事前にウォームアップすることで、リクエストの処理時間が大幅に短縮され、より高速で一貫したユーザーエクスペリエンスを提供できます。

  • 予測可能なパフォーマンス
    Provisioned Concurrencyは、アプリケーションがリクエストを処理するための一定のリソースプールを確保することを保証し、オンデマンドでスケールする必要がなく、予測可能なパフォーマンスレベルを提供します。

  • より良いコントロール
    アプリケーションの要件に基づいて事前にウォームアップされたインスタンス数を構成できるため、アプリケーションのパフォーマンスとコストをより良くコントロールできます。

いつProvisioned Concurrencyを使うか

Provisioned Concurrencyは、低遅延のレスポンスが必要で、予測可能な負荷パターンを持つアプリケーションにもっとも有益です。次のような場合に使用できます。

  • インタラクティブアプリケーション
    ユーザーがスナッピーで一貫した応答時間を期待するWebアプリケーションやモバイルアプリケーションなどのアプリケーションは、Provisioned Concurrencyを利用することで大きな恩恵を受けることができます。

  • スケジュールされたジョブ
    Lambda関数をスケジュールされたジョブまたはcronタスクとして実行している場合、Provisioned Concurrencyを使用してトリガーされた際にすぐに実行されるようにすることができます。

  • 高スループットサービス
    低遅延で大量のリクエストを処理する必要があるサービスにとっては、Provisioned Concurrencyが一定のパフォーマンスレベルを維持するのに役立ちます。

ただし、Provisioned Concurrencyは全てのシナリオに適しているわけではないことに注意する必要があります。特にアプリケーションの負荷が非常に変動する場合や予測不可能な場合には、コストの増加につながる可能性があるため、慎重に検討する必要があります。

プライシングモデルの理解

Provisioned Concurrencyを使用する場合、設定した事前ウォームアップインスタンス数と、それらがプロビジョニングされた期間に基づいて請求されます。次のコンポーネントを使用してコストを計算します。

  • Provisioned Concurrency
    設定したプロビジョニングされたインスタンス数に基づいて請求されます。これらが使用されているかどうかにかかわらず、請求されます。

  • Duration
    プロビジョニングされたインスタンスの総期間に対して、GB-秒単位で請求されます。これは、関数のメモリサイズ、プロビジョニングされたインスタンス数、およびプロビジョニングされた期間の積で計算されます。

  • Requests
    Lambda関数で処理されたリクエストの数に対して、オンデマンドのインスタンスとプロビジョニングされたインスタンスの両方が含まれます。

Provisioned Concurrencyを実装する前に、プライシングの影響を注意深く考慮することが重要です。コストを最適化するためには、アプリケーションの負荷パターンを分析し、適切な数の事前ウォームアップインスタンスを設定する必要があります。未使用のインスタンスが多すぎるとコストが増加することに注意し、インスタンスが少なすぎるとコールドスタートやレイテンシの増加が発生する可能性があります。

月額料金例

Lambda関数のメモリサイズが1.5GBで、Provisioned Concurrencyのインスタンス数が20台設定されているとします。関数は1か月間(30日間)プロビジョンされ、その間に2,500万のリクエストを処理します。

この例では、US East(N. Virginia)リージョンの価格を使用し、Provisioned ConcurrencyのGB-時間あたりの料金は$0.015、リクエストあたりの料金は$0.20とします。

  • Provisioned Concurrencyのコスト: Provisioned Concurrencyの総コストを計算するには、まずGB-時間を求める必要があります。1つのインスタンスが1.5GBのメモリを持ち、30日間に20台のインスタンスがプロビジョンされているため、次のように計算します。
    • GB-時間 = 1.5GB * 20インスタンス * 1日あたりの24時間 * 30日 = 21600 GB-時間
    • Provisioned Concurrencyのコスト = 21600 GB-時間 * $0.015/GB-時間 = $324
  • リクエストのコスト: 2,500万のリクエストに対するコストは次のとおりです。
    • リクエストのコスト = 2,500万リクエスト * ($0.20/1百万リクエスト) = $5

この例の総月額料金は、$329(Provisioned Concurrencyのコスト + リクエストのコスト)になります。

Provisioned Concurrencyの設定

AWSコンソールでのProvisioned Concurrencyの設定

AWS Management Consoleを使用してLambda関数のProvisioned Concurrencyを設定するには、次の手順に従います。

  1. AWS Management Consoleに移動し、アカウントにサインイン
  2. 検索バーで「Lambda」を検索するか、「Services」メニューからLambdaサービスを開く
  3. リストから目的のLambda関数を見つけ、その名前をクリックして関数構成ページを開く
  4. 「Function Configuration」パネルで、「Versions」タブを選択
  5. Provisioned Concurrencyを有効にするLambda関数のバージョンまたはエイリアスを選択
  6. 「Provisioned Concurrency」セクションまでスクロールし、「Edit」をクリック
  7. 「Provisioned Concurrency」入力フィールドに設定したいプロビジョニングされたインスタンスの数を入力
  8. 変更を適用するために「Save」をクリック

設定が保存されると、AWSは指定された数のインスタンスを事前ウォームアップしてLambda関数に提供します。

AWS CLIおよびSDKの使用

AWS CLIまたはSDKを使用してProvisioned Concurrencyを設定することもできます。AWS CLIを使用してProvisioned Concurrencyを設定するには、次のコマンドを実行します。

bash
$ aws lambda put-provisioned-concurrency-config \
  --function-name <FUNCTION_NAME> \
  --qualifier <VERSION_OR_ALIAS> \
  --provisioned-concurrent-executions <NUMBER_OF_INSTANCES>

<FUNCTION_NAME>をLambda関数の名前、<VERSION_OR_ALIAS>を設定したいバージョンまたはエイリアス、<NUMBER_OF_INSTANCES>を設定したいプロビジョニングされたインスタンスの数に置き換えてください。

SDKを使用してProvisioned Concurrencyを設定する場合は、好みのプログラミング言語のSDKの関連ドキュメントを参照してください。

IaCのコードでのProvisioned Concurrencyの管理

AWS CloudFormationやTerraformなどのInfrastructure as Code (IaC)ツールを使用すると、Provisioned Concurrencyを含め、AWSリソースの設定と自動化を管理することができます。AWS CloudFormationを使用してProvisioned Concurrencyを設定する例を以下に示します。

yml
Resources:
  MyLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      # Function properties...

  MyLambdaFunctionProvisionedConcurrency:
    Type: AWS::Lambda::ProvisionedConcurrencyConfig
    Properties:
      FunctionName: !Ref MyLambdaFunction
      Qualifier: <VERSION_OR_ALIAS>
      ProvisionedConcurrentExecutions: <NUMBER_OF_INSTANCES>

<VERSION_OR_ALIAS>を設定したいバージョンまたはエイリアス、<NUMBER_OF_INSTANCES>を設定したいプロビジョニングされたインスタンスの数に置き換えてください。

Terraformでは、aws_lambda_provisioned_concurrency_configリソースを使用してProvisioned Concurrencyを設定できます。

tf
resource "aws_lambda_function" "my_lambda_function" {
  # Function properties...
}

resource "aws_lambda_provisioned_concurrency_config" "my_lambda_function_provisioned_concurrency" {
  function_name                     = aws_lambda_function.my_lambda_function.function_name
  provisioned_concurrent_executions = <NUMBER_OF_INSTANCES>
  qualifier                         = <VERSION_OR_ALIAS>
}

<VERSION_OR_ALIAS>を設定したいバージョンまたはエイリアス、<NUMBER_OF_INSTANCES>を設定したいプロビジョニングされたインスタンスの数に置き換えてください。

グローバルスコープの効果の比較

AWS Lambdaでは、グローバルスコープとは、Lambda関数ハンドラーの外側で定義された変数、オブジェクト、またはリソースを指します。グローバルスコープ変数は、Lambda関数コンテナが作成されたときに初期化され、同じコンテナの複数の呼び出しで再利用されます。特にProvisioned Concurrencyを使用する場合、グローバルスコープはLambda関数のパフォーマンスに影響を与える可能性があります。

例を用いてグローバルスコープの効果を比較します。

python
import boto3

# Global scope variable
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("MyTable")

def lambda_handler(event, context):
    # Function scope variable
    s3 = boto3.client("s3")

    # Function logic...

この例では、dynamodbリソースとtable変数はグローバルスコープにあり、s3クライアントはファンクションスコープにあります。Provisioned Concurrencyを使用する場合、グローバルスコープ変数はコンテナが作成されたときに一度だけ初期化されます。これは、DynamoDBリソースとテーブルが同じコンテナの全ての呼び出しで利用可能であることを意味し、後続の呼び出しの初期化オーバーヘッドが減少します。

一方、s3クライアントは各呼び出しで初期化されるため、レイテンシが増加する可能性があります。ただし、頻度が低いまたは低スループットの場合、これは無視できる可能性があります。

静的初期化とProvisioned Concurrency

静的初期化とは、Lambda関数コンテナの起動フェーズでリソース、モジュール、またはデータを初期化するプロセスを指します。これは、複雑なアプリケーションやリソース集約型アプリケーションの場合、コールドスタートのレイテンシに重大な影響を与える可能性があります。

Provisioned Concurrencyを使用する場合、静的初期化はプリウォームされたインスタンスが作成されたときに行われます。これにより、Lambda関数はリソース、モジュール、およびデータがすでに初期化されている状態で開始され、各呼び出しのレイテンシが低下します。

機械学習モデルをロードするために静的初期化を使用する例を考えてみます。

python
import json
import boto3
import numpy as np
from sklearn.externals import joblib

# Static initialization
s3 = boto3.client("s3")
s3.download_file("my-bucket", "models/model.pkl", "/tmp/model.pkl")
model = joblib.load("/tmp/model.pkl")

def lambda_handler(event, context):
    # Function logic
    input_data = np.array(json.loads(event["body"])["input"])
    prediction = model.predict(input_data)

    return {
        "statusCode": 200,
        "body": json.dumps({"prediction": prediction.tolist()})
    }

この例では、機械学習モデルをダウンロードしてスタティック初期化フェーズでロードしています。Provisioned Concurrencyを使用する場合、モデルはすでにロードされ、全ての呼び出しで使用できるため、返信時間が短くなります。

Provisioned Concurrencyを使用したバージョンまたはエイリアスでLambda関数を呼び出す

Provisioned Concurrencyの利点を得るには、プリウォームされたインスタンスが設定されたLambda関数の特定のバージョンまたはエイリアスを呼び出す必要があります。この章では、Provisioned Concurrencyに設定したバージョンまたはエイリアスを使用してLambda関数を呼び出す方法について説明します。

バージョンを使用してLambda関数を呼び出す

Lambda関数のバージョンは、関数のコードと構成の不変のスナップショットです。Lambda関数の特定のバージョンを呼び出すには、:version_number構文を使用して関数名にバージョン番号を追加する必要があります。

以下はAWS CLIを使用した特定のバージョンを呼び出す例です。

bash
$ aws lambda invoke --function-name <FUNCTION_NAME>:<VERSION_NUMBER> --payload '{"key": "value"}' response.json

<FUNCTION_NAME>をLambda関数の名前に、<VERSION_NUMBER>をProvisioned Concurrencyを構成したバージョン番号に置き換えてください。

エイリアスを使用してLambda関数を呼び出す

エイリアスとは、特定の関数バージョンへの名前付き参照です。エイリアスを使用することで、関数呼び出しとデプロイのプロセスを簡素化できます。Provisioned ConcurrencyのあるLambda関数のバージョンにエイリアスを作成し、新しいバージョンに切り替える場合はエイリアスを更新できます。

以下はAWS CLIを使用してエイリアスを使用してLambda関数を呼び出す例です。

bash
$ aws lambda invoke --function-name <FUNCTION_NAME>:<ALIAS_NAME> --payload '{"key": "value"}' response.json

<FUNCTION_NAME>をLambda関数の名前に、<ALIAS_NAME>をProvisioned Concurrencyを構成したエイリアス名に置き換えてください。

参考

https://docs.aws.amazon.com/lambda/latest/dg/provisioned-concurrency.html
https://docs.aws.amazon.com/lambda/latest/operatorguide/global-scope.html
https://www.youtube.com/watch?v=7Bc97tAySkU&ab_channel=CloudWithRaj

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!