Amazon Web Services ブログ

Operating Lambda: パフォーマンスの最適化 – Part 3

Operating Lambda シリーズでは、AWS Lambda ベースのアプリケーションを管理している開発者、アーキテクト、およびシステム管理者向けの重要なトピックを取り上げます。この 3 部構成のシリーズでは、Lambda ベースのアプリケーションのパフォーマンスの最適化について説明します。

Part 1 では、Lambda 実行環境のライフサイクルについて説明し、コールドスタートを定義、測定、改善する方法について説明しました。Part 2 では、メモリ構成が Lambda パフォーマンスに及ぼす影響と、静的初期化コードを最適化する方法について説明しました。このブログでは、Lambda 関数のパフォーマンス向上に役立つ関数のアーキテクチャとベストプラクティスについて説明します。

AWS では、ネットワーキングレイヤーやコンピューティングレイヤーなど、Lambda サービスの基盤となるコンポーネントを定期的に改善しています。Lambda 開発者は、関数を変更することなく、自動的にこれらの改善の恩恵を受けることができます。

関数を最適化するには、開発者が最も影響を与えることができる Lambda 実行ライフサイクルの部分に焦点を当てるのがベストプラクティスです。ハンドラの外部にある初期化コードとハンドラコード自体は、どちらもカスタマサイドの最適化にとって重要な領域です。Lambda サービスによる継続的な改善と組み合わせて、これらの領域に焦点を合わせることが、全体的なパフォーマンスを最適化するための推奨される方法です。

インタラクティブワークロードと非同期ワークロードのパフォーマンスの比較

分散システムアプリケーションは、ネットワーク上でメッセージを使用して通信する複数のサービスで構成されます。ネットワークの遅延、トラフィック、メッセージの再試行、システムフェールオーバー、およびサービスの個々のパフォーマンスプロファイルにより、作業単位の完了にかかる時間は異なる場合があります。

平均的な数値に対してパフォーマンスを測定する代わりに、外れ値を測定する方が役立ちます。AWS X-Ray レポートには、外れ値のパフォーマンスを特定するのに役立つ応答分布ヒストグラムが表示されます。パーセンタイルメトリクスを使用すると、たとえば、p95 または p99 の範囲で発生したレイテンシーを特定できます。これは、最も遅いリクエストの 5% または 1% のパフォーマンスを示します。

パフォーマンス目標は、ユースケースによって決定されます。インタラクティブワークロードでは、呼び出しはエンドユーザーイベントによって直接トリガーされます。ウェブアプリやモバイルアプリなどのアプリケーションでは、リクエストのラウンドトリップパフォーマンスはエンドユーザーが直接体験します。ユーザーがアプリケーションのバックエンドに API リクエストを行うと、これは応答を返す前にダウンストリームサービスを同期的に呼び出します。このようなタイプのアプリケーションでは、ラウンドトリップ遅延を最適化してユーザーエクスペリエンスを向上させることが重要です。

多くの対話型同期ワークロードでは、リアクティブな非同期アプローチを使用するように設計を再構築できる場合があります。この場合、最初の API 呼び出しはリクエストを Amazon SQS キューに保持し、呼び出し元へすぐに応答を返します。

Amazon API Gateway を使用している場合、これは Lambda 関数の代わりにサービス統合を使用して完了できます。処理は非同期的に継続され、呼び出し元が進行状況をポーリングするか、アプリケーションが Webhook または WebSocket を使用して要求のステータスを通信します。このアプローチにより、エンドユーザーエクスペリエンスが向上すると同時に、ワークロードの拡張性と耐障害性を高めることができます。

詳細については、「サーバーレスウェブアプリでのバックエンドリクエストとフロントエンド通知の管理(英語)」をご覧ください。

多くの非同期ワークロードでは、個々の Lambda 関数のコールドスタートレイテンシーは、集約された全体的なパフォーマンスよりも重要ではありません。Amazon S3 や SQS などのイベントソースを操作する場合、Lambda はトラフィックを処理するようにスケールアップします。多くのタスクを並行して処理できるため、関数呼び出し全体の中の少数の割合でコールドスタートレイテンシーが発生しても、処理タスクの全体的な時間への影響はごくわずかです。

Lambda 関数を使用しないとき

Lambda 関数を使用する必要は必ずしもありません。状況によっては、パフォーマンスを向上させる他の方法があるかもしれません。

オーケストレータとして機能し、他のサービスや関数を呼び出して作業を調整する関数の場合、関数のアイドル時間が発生する可能性があります。この関数は通常、他のタスクが実行される間待機し、コストが増加します。ほとんどの場合、オーケストレーションフローを AWS Step Functions に移行すると、よりメンテナンス性と弾力性に優れたステートマシンが作成され、コスト削減に役立ちます。

