Amazon KinesisとAWS Lambdaを活用したBaikonurロギング

前提条件

なぜKinesisを活用するのか?

Kinesis Data Streamsは、ストリーミングデータを扱うための高スループット、低レイテンシのフルマネージドサービスです。ストリームは複数のシャードから構築され、一つのシャードは、 1 MB/秒の読み取りと2 MB/秒の書き込みキャパシティを提供します 。 Kinesis Data Streamに書き込まれたデータは、データ保持期間パラメーターで指定された期間保存されます。したがって、キャパシティプランニングでは、格納、取り出しのピークスループットさえ知っていれば十分です。

注釈

Kinesis Data Streamsにはオートスケーリング機能がありません。 amazon-kinesis-scaling-utils を使用すると、Kinesis Data Streamのシャード数を自動的に変更することができます。

なぜKinesis上のデータをLambdaで処理するのか?

Kinesis Data Streams上のデータを(イベントソースマッピングを用いて) Lambda関数で処理することで実装コストと管理コストを抑えることができます:

  1. Lambdaでは、サーバーやOSなどを管理する必要はありません。
  2. Kinesis Data Streamsからデータを読み取るためのロジックを実装する必要はありません。データのバッチは、Lambda関数の実行時に``event``オブジェクトで渡されます。
  3. イベントソースマッピングでは、Kinesis Data Stream上の処理済みデータの位置が自動的に管理されます。各シャードでどの位置までのデータが正常に処理されたかが記録されます。Lambdaの実行ががエラーなしで完了した場合にのみ、位置が更新されます。
  1. Kinesis Data StreamにマッピングされたLambda関数がエラーで終了した場合、実行が成功するか、データの期限が切れるまLambda関数が同じバッチで再度実行されます。データは保持期間が超過したときにのみKinesis Data Streamから削除されます。
    • したがって、再試行ロジックを実装する必要はありません。ただし、同じバッチでLambdaが一定回数失敗し続けている場合、そのバッチをキュー(またはS3)に保存し、データ処理パイプラインが停止しないようにエラーなしで終了することをお勧めします。

なぜロギングにKinesisとLambdaを活用するか?

KinesisとLambdaの連携により、ログ処理のコントロールを維持しながら、自前のロギングクラスターをマネージドサービスに置き換えることができます。

KinesisとLambdaを使うとで、モジュール式で拡張可能な、スケーラブルなロギングアーキテクチャが実現できます。ログ転送の信頼性を向上するメリットもあります。データがKinesis Data Streamに正常に書き込まれれば、欠損することはありません。

共通スキーマ要件

「Baikonurログ周り構成」とは、Kinesis Data Streamsと次のBaikonurログ周りLambdaモジュールを1つ以上の組み合わせを使っているものを指す。

これらのモジュールには、次の共通スキーマ要件があります。

  • すべてのデータはJSONである必要があります。ルート要素の型は object でなければなりません。

  • すべてのデータには、次のキーが含まれている必要があります。

    • データ種類識別子 (デフォルトのキー名: log_type )
    • 一意の識別子。例: uuid.uuid4() (デフォルトのキー名: log_id )
    • dateutilでパース可能なタイムスタンプ (デフォルトのキー名: time )

注釈

すべてのキー名はカスタマイズ可能です。

共通スキーマ要件により、次のことが可能になります:

  1. より簡単なパース

  2. 異なるLambdaモジュール間の互換性の向上

    • 異なるモジュールを同じKinesis Data Streamに接続することができます。
  3. 共通スキーマ要件のキーを使った機能を実装することができます:

    • 最も重要な機能の1つは、データ種類識別子の値に基づいたログのフィルタリング機能です。
    • terraform-aws-lambda-kinesis-to-s3 では、ファイル名の一意性を保証するためにlog_idが必要で、日付でログを仕分けるためにタイムスタンプフィールドを必要です。
    • terraform-aws-lambda-kinesis-to-es では、Elasticsearchで日別のインデックスを作成するためにタイムスタンプが必要です (例: index-20200314)。

注釈

データが共通スキーマ要件を満たしている限り、このページで説明されているアーキテクチャとモジュールは、高速で信頼性を担保する必要があるあらゆるデータ転送に適用できます。例えば、マイクロサービス間通信に活用できます。

アーキテクチャ例

1つストリーム - 1つの転送先

宛先ごとにKinesis Data Streamを作成します。たとえば、すべてのログデータをS3に保存し、一部のみをElasticsearchに保存する場合は、2つのストリームを作成し、 terraform-aws-lambda-kinesis-to-s3 および terraform-aws-lambda-kinesis-to-es モジュールをデプロイしてマップします。それらをそれぞれのストリームに:

   Kinesis API
       v
[App] ---> [KDS "s3"] ---> [kinesis-to-s3] ---> S3
     \
      ---> [KDS "es"] ---> [kinesis-to-es] ---> ES

S3とElasticsearchの両方にログを保存する場合は、両方のストリームにデータを書き込みます。

1つのストリーム - 複数の転送先

上記の例では、Elasticsearchにログを保存するためには、両方のストリームに同じデータを書き込む必要があります。 Elasticsearch用のストリームに terraform-aws-lambda-kinesis-to-s3 を追加することで、重複格納をなくすことができます:

   Kinesis API
       v
[App] ---> [KDS "s3"] ---> [kinesis-to-s3] ---> S3
     \
      ---> [KDS "es"] ---> [kinesis-to-es] ---> ES
                     \
                      ---> [kinesis-to-s3] ---> S3

これで、各ログイベント複数回格納されることはありません。

Kinesisルーティングパターン

ルータと呼ばれる一つのKinesisのストリームにデータを書き込みます。さらに、転送先ごとに出力ストリームを作成します。転送モジュール (terraform-aws-lambda-kinesis-forward)を追加しホワイトリスを設定することで、 出版-購読型モデル に類似したアーキテクチャを作成できます。ここで、トピックはデータ種類識別子であり、出力ストリームは購読グループを表します:

   Kinesis API
       v
[App] ---> [KDS "router"] ---> [kinesis-forward] ---> [KDS "A"]
                         \
                          ---> [kinesis-forward] ---> [KDS "B"]
                          \
                           --> [kinesis-forward] ---> [KDS "C"]

このパターンはマイクロサービス間の通信にも活用できます。

各出力ストリームには、それぞれのLambdaモジュールの組み合わせや購読者を追加することが可能です:

   Kinesis API
       v
[App] ---> [KDS "router"] ---> [kinesis-forward] ---> [KDS "A"] ---> [S3]
                         \
                          ---> [kinesis-forward] ---> [KDS "B"] ---> [ES]
                          \
                           --> [kinesis-forward] ---> [KDS "C"] <--- [External subscriber]

CloudWatch Logsサブスクリプションフィルタを活用したKinesisルーティングパターン

Kinesisルーティングパターン をさらに拡張します。「ルーター」ストリームへのデータ格納をCloudWatch Logsサブスクリプションフィルタで行うよう変更します。これで、すでにログをCloudWatchに出力しているユースケースに置いて、アプリケーションでのPutRecord/PutRecordsの実装が必要がなくなります。たとえば、ECSでawslogsロギングドライバーを使用しているユースケースでは、出力先ロググループにサブスクリプションフィルタを設定すると、ログ周りアーキテクチャが次のようになります:

 stdout->awslogs      Subscription filter
       v                      v
[App] ---> [CloudWatch Logs] ---> [KDS "router"] ---> [kinesis-forward] ---> [KDS "A"] ---> [S3]
                                                \
                                                 ---> [kinesis-forward] ---> [KDS "B"] ---> [ES]
                                                 \
                                                  --> [kinesis-forward] ---> [KDS "C"] <--- [External subscriber]