AWS Lambda Container Reuseのベストプラクティス

AWS Lambdaを他のサービスに接続するときのウォームスタートの最適化

AWS Lambdaはサーバーレスでステートレスであるため高いスケーラビリティを提供し、ラムダ関数の多くのコピーを瞬時に生成できます(ここで説明します)。ただし、アプリケーションコードを記述するときは、いくつかのステートフルデータへのアクセスが必要になる場合があります。これは、RDSインスタンスやS3などのデータストアに接続することを意味します。ただし、AWS Lambdaから他のサービスに接続すると、関数コードに時間がかかります。また、RDSインスタンスへの許可された接続の最大数に到達するなど、スケーラビリティが高いことによる副作用もあります。これに対処する1つのオプションは、AWS Lambdaでコンテナーの再利用を使用して接続を維持し、ラムダの実行時間を短縮することです。

ラムダリクエストのライフサイクルを説明するための便利な図がここにあります。

コールドスタート中、関数が初めて呼び出されたとき、または非アクティブな期間が経過した後に、次のことが発生します。

  • コードと依存関係がダウンロードされます。
  • 新しいコンテナが開始されます。
  • ランタイムはブートストラップされます。

最後のアクションは、ラムダ関数が呼び出されるたびに発生するコードを開始することです。コンテナがラムダ関数の後続の呼び出しに再利用される場合、コードの開始までスキップできます。これはウォームスタートと呼ばれます。これは、ハンドラメソッドのスコープ外で接続を定義することにより、他のサービスに接続するときに最適化できるステップです。

Lambdaから他のAWSサービスに接続する

例:RDSインスタンスに接続し、AWSアイコンはここから取得

実行する基本的で一般的な例があります。コンテナリソースに接続して、強化データを取得します。この例では、JSONペイロードにIDが含まれており、Lambda関数はPostgreSQLを実行しているRDSインスタンスに接続してIDの対応する名前を検索し、強化されたペイロードを返すことができます。ラムダ関数はVPCに存在するRDSに接続しているため、ラムダ関数はプライベートサブネットにも存在する必要があります。これにより、コールドスタートにいくつかの手順が追加されます。VPCエラスティックネットワークインターフェイス(ENI)を接続する必要があります(ジェレミーデーリーのブログで述べたように、コールドスタートに時間がかかります)。

注:RDSの代わりにDynamoDBでキー/値ストレージを使用する場合、VPCの使用を避けることができます。

このタスクの2つのソリューションを検討します。1つ目は「単純な」ソリューションで、2つ目のソリューションは、以降の呼び出しで接続を再利用することでウォームスタート時間を最適化します。次に、各ソリューションのパフォーマンスを比較します。

オプション1 —ハンドラー内でRDSに接続する

このコード例は、このタスクにナイーブにアプローチする方法を示しています。データベース接続はハンドラーメソッド内にあります。ペイロードを返す前にIDの名前を取得する単純な選択クエリがあります。これには名前が含まれています。

同時実行数20のバースト2000回の小規模テストでこのオプションがどのように機能するかを見てみましょう。最小期間は18ミリ秒で、平均は51ミリ秒で、最大1秒を超えます(コールドスタート期間)。

ラムダ期間

以下のグラフは、データベースへの最大接続数が8つであることを示しています。

5分間のウィンドウ内のRDSデータベースへの接続数。

オプション2 —グローバル接続を使用する

2番目のオプションは、接続をハンドラーメソッドの外部のグローバルとして定義することです。次に、ハンドラー内に、接続が存在するかどうかを確認するチェックを追加し、存在しない場合のみ接続します。これは、コンテナごとに1回だけ接続が行われることを意味します。このように条件を設定して接続を設定すると、コードロジックで必要でない場合は接続する必要がなくなります。

データベースへの接続を閉じなくなったため、その後の関数呼び出しのために接続は維持されます。接続を再利用すると、ウォームスタート期間が大幅に短縮されます。平均期間は約3倍速くなり、最小値は18ミリ秒ではなく1ミリ秒になります。

ラムダ期間

RDSインスタンスへの接続は時間のかかるタスクであり、呼び出しごとに接続する必要がないことはパフォーマンスにとって有益です。単純なデータベースクエリでデータベースに接続する場合、同時接続のレベルと一致する最大データベース接続数20を達成します(20回の同時呼び出しx 100回を行いました)。呼び出しのバーストが停止すると、接続は徐々に閉じます。

AWSがラムダ期間の許容値を15分に増やしたため、これはデータベース接続が長く続く可能性があり、RDSの最大接続数に達する危険があることを意味します。デフォルトの最大接続は、RDSパラメータグループ設定で上書きできますが、接続の最大数を増やすと、メモリの割り当てに問題が生じる可能性があります。小さいインスタンスのデフォルトのmax_connections値は100未満です。これらの制限に注意し、必要なときにデータベースに接続するためのアプリケーションロジックのみを追加してください。