あるサービスから別のサービスにデータを転送し、そのデータに対してビジネスロジックを実行しないような Lambda 関数は、サービス統合に置き換えることが可能です。たとえば、テーブルから項目を読み取るために、通常、API Gateway と Amazon DynamoDB の間に Lambda 関数を配置する必要はありません。これは、DynamoDB に対して直接 API ゲートウェイのサービス統合で VTL を使用することで実現できます。これにより、スケーラビリティが向上し、コストが削減されます。

マイクロサービスがフィルタリングのためにすべてのイベントを Lambda 関数に送信し、イベントの小さなサブセットでのみ動作する場合は、関数を呼び出す前にフィルタリングを実装できる場合があります。たとえば:

S3 イベントはプレフィックスとサフィックスのパターンでフィルタリングできるため、特定のオブジェクトキーのみでフィルタリングできます。
Amazon SNS は、ターゲットを呼び出す前にメッセージをフィルタリングできます。
Amazon EventBridge を使用すると、ルール内で強力なコンテンツフィルタリングロジックを使用して、イベントをトリガーする関数を高度に選択できます。
フィルタを使用すると、アプリケーションが関心のあるイベントに対してのみ Lambda 関数を呼び出すため、コストを削減し、効率を向上させることができます。

コストの最適化

ワークロードに対して Lambda を実行するコストは、実行回数、実行時間とメモリ使用量 (GB/秒として合計)、およびデータ転送の 3 つの要素によって決まります。前のセクションで説明したメモリ割り当ての影響以外にも、これら 3 つの変数に影響を与え、コストを削減できる設計上の選択肢が他にもあります。

ランタイムの選択は、コストに影響する可能性があります。一般に、コンパイルされた言語は、インタプリタ言語よりも速くコードを実行しますが、初期化に時間がかかる場合があります。基本的な機能を備えた小さな関数では、多くの場合、インタープリタ言語が最速の総実行時間に適しており、したがってコストが最も低くなります。コンパイルされた言語を使用する関数は、計算が複雑なワークロードや Provisioned Concurrency を使用している場合では高速になることが多く、これは関数実行の前に初期化のオーバーヘッドが発生するためです。

呼び出し頻度は、コストを決定する主要な要因です。Lambda 関数をトリガーするイベントに応じて、呼び出しの総数を減らすために使用できるさまざまなコントロールがあります。以下によってトリガーされる Lambda 関数の場合:

API Gateway: 頻繁に変更されないデータを返す呼び出しには、API Gateway の前で CloudFront キャッシュを使用します。これにより CloudFront のコストは増加しますが、API ゲートウェイと Lambda のコストは削減されます。

SQS: batchSize プロパティは、呼び出しごとに Lambda に送信される SQS キュー内の項目数を決定します。この数を増やすと、Lambda 呼び出しの数が減ります。ユースケースによっては、SQS に送信されるメッセージごとに多くのデータを集約して、より少ない呼び出しでより多くのデータを処理できる場合があります。

SAM テンプレートでの batchSize の使用については、こちらのURLをご参照ください。

https://github.com/aws-samples/s3-to-lambda-patterns/blob/master/ddbImporter/v2/template.yaml

また、ワークロード全体のデータ転送コストも考慮してください。Lambda のデータ転送コストは、データ転送セクションの https://aws.amazon.com/ec2/pricing/on-demand/ に記載されている Amazon EC2 データ転送料金で課金されます。インターネットから転送されるデータには、転送コストはかかりません。一般に、マイクロサービス間でメッセージで渡されるデータの量を制限することで、これらのコストを最小限に抑えることができます。

結論

この記事は、Lambda でのパフォーマンス最適化に関するシリーズの 3 部構成の最後のパートです。Lambda サービスは、サービスの基盤となるハードウェア、ソフトウェア、アーキテクチャのパフォーマンスを頻繁に改善します。この投稿では、開発者がパフォーマンスに最も大きな影響を与えることができる Lambda ライフサイクルの部分について説明しました。

インタラクティブワークロードと非同期ワークロードを比較し、Lambda 関数の代わりに直接サービス統合を使用できる場合を比較しました。また、ワークロードの実行コストの削減にも役立つコスト最適化のヒントをいくつか紹介しました。

サーバーレスの学習リソースについては、こちらで見つけることができます。

今から始めるサーバーレス
ServerlessLand.com

この記事の翻訳は、Solution Architect  福井厚が担当しました。原文はこちらからご覧いただけます。