S3オブジェクトをLambdaで処理する設計例4選

はじめに

初めまして!

2022年の2月からクラウド事業推進部にジョインいたしました、太田有人(おおたありひと)と申します。 GIMLEサービスを提供するチームの一員として、本ブログの投稿をやっていくことになりました。 初回の投稿で緊張しておりますが、暖かく見守っていただけると幸いです(^_^)


現在、私はAWSを使ったシステムを作るプロジェクトにてお仕事しております。 その中で先日、S3にあるオブジェクトをLambdaで処理する設計の方法を考える機会がありました。 その時はS3イベントをトリガーとした場合とSQSを挟んだ場合の違いについてのお話だったのですが、 「他にどんなパターンが考えられるかな」という考えがふと頭によぎりました。

そこで今回は、S3バケットのオブジェクトをLambdaで処理する方法をいくつかピックアップして、それぞれの特徴などを書いていければと思います!

S3オブジェクトをLambdaで処理する際の設計例4選

1 S3イベントをトリガーとしてLambdaを呼び出す

こちらが一番シンプルなパターンかと思います。

S3バケットでは、特定のイベントが発生したときに通知を受け取ることができます。 このイベントはSNSのトピック・SQSのキュー・Lambda関数に送信することができるので、これを利用してLambda関数を呼び出すパターンです。

利用できるS3イベントの一覧は、Amazon S3 イベント通知に記載がありますので、参照いただければと。

Lambdaの呼び出し方としては、非同期での呼び出しとなりますね。 非同期での呼び出しでは、以下のような特徴があります。

  • Lambda はリクエストをキューに入れ、追加情報のない成功のレスポンスを返す
  • Lambda は関数の非同期イベントキューを管理し、エラー発生時に再試行を行う
    • 初回実行含めて最大3回までのリトライを実施
    • 最初と2回目の間に1分間、2回目と3回目の間に2分間の待機時間がある
  • 同時実行数が不足している場合、追加のリクエストはスロットリングされる
  • スロットリングエラーやシステムエラーが発生すると・・・
    • Lambdaはイベントをキューに戻す
    • その後最大6時間、関数の再実行を試行する。再試行の間隔は、1秒〜5分まで
      • キュー内のイベントの量によって、再試行の間隔は自動的に延長される
  • Lambda関数の正常終了時、エラー時それぞれで、関数の呼び出し結果(呼び出しレコード)の送信先が設定できる
  • 上記の送信先とは別に、デッドレターキューとしてSQSのキュー、またはSNSのトピックが設定できる

こちらのパターンだと、Lambda関数が受け取るイベントにはS3オブジェクトのパスが設定されるため、Lambda内でS3からファイルを読み取る処理が必要になってきます。

2 S3からSQSを挟んでLambdaを呼び出す

こちらもよくあるパターンですね。S3のイベントをSQSのキューに入れて、Lambdaで処理します。

Lambdaの呼び出し方としては、同期での呼び出しです。 同期での呼び出しでは、以下のような特徴があります。

  • Lambdaは関数を実行し、レスポンスを待つ
  • 関数の実行後、呼び出された関数のバージョンなどの情報と共に、関数からのレスポンスを返す

このパターンではSQSのキューをLambdaのトリガーとして設定するのですが、その際の特徴は以下のようになります。

  • SQSの標準キューおよびFIFO(First in first out)キューでのLambda呼び出しをサポート
  • Lambda はキューをポーリングし、Lambda 関数を、キューメッセージを含むイベントと共に同期的に呼び出す
    • キューに入っているメッセージをバッチで読み取り、バッチごとに、一度に関数を呼び出す
    • 関数が正常にバッチを処理すると、キューからそのメッセージを削除する
  • Lambda側では、以下を設定する
    • バッチで一度に読み取るメッセージの数(バッチサイズ)
    • 関数を呼び出す前にレコードを収集する最大時間(バッチウィンドウ)
      • こちらは標準キューを使用する場合のみ適用される