他のタスクにグローバル接続を使用する

S3に接続するLambda

Lambdaで実行する必要がある一般的なタスクは、S3からステートフルデータにアクセスすることです。以下のコードスニペットは、AWSが提供するPython Lambda Functionブループリントです。AWSコンソールにログインしてここをクリックすると、ナビゲートできます。 RDSの例では、ハンドラーの内部でグローバル接続が設定されているのに対し、コンテナーでは、S3クライアントがハンドラーの外部で完全に定義されていることがコードでわかります。どちらのアプローチもグローバル変数を設定し、後続の呼び出しで使用できるようにします。

s3-get-object lambdaブループリントコードスニペットhttps://console.aws.amazon.com/lambda/home?region=us-east-1#/create/new?bp=s3-get-object-python

環境変数の復号化

ラムダコンソールには、セキュリティを強化するために環境変数を暗号化するオプションがあります。次のコードスニペットは、AWSが提供するLambda関数から環境変数を復号化するヘルパースクリプトのJavaの例です。このチュートリアル(特に手順6)に従って、コードスニペットに移動できます。 DECRYPTED_KEYはクラスグローバルとして定義されているため、decryptKey()関数とロジックはラムダコンテナーごとに1回だけ呼び出されます。したがって、ウォームスタート期間が大幅に改善されます。

https://console.aws.amazon.com/lambda/home?region=us-east-1#/functionsおよびhttps://docs.aws.amazon.com/lambda/latest/dg/tutorial-env_console.html

他のFaaSソリューションでグローバル変数を使用する

このアプローチはAWS Lambdaに限定されていません。グローバル接続を使用する方法は、他のクラウドプロバイダーのサーバーレス機能にも適用できます。 Google Cloud Functionsのヒントとコツのページでは、非遅延変数(変数が常にハンドラーメソッドの外側で初期化される場合)と遅延変数(グローバル変数は必要な場合にのみ設定される)グローバル変数について適切な説明があります。

その他のベストプラクティス

留意すべきその他のベストプラクティスを次に示します。

テスト中

FaaSを使用すると、マイクロサービスアーキテクチャの構築が容易になります。また、機能の小さな個別の部分を持つことは、効果的な単体テストと密接に関連しています。単体テストを支援するには:

  • ラムダパッケージからテストの依存関係を除外することを忘れないでください。
  • プログラムのメインメソッドの場合と同様に、ハンドラメソッドからロジックを分離します。

依存関係とパッケージサイズ

展開パッケージのサイズを小さくすると、コードのダウンロードが初期化時に速くなり、コールドスタート時間が改善されます。未使用のライブラリとデッドコードを削除して、展開ZIPファイルのサイズを小さくします。 AWS SDKはPythonおよびJavaScriptランタイム用に提供されているため、デプロイパッケージに含める必要はありません。

Node.jsが優先Lambdaランタイムである場合、関数コードのサイズを縮小し、展開パッケージのサイズを最小化するために、縮小化とu化を適用できます。縮小化とu化のすべてではありませんが、いくつかの側面を他のランタイムに適用できます。 Pythonコードから空白を削除することはできませんが、コメントを削除して変数名を短くすることはできます。

メモリを設定する

ラムダ関数の最適なメモリ量を見つけるための実験。メモリの割り当てに対して料金を支払うため、メモリを2倍にすると、1ミリ秒あたり2倍の料金がかかります。ただし、割り当てられたメモリに応じて計算容量が増加するため、実行時間が半分以下に短縮される可能性があります。このような最適なメモリ設定を選択するための便利なツールがすでにいくつかあります。

最後に…

考慮すべき1つのことは、接続の再利用方法を適用する必要があるかどうかです。ラムダ関数が1日に1回など、まれにしか呼び出されない場合、ウォームスタート用に最適化してもメリットはありません。多くの場合、パフォーマンスの最適化とコードの読みやすさの間でトレードオフがあります。「不正化」という用語はそれ自体を表しています。さらに、グローバル変数をコードに追加して他のサービスへの接続を再利用すると、コードのトレースが困難になる可能性があります。 2つの質問が思い浮かびます。

  • 新しいチームメンバーはコードを理解しますか?
  • あなたとあなたのチームは将来、コードをデバッグできますか?

しかし、その規模のためにLambdaを選択し、高いパフォーマンスと低コストを望んでいる可能性があるので、チームのニーズに合ったバランスを見つけてください。

これらの意見は著者のものです。この投稿で特に断りのない限り、Capital Oneは言及された企業のいずれとも提携しておらず、承認されていません。使用または表示されるすべての商標およびその他の知的財産は、それぞれの所有者の所有物です。この記事は©2019 Capital Oneです。