AWS Firehoseを使用した ログ出力設定について

はじめに

はじめてのかたははじめまして、たまにカレーにマヨネーズかけるよねって言ったら誰からも賛同を得られなかったhaya_timeです。

今回はAWSサービスのFirehoseを使ってログを出力した内容を書いていきたいと思います。

背景

私は社内の別々の部署間で案件のID管理をやり取りするWEBアプリケーションの開発・運用を担当しているのですが、実際にユーザがどの程度の頻度で なんの機能を使っているのかを分析したいので、ログを出力して欲しいという要望があがりました。 さらに集計するときデータ転送しやすいようにDB登録ではなく、JSON形式のファイルでアウトプットすることになりました。

とりあえず簡単なデータで分析を行いたいということなので、APIが呼び出された時間とユーザ情報、呼び出されたAPI名をログに出力する対応に着手することにしました。

動作環境

稼働しているWEBアプリケーションの運用環境は下記の通りです。 (今回のブログで参考になりそなものだけ抜粋)

  • サーバ:AWS EC2

  • nodejs(v18.x)

当初の実装方法

まずはファイルの出力先としてどこにするか検討し、AWS S3にアップロードすることにしました。AWS S3にした理由は、様々なアプリケーションに幅広く対応しているからです。

WEBアプリケーションはnodejsで稼働しているので、AWS SDKのモジュールを使用し直接AWS S3にファイルをアップロードしようとしました。

懸念があるとすれば、リクエスト受けた時点でファイルをアップロードするので1リクエスト1ファイルとなり、ファイル数が多くなる可能性がありました。また都度S3にファイルを登録していると全体的にパフォーマンスが下がる可能性もありました。

しかし、社内システムなので使用するユーザ数が限られていることと、バケットを日別にローテションすれば問題ないと思い、上記設計で実装することにしました。

そんな感じで実装を進めていこうとしたのですが、ちょっと調べるとAWSにはFirehoseというデータの収集に最適なサービスがあるじゃないですか! 教えてくれた同僚に感謝!そして自分の無知に嫌悪してました。。。

AWS Firehoseとはとは?

docs.aws.amazon.com

あらためてFirehoseとは、Amazon Data Firehoseの略でリアルタイムでのデータストリームの収集・処理・分析を行うAmazon Kinesisサービスの一つです。 ユーザからの入力やアクセスログなどで生成されたデータソースを、指定したAWSのサービスにストリーミングデータとして転送するサービスです。

上記で上げたS3に出力されるファイル数の問題と、パフォーマンスの懸念点を解決してくれるドンピシャなサービスですね!

構成イメージ

Firehoseの設定

ますはFirehoseの設定をしていくので、AWSコンソールにログインしAmazon Data Firehoseを選択しFirehoseストリームを作成していきます。

今回はS3に直接出力するので、ソースを「Direct PUT」、送信先を「Amazon S3」とします。 ストリーム名は任意の名前で、オプションについては今回はとくに初期設定から変更なしです。

設定1

続いて送信先の設定です。 S3のバケット名は任意として、改行の区切り文字は「有効」とします。 プレフィックスオプションについても任意で良いと思いますが、今回は日別毎にバケットを分ける予定なのでプレフィックスも日までを付与します。 また、エラーについてはエラー配下に出力するようにしています。

設定2

バッファについてはバッファサイズとバッファ間隔は共に最大の128MiBと900秒としました。 推奨は5MiBと300秒なのですが集計するときはサイズが大きくてもファイル数が少ないほうが取り込みやすいと思い、また集計のデータ取り込みが1日1回とリアルタイム性を求めていなかったため最大値にしてます。

あと、S3にアクセスするのでFirehoseのサービスロールにS3の権限を付与しておく必要があります。

以上で設定は完了です。想定通りにファイルが出力されるかテストを行います。ただし、本設定だと900秒まではファイルがローテションされないので注意が必要です。

設定3

サーバサイドの実装

やることはS3にアップロードしようとしていたときと同じでAWS SDKを使ってログ情報をFirehoseに送信するだけです。 SDKは「@aws-sdk/client-firehose(3.535.0)」を使っています。 サーバとして使用しているEC2のインスタンスプロファイルのポリシーにFirehoseの権限を付与していますので、Firehoseのストリーム名を指定し送信関数を呼び出せば実装は完了です。 下記は実際のソースのサンプルになります

const { FirehoseClient, PutRecordCommand } = require('@aws-sdk/client-firehose')
const client = new FirehoseClient({ region: 'ap-northeast-1' })

/**
 * ログ内容をFirehoseに転送
 * リクエスト情報からユーザの行動ログをAWS Firehose経由でS3に出力
 * @param {object} logData 出力するログ情報のJSON配列
 * @return
 */
module.exports.output = (logData) => {
    try {
        // 出力設定
        const input = {
            // Firehoseで設定したものと同じ
            DeliveryStreamType: 'DirectPut',
            // 送信したいFirehoseのストリーム名
            DeliveryStreamName: xxxxx,
            Record: {
                Data: new TextEncoder().encode(JSON.stringify(logData)),
            },
        }

        // Firehoseにリクエスト送信
        const command = new PutRecordCommand(input)
        client.send(command)

        return
    } catch (err) {
        console.log("エラー");

        return
    }
}

まとめ

今回はログ内容をFirehoseを使ってS3に出力してみました。 当初のS3に直接ファイルを登録するよりもデータ転送するだけなのでパフォーマンスに影響が少なくて出力されるファイルもまとまっているので良かったかなと思います。 Firehoseはしきい値を設けてデータをファイルに出力出来るので、今回のような解析用のログデータの出力やリアルタイムにデータファイルを扱いたいようなユースケースには向いているシステムだなと思いました。