こちらのパターンでも、Lambda のイベントには S3 オブジェクトのパスが設定されるため、Lambda内でS3からファイルを読み取る処理が必要になってきます。 もし処理したいデータがSQS のメッセージサイズ上限(256 KB)におさまるのであれば、 S3イベントをSQSに送信するのではなく、処理したいデータを直接SQSのキューに送信することを検討してもいいかもしれませんね。 そうすれば、Lambdaのイベントに直接データを渡すことができて、Lambdaの実装がシンプルにできるかと思います。

メッセージの可視性タイムアウトやデッドレターキューの設定、エラー発生時の処理、ショートポーリングとロングポーリングなど、SQSで設定する部分も含めて考慮点が増えますが、 LambdaとS3のみで実装するパターン1と比べると、柔軟な設計が可能になるかと思います。

SQSのキューをLambdaのトリガーとする際の詳細については、Amazon SQS での Lambda の使用に記載があります。

3 S3からEventBridge経由でStepFunctionsを起動してLambdaや他のAWSサービスを呼び出す

だいぶ複雑なパターンになってきました。S3のイベントを、EvengBridge経由でStepFunctionsのステートマシンに送信して、処理します。 このパターンの場合、StepFunctionsのステートマシンを作成してワークフローを考えないといけないので、実装の考慮点が増えます。 StepFunctionsのステートマシンでワークフローを設計することができるので、パターン1や2と比べて柔軟性は高くなりますね。

StepFunctionsは、AWS SDKと統合されており、連携できるAWSサービスがかなりたくさんあります。

AWS Step Functions が AWS SDK 統合で 200 を超える AWS のサービスのサポートを追加

Systems ManagerのDocumentを通じたEC2上でのアプリ実行や、ECSタスクを起動したりすることも可能で、ワークフローの柔軟性はかなり高いと思います。 ただ、エラー時のリトライ処理も考慮してフローの中に組み込む必要があるため、設計の難易度は結構高かったりします。 (あと、ステート間でデータを受け渡す際の構造としてJsonPathというものが使用されているのですが、こいつを理解するのに苦労した覚えが・・・)

Lambdaの数が増えてフロー制御がややこしくなっている場合や、Lambdaの最長実行時間に収まらない処理を挟んだフローが必要な場合は、検討の候補に挙がってくるのではないでしょうか。

4 S3 Object Lambdaを利用する

S3 Object Lambdaは 2021/3/18 に利用できるようになった比較的新しい機能で、S3へのGETリクエストに対するレスポンスを、Lambdaで加工できるサービスです。 S3 Object Lambda アクセスポイントを、S3の標準アクセスポイントと関連づけて使用します。

ドキュメントからは、以下のような特徴があるみたいです。

  • Object Lambda アクセスポイントは、1つの標準アクセスポイントと1つのS3バケットに関連づけられる
  • 使用するLambda関数は、Object Lambda アクセスポイントと同一アカウント、同一リージョンに存在する必要がある
  • Lambda関数の実行時間は60秒まで許可されているが、実際の実行可能時間はより短くなる場合がある
    • Lambda関数自体のタイムアウト設定や、呼び出し側でのタイムアウト設定が60秒より短い場合など
  • CloudFormationテンプレートでの記述も可能

このパターンの場合、エラー時のリトライ処理はクライアント側で実装することになるかと思います。

S3のオブジェクトを編集して返す必要があるけど、保管しておかないといけないオブジェクトは編集前のものだけ・・・のようなケースで検討できるパターンではないでしょうか。 (特定の情報をマスクする、オブジェクトのデータの一部だけを返す、など)

おわりに

今回紹介できませんでしたが、S3イベントをSNSトピックに送信した後、複数の別々のLambdaを呼び出す方法など、この他にも様々なパターンが考えられます。 それぞれのサービスやパターンの特徴を掴んで、要件に沿った最適な設計をしていきたいですね!

参考情報リンクまとめ