AWS Lambda 関数を使用するためのベストプラクティス - AWS Lambda

AWS Lambda 関数を使用するためのベストプラクティス

AWS Lambda の使用時に推奨されるベストプラクティスを以下に示します。

Lambda アプリケーションのベストプラクティスの詳細については、Serverless Land の「アプリケーション設計」を参照してください。AWS アカウントチームに連絡し、アーキテクチャのレビューをリクエストすることもできます。

関数コード

  • Lambda ハンドラーをコアロジックから分離します。これにより、関数の単体テストが実行しやすくなります。Node.js では、次のようになります。

    exports.myHandler = function(event, context, callback) { var foo = event.foo; var bar = event.bar; var result = MyLambdaFunction (foo, bar); callback(null, result); } function MyLambdaFunction (foo, bar) { // MyLambdaFunction logic here }
  • 実行環境の再利用を活用して関数のパフォーマンスを向上させます。関数ハンドラー外で SDK クライアントとデータベース接続を初期化し、静的なアセットを /tmp ディレクトリにローカルにキャッシュします。関数の同じインスタンスで処理された後続の呼び出しは、これらのリソースを再利用できます。これにより、関数の実行時間が短縮され、コストが節約されます。

    呼び出し間でデータが漏れるのを防ぐため、実行環境を使用してセキュリティ上の懸念があるユーザーデータ、イベント、またはその他の情報を保存しないでください。関数がハンドラー内のメモリに保存できない変更可能な状態に依存している場合は、ユーザーごとに個別の関数または個別のバージョンの関数を作成することを検討してください。

  • keep-alive ディレクティブを使用して永続的な接続を維持します。Lambda は、時間の経過とともにアイドル状態の接続を消去します。関数を呼び出すときにアイドル状態の接続を再利用しようとすると、接続エラーが発生します。永続的な接続を維持するには、ランタイムに関連付けられている keep-alive ディレクティブを使用します。例については、「Node.js で Keep-alive を使用して接続を再利用する」を参照してください。

  • 環境変数を使用して、オペレーショナルパラメータを関数に渡します。たとえば、Amazon S3 バケットに書き込む場合、書き込み先のバケット名はハードコーディングせずに、環境変数として設定します。

  • 関数のデプロイパッケージ内で依存関係を制御します。AWS Lambda 実行環境には、AWS SDK for the Node.js および Python ランタイムなどのライブラリが多数含まれています (詳細なリストについては、Lambda ランタイム を参照してください)。最新の機能やセキュリティ更新プログラムを有効にするために、Lambda はこれらのライブラリを定期的に更新します。この更新により、Lambda 関数の動作が微妙に変化する場合があります。関数で使用する依存関係を完全に制御するには、すべての依存関係をデプロイパッケージでパッケージングします。

  • デプロイパッケージをランタイムに必要な最小限のサイズにします。これにより、呼び出しに先立ってデプロイパッケージをダウンロードして解凍する所要時間が短縮されます。Java または .NET Core で作成した関数の場合は、デプロイパッケージの一環として AWS SDK ライブラリ全体をアップロードしないようにします。代わりに、SDK のコンポーネントを必要に応じて選別するモジュール (DynamoDB、Amazon S3 SDK モジュール、Lambda コアライブラリなど) を使用します。

  • Java で記述されたデプロイパッケージを Lambda で解凍する所要時間を短縮します。そのために、依存する .jar ファイルを別個の /lib ディレクトリにファイルします。これで関数のすべてのコードを多数の .class ファイルと一緒に単一の Jar に収納するよりも高速化されます。手順については、「.zip または JAR ファイルアーカイブで Java Lambda 関数をデプロイする」を参照してください。

  • 依存関係の複雑さを最小限に抑えます。フレームワークを単純化して、実行環境起動時のロードを高速化します。たとえば、Spring Framework などの複雑なフレームワークよりも、DaggerGuice などの単純な Java 依存関係インジェクション (IoC) フレームワークを使用します。

  • Lambda 関数内で任意の条件が満たされるまで、その関数自身を自動的に呼び出すような再帰的なコードを使用しないでください。これを行うと意図しないボリュームで関数が呼び出され、料金が急増する可能性があります。誤ってこのようなコードを使用した場合は、すぐに関数の予約済同時実行数を 0 に設定して、コードを更新している間のすべての関数の呼び出しをスロットリングします。

  • Lambda 関数コードで文書化されていない非公開の API を使用しないでください。AWS Lambda マネージドランタイムでは、Lambda が Lambda の内部 API にセキュリティと機能面の更新を定期的に適用します。これらの内部 API 更新には後方互換性がないことがあり、関数にこれらの非公開 API に対する依存関係がある場合、呼び出しの失敗などの意図しない結果につながります。公開されている API のリストについては、「API リファレンス」を参照してください。

  • 冪等性コードを記述します。関数の記述に冪等性コードを使用すると、重複するイベントが同じ方法で処理されるようになります。コードでは、イベントを適切に検証し、重複するイベントを適切に処理する必要があります。詳細については、「Lambda 関数を冪等にするにはどうすればよいですか?」を参照してください。

  • Java DNS キャッシュを使用しない Lambda 関数は、既に DNS レスポンスをキャッシュしています。別の DNS キャッシュを使用すると、接続タイムアウトが発生する可能性があります。

    java.util.logging.Logger クラスは JVM DNS キャッシュを間接的に有効にできます。デフォルト設定を上書きするには、 を初期化する前に networkaddress.cache.ttl を 0 に設定しますlogger。例:

    public class MyHandler { // first set TTL property static{ java.security.Security.setProperty("networkaddress.cache.ttl" , "0"); } // then instantiate logger var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class); }

    UnknownHostException 障害を防ぐために、 networkaddress.cache.negative.ttlを 0 に設定することをお勧めします。このプロパティは、 AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL=0環境変数を使用して Lambda 関数に設定できます。

    JVM DNS キャッシュを無効にしても、Lambda のマネージド DNS キャッシュは無効になりません。

Function Configuration

  • Lambda 関数のパフォーマンステストは、最適なメモリサイズ設定を選択する上で欠かせない部分です。メモリサイズが増えると、関数で利用できる CPU も同様に増加します。関数のメモリ使用量は呼び出しごとに決定され、Amazon CloudWatch で表示できます。次に示すように、呼び出しごとに REPORT: エントリが作成されます。

    REPORT RequestId: 3604209a-e9a3-11e6-939a-754dd98c7be3 Duration: 12.34 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 18 MB

    Max Memory Used: フィールドを分析することで、関数のメモリが不足しているか、関数のメモリサイズをオーバープロビジョニングしているかを判断できます。

    関数のために適切なメモリの構成を見つけるには、オープンソースの AWS Lambda Power Tuning プロジェクトを使用することを推奨しています。詳細については、GitHub の AWS Lambda Power Tuning を参照してください。

    関数のパフォーマンスを最適化するためには、Advanced Vector Extensions 2 (AVX2) が利用可能なライブラリのデプロイも推奨されます。これにより、機械学習による推定、メディア処理、ハイパフォーマンスコンピューティング (HPC)、科学シミュレーション、財務モデリングなど、負荷が大きくなりがちなワークロードを処理できるようになります。詳細については、AVX2 を使用した高速な AWS Lambda 関数の作成を参照してください。

  • Lambda 関数のロードテストにより、最適なタイムアウト値を決定します。関数の実行時間を分析し、依存関係サービスの問題に伴って関数の同時実行が必要以上に増えるような状況をより的確に判定します。これは、Lambda のスケーリングを処理しない可能性があるリソースに対して Lambda 関数からネットワークの呼び出しを行うときに特に重要です。

  • IAM ポリシーの設定時に最も制限的なアクセス許可を使用します。Lambda 関数に必要なリソースとオペレーションを把握し、実行ロールをこれらのアクセス許可に制限します。詳細については、「AWS Lambda アクセス許可の管理」を参照してください。

  • Lambda クォータ について理解を深めます。ランタイムのリソース制限を決定する際に、ペイロードサイズ、ファイル記述子、および /tmp スペースが見過ごされがちです。

  • 使用しなくなった Lambda 関数を削除します。削除することで、未使用の関数がデプロイパッケージサイズの制限対象として不必要にカウントされなくなります。

  • イベントソースとして Amazon Simple Queue Service を使用している場合、関数の予想呼び出し時間の値が、キューの可視性タイムアウトの値を超過していないことを確認してください。これは、CreateFunctionおよびUpdateFunctionConfigurationの両方に適用されます。

    • CreateFunction の場合、AWS Lambda における関数の作成プロセスは失敗します。

    • UpdateFunctionConfiguration の場合、関数の呼び出しが重複する可能性があります。

関数のスケーラビリティ

  • アップストリームおよびダウンストリームでのスループットの制約について理解を深めます。Lambda 関数は負荷に応じてシームレスにスケールしますが、アップストリームとダウンストリームの依存関係においてスループット能力は同じではない場合があります。関数がスケーリングできる高さを制限する必要がある場合、関数に 予約された同時実行数を設定 できます。

  • スロットリング耐性を組み込みます。Lambda のスケーリングレートを超えるトラフィックが原因で同期関数にスロットリングが発生した場合、次の戦略を使用してスロットリング耐性を改善できます。

    • ジッターを伴うタイムアウト、再試行、バックオフ」を使用します。これらの戦略を実装すると、呼び出しの再試行がスムーズになり、Lambda が数秒以内にスケールアップしてエンドユーザーのスロットリングを最小限に抑えることができます。

    • プロビジョニングされた同時実行数 を使用します。プロビジョニングされた同時実行は、Lambda が関数に配分する事前初期化済みの実行環境の数です。Lambda は、利用可能なときにプロビジョニングされた同時実行を使用し、受信リクエストを処理します。Lambda は、必要に応じてプロビジョニングされた同時実行設定を超えて、関数をスケーリングすることもできます。プロビジョニングされた同時実行を設定すると、AWS アカウントに追加料金が請求されます。

メトリクスおよびアラーム

  • Lambda 関数のメトリクスの使用 および CloudWatch アラームを使用することで、Lambda 関数コード内からメトリクスを作成または更新しないようにします。Lambda 関数のヘルスを追跡する方法としてより効率的であり、デプロイプロセスの早期で問題を把握できます。たとえば、Lambda 関数の推定される呼び出し所要時間に基づいてアラームを設定し、関数コードに起因するボトルネックやレイテンシーに対処できます。

  • ログ記録のライブラリやAWS Lambda メトリクスとディメンションを活用して、アプリケーションエラー (ERR、ERROR、WARNING など) を補足します。

  • AWS コスト異常の検出を使用して、アカウントの異常なアクティビティを検出します。コスト異常の検出は、機械学習を使用し、コストと使用量を継続的にモニタリングしながら誤検出アラートを最小限に抑えます。コスト異常の検出は、AWS Cost Explorer のデータを使用しますが、データには最大 24 時間の遅延があります。その結果、使用を開始してから異常を検出するまでに最大 24 時間かかる場合があります。コスト異常の検出を開始するには、まず Cost Explorer にサインアップする必要があります。次に、コスト異常の検出をアクセスします。

ストリームの使用

  • バッチおよびレコードの各種サイズのテストにより、関数がタスクを完了できるスピードに合わせて各イベントソースのポーリング間隔を調整します。CreateEventSourceMapping BatchSize パラメータは、各呼び出しで関数に送信できるレコードの最大数を制御します。通常、バッチサイズが大きいほど、大きなレコードセット全体での呼び出しのオーバーヘッドをより効率的に吸収し、スループットを増大できます。

    デフォルトで、Lambda はレコードが使用可能になると同時に関数を呼び出します。Lambda がイベントソースから読み取るバッチにレコードが 1 つしかない場合、Lambda は関数に 1 つのレコードしか送信しません。少数のレコードで関数を呼び出さないようにするには、バッチ処理ウィンドウを設定することで、最大 5 分間レコードをバッファリングするようにイベントソースに指示できます。関数を呼び出す前に、Lambda は、完全なバッチを収集する、バッチ処理ウィンドウの期限が切れる、またはバッチが 6 MB のペイロード制限に到達するまでイベントソースからのレコードの読み取りを継続します。詳細については、「バッチ処理動作」を参照してください。

    警告

    Lambda イベントソースマッピングは各イベントを少なくとも 1 回処理し、レコードの重複処理が発生する可能性があります。重複するイベントに関連する潜在的な問題を避けるため、関数コードを冪等にすることを強くお勧めします。詳細については、 AWS ナレッジセンターの「Lambda 関数を冪等にするにはどうすればよいですか?」を参照してください。

  • シャードを追加して Kinesis ストリーム処理のスループットを増加させます。Kinesis ストリームは 1 つ以上のシャードで構成されます。Lambda は、最大で 1 つの同時呼び出しを使用して、各シャードをポーリングします。たとえば、ストリーミングに 100 個のアクティブなシャードがある場合は、最大で 100 個の Lambda 関数呼び出しが同時に実行されます。シャードの数を増やすと、直接的な結果として、Lambda 関数の同時呼び出しの最大数が増えます。また、Kinesis ストリーム処理のスループットが増える場合があります。Kinesis ストリームのシャード数を増やす場合は、データの適切なパーティションキー (「パーティションキー」を参照) を選択していることを確認し、関連レコードが同じシャードに割り当てられ、データが適切に配分されるようにします。

  • Amazon CloudWatchを IteratorAge で使用し、Kinesis ストリームが処理されているかどうかを判断します。たとえば、CloudWatch アラームを最大値の 30,000 (30 秒) に設定します。

セキュリティに関するベストプラクティス

  • AWS Security Hub を使用して、セキュリティのベストプラクティスに関連する AWS Lambda の使用状況をモニタリングします。Security Hub は、セキュリティコントロールを使用してリソース設定とセキュリティ標準を評価し、お客様がさまざまなコンプライアンスフレームワークに準拠できるようサポートします。Security Hub を使用して Lambda リソースを評価する方法の詳細については、「AWS Security Hub ユーザーガイド」の「AWS Lambda コントロール」を参照してください。

  • Amazon GuardDuty Lambda Protection を使用し、Lambda ネットワークのアクティビティログを監視します。GuardDuty Lambda Protection は、AWS アカウント で Lambda 関数が呼び出されたときの潜在的なセキュリティ脅威を特定するために役立ちます。例えば、1 つの関数が暗号通貨関連のアクティビティに関連付けられている IP アドレスをクエリしたとします。GuardDuty は、Lambda 関数が呼び出されたときに生成されるネットワークアクティビティのログを監視します。詳細については、「Amazon GuardDuty ユーザーガイド」の「Lambda Protection」を参照してください。