fluent-plugin-kinesis Ver2 の圧縮オプションがすごい話

Amazon Kinesisへデータを送る方法として、Fluentdを使用するのはよくあることかと思います。
その際使用するプラグインはaws-fluent-plugin-kinesisでしょう。

このプラグイン、とてもいいんですが、
問題があるとすると、ログレコードの圧縮が難しい ことです。
ログレコードを圧縮することが出来れば

  • 1シャードあたりの1MB/sec 制限内でより多くのレコードを処理できる
    • シャード数を減らせる
    • Lambdaをトリガーしている場合、Lambda実行回数が減らせる

というメリットがあります。

かつては zlib_compression というオプションがあったようですが、いつの間にか非推奨になっていたようです。
ところが最近「Version 2.0」がリリースされ「compression」オプションが新たに追加されていました!!
さっそく試してみます。

前提

KPLの使用

1レコードにログレコードを圧縮させる技術である「KPL」を使用します。
fluent-plugin-kinesisでは「kinesis_streams_aggregated」を使用すると実現できます。

ちなみに「kinesis_streams_aggregated」は、Ver1系では「kinesis_producer」という名前でした。

システム構成

システム構成は以下のようにしてます。
それぞれ1台(1個)ずつ存在しています。

[Fluentd on EC2] => [Kinesis] ⇒ [Lambda] ⇒ [S3]

fluent-plugin-kinesisからKinesisへログを送信します。
KinesisはそのイベントをトリガーとしてLambdaにデータを渡し、LambdaはS3にログを出力します。
Kinesisへログが吐かれるたびに、S3にファイルが出来るイメージです。

検証方法

11KB程度のログを1秒間に100回ログに出力し、Kinesisに出力します。
以下詳細です。

検証方法 詳細

どのくらいログサイズが圧縮されているかを見るために、でかいJSONファイルを用意します。
今回は以下のようなものを用意しました。

Name Size
sample.json 11KB

中身はでかいのでここには載せませんが、よくある設定ファイルのようなフォーマットです。
このJSONを以下のスクリプトを使用し、ログファイルに出力します。

$ vim test.sh
#!/bin/bash
 
start=$(date)
for i in $(seq 1 ${1})
do
    echo ${i}
    echo -en "number:${i}\tjson:$( echo $(cat sample.json))" >> /var/log/test.log
    sleep 0.01s
done
echo ${start}
date

だいぶ雑なシェルですが、引数で渡された数値分、JSONを「/var/log/test.log」に出力します。
1秒間に100回ログを出力します。
このログをKinesisに送信します。

Fluentdの設定

Fluentdの設定はこんな感じです。

$ source.conf
<source>
    @label @aggregate
    @type tail
    format none
    tag "test.log"
    path /var/log/test.log
    pos_file /var/tmp/test.log.pos
    read_from_head true
</source>

$ match.conf
<label @aggregate>
    <match **>
        @type kinesis_streams_aggregated
        region ap-northeast-1
        stream_name ${stream_name}
        include_tag_key true
        flush_interval 60s
        buffer_chunk_limit 1m
        buffer_type file
        buffer_path /var/tmp/buffer/kinesis
        compression zlib
    </match>
</label>

以下の条件のどちらかを満たす場合、FluentdはKinesisにデータを送信します。

  • ログバッファサイズが1MBを超える
  • 1分が経過する

Lambdaの設定

Lambdaでやることは以下のとおりです。

  • KPLで圧縮されたレコードを紐解く
  • データ自体はgzipで圧縮されているためそれの解凍
  • 受け取ったデータをS3に出力

検証

compressionオプションを無効にした状態と有効にした状態で実行し、比較します。

# ログ出力を1万回実行
./test.sh 10000

結果がこちら

compressionの有無 出力ファイル数 出力ファイルの平均サイズ
無効 62 約1.3MB
有効 12 約7.0MB

compressionオプションを有効にすると、これだけの差が出ました。
Kinesisの1シャードは、「1秒間に1MBのデータしか受け付けない」という制限がありますが
1回のログ送信で約7MB分のログデータを送れたことになるので、かなりインパクトがでかいです。

上記検証では、1レコードサイズにしては巨大なサイズにしてしまったので、
よくある感じのログレコードを大量に出力させたいと思います。

# sample log
127.0.0.1 - - [02/Jan/2017:23:46:58 +0900] "GET /favicon.ico HTTP/1.1" 200 318 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0) Gecko/20100101 Firefox/17.0" "-"
# ログ出力を1万回実行
./test.sh 10000

結果がこちら

compressionの有無 出力ファイル数 出力ファイルの平均サイズ
無効 3 約540KB
有効 3 約540KB

Fluentdのbuffer_chunk_limitを1MBにしている影響で、
1分(flush_interval)が経過しログが送信されたので違いがなくなっています。
buffer_chunk_limitを100KBにしてみます。

compressionの有無 出力ファイル数 出力ファイルの平均サイズ
無効 25 約64KB
有効 23 約70KB

若干ではありますが、compressionオプションを有効にしたほうがファイル数は少なく済みました。

検証結果

  • ログの圧縮により、Kinesisへの送信回数を抑えることが出来る
  • シャードのサイズ制限以上のログレコードを送信することが出来る

最後に

今回はKPLを使用しましたが、通常のKinesis Streamの場合は
1レコードずつ圧縮して送信されるため、通信経路上もサイズ削減の恩恵を受けることが出来ると思います。

可能ならば、fluent-plugin-kinesisのVersionを2にあげて圧縮を有効にしたほうがいいかもしれません。

では!