はじめに
こんにちは、2024年12月からGakken Leapにジョインした丹羽です。
ポジションはインフラエンジニアで、今後はインフラの安定化や最適化に取り組んでいきたいと思います。
さて、この記事では入社早々に取り組んだData Firehoseを使ったログ基盤の構築について解説します。
構成
構成図を下記に示します。
各サービスを動かしているECSサービスからJSONフォーマットでログを送信します。ログの転送量や頻度が今後増加することを考慮してスケーラブルなKinesis DataStreamsでデータを受信し、それからData Firehoseに渡して加工、最終的にJSON LinesファイルとしてS3バケットに格納します。
AWSを活用したログ基盤のリソース構成としてはごく標準的なものだと思います。
以下では、実際に構築していく中でつまずいた点とその解決方法について見ていきます。
つまずきポイント
クロスアカウント
構成図で示した通り、ログを送信するECSサービスとログ基盤のAWSアカウントは分かれています。Kinesis DataStreamsはリソースポリシーという自前のアクセス許可機構を持っているので、ここで送信元のECSサービスのタスクロールを許可するのですが、一方タスクロールの方でもDataStreamsへのアクセス許可をIAMポリシーとして明示的に指定してやらなければいけません。
このうち後者の設定が漏れていたので、クロスアカウントでの動作確認に手間取ってしまいました。
改行の挿入
最終的にJSON Linesファイルとして保存するためには、DataStreamsに送信するデータに最初から改行コードを含めておく、あるいはFirehoseでLambdaによる前処理を挟んで挿入するなどの工夫が以前は必要だったと記憶していますが、今回の構築では「改行の区切り文字」というオプションを有効にしたところ、すぐ実現できました。
余計な手間をかけないよう、サービスの機能は事前に把握しておくのがいいですね。
動的パーティショニング
FirehoseがデータをS3に格納する際、デフォルトではFirehoseで処理された時のタイムスタンプに基づいてパーティションが自動設定されますが、今回はデータ内にあるtimestampプロパティに基づいてパーティショニングしたかったので、動的パーティショニング機能を使うことにしました。
この機能はデータをJSONとしてパース・加工し、取り出した値に基づいてパーティションを決定できるというものです。この値の加工にはjqコマンドのクエリを指定するので、jq力が問われます。
今回、timestampプロパティはISO8601形式だったので、strptime関数で何の苦労もなく文字列に変換できる…と思っていたのですが、なぜかミリ秒が含まれていると変換に失敗します。しばらく悩みましたが、結局ミリ秒の情報は使用しないので変換前に切り捨てることにしました。
# timestampは "2025-01-01T00:00:00.000Z" のフォーマット
.timestamp | split(".")[0] | strptime("%Y-%m-%dT%H:%M:%S") | strftime("%Y/%m/%d/%H")
CLIでの動作確認
構築後に動作確認をしようとaws-cliからDataStreamsにデータを投げたところ、動的パーティショニングでエラーになりました。どうも送信したデータが文字化けしたような状態になっています。
Webで情報収集して判明したのは、aws-cli v2ではコンソールからデータを渡す際にbase64でエンコードされていることが前提で、生データを渡す時は--cli-binary-format raw-in-base64-out
オプションを明示的に指定してやる必要があるということでした。v1の時はオプションを指定せずに生データを渡せたと記憶しているので嬉しくない改変ですが、ともかくこれで、動作確認も無事に行うことができました。
まとめ
構成自体はそれなりに見当がついているつもりでいましたが、それでも細かい箇所では色々と調査や検証が必要でした。
この記事が今後似たような構成を構築する方の参考になれば幸いです。
エンジニア募集
Gakken LEAP では教育をアップデートしていきたいエンジニアを絶賛大募集しています!!
ぜひお気軽にカジュアル面談へお越しください!!