エンジニアリング

Logstashに最適なRedis

Logstashのデプロイでは、 Elasticsearchやその他のダウンストリームコンポーネントの処理速度低下につながる可能性があるイベントの急増を制御するために、メッセージキューが使用されます。Redisは、メッセージキューとして使用される技術の1つです。高速で使いやすく、リソース消費も少ないため、最も人気がある方法の1つでもあります。

5年以上前にリリースされた Logstashバージョン1.0.4以降、ずっと利用されているのはこのためです。

Logstashパイプラインの改善、そしてRedisプラグインそのものの改善のために最近多数の変更が加えられたため、 Logstash Redis入力プラグインが提供するパフォーマンスメリットを完全に実現できないことがあります。

Logstashは、システムのコアの数を検知して、それに応じたパイプラインワーカー数を設定します。ただし、パイプラインワーカーが関係するのはフィルタと出力プラグインだけで、入力プラグインには影響しません。Reds入力プラグインに「スレッド」を設定しておかないと、一部のメリットしか活かせないのです。

テストセットアップでは、2015 MacBook Proなどの同じマシンで、最近の最も安定したバージョンであるLogstash (2.3.4) と Redis 3.0.7を使用しました。使用したLogstashの構成は次のとおりです:

input {  
  redis {
    host => "127.0.0.1"
    data_type => "list"
    key => "redis_test"
    # batch_count => 1
    # threads => 1
  }
}
output { stdout { codec => dots } }

次を指定して実行:bin/logstash -f some_config_file.config | pv -Wart > dev/null

このテストセットアップは最大スループットをテストするだけが目的です。実際のパフォーマンスの数字は、どのプラグインを使用するかによって大きく変わってきます。

他のすべての設定はデフォルトのままにして、threads設定のみについていくつかのオプションを指定して繰り返しテストした結果、次のような数値が得られました:

  • threads => 1 : 37.4K/sec
  • threads => 2 : 57K/sec
  • threads => 4 : 67K/sec
  • threads => 8 : 87.3K/sec

今度は、 batch_count => 250と設定し、この増加分をフルに活用できるように、Logstash コマンドラインにも-b 250 を追加して、同じテストを実行してみましょう。:

  • threads => 1 : 35.2K/sec
  • threads => 2 : 48.9K/sec
  • threads => 4 : 58.7K/sec
  • threads => 8 : 72.5K/sec

ご覧のように、デフォルトのbatch_count (つまり、125)を 250 に増やすと、パフォーマンスは低下します。

これまでは、Redis を使用するときにはbatch_countを増やすのが一般的でした。もうベストプラクティスというものはなく、実際にはパフォーマンスが低下してしまいます。主な原因は、Logstash パイプラインのアーキテクチャが変わったことです。batch_countを 250 に増やしても、threads => 8の設定では、パフォーマンスは 89.5K/秒から 72.5K/秒 に落ちてしまいます。batch_countのスイートスポットは 125 前後で、これはpipeline-batch-sizeです (これにはちゃんとした理由があります!)。

さらに詳しく

Redis 入力プラグインには最近、デフォルトでバッチモードをオンにして Redis サーバーでのバッチ取得コマンドの実行にLua scriptを使用する、という変更が加えられました。Lua スクリプトはトランザクションとしての役割も果たすため、1ステップでバッチを取得してキューを短くすることができます。こうすると、他の Radis 接続は強制的に待機状態になります。アップストリーム Logstash 構成の Logstash Redis 出力プラグインはこうした接続の 1 つです。

また、アップストリームの Logstash がイベントをどんな方法で Redis に追加するかによっても、ダウンストリームの Logstash インスタンスのパフォーマンスは変わります。たとえば、今回のテスト結果を出す間に、redis_cliLLEN redis_testを使ってリストサイズを取得すると、測定値にしばしば影響することが判明しました。アップストリームの Logstash インスタンスでは、次のような要素を理解しておく必要があります:

  • イベントの生成速度
  • イベントのサイズ (最小、最大、平均)
  • Redis 出力に使用される batch_events のサイズ

アップストリームの Redis 出力には、500 などの大きな batch_eventsサイズを使うと良いかも知れません。これで Redis を呼び出す前にイベントがバッファに入り、ダウンストリームの Redis 入力スレッドが Redis からプルする時間の余裕ができます。ただし、イベント生成速度が高い場合は、それほど効果はありません。500 バッチ単位で 50K/秒となると、バッチバッファは 10 ミリ秒でいっぱいになってしまいます。

これまで説明してきたように、Lua スクリプトのパフォーマンスはバッチサイズが大きくなると比例して低下することが分かりました。つまり、Redis 入力のバッチサイズが大きければ大きいほど、Redis の上下両側の Logstash インスタンスのパフォーマンスが影響を受けることになるわけです。

通常、Redis 入力プラグインの設定と、パイプラインバッチサイズ、ワーカースレッドを調整する上での目標は、入力がフィルタ/出力ステージに追いつけるようにすることです。デフォルト設定でのフィルタ/出力ステージの平均スループットがどれくらかを把握しておく必要があります。たとえば、フィルタ/出力ステージのスループットが 35K/秒である場合、1 つか 2 つのワーカーでも 1 秒あたりに十分な数のイベントをフェッチできるので、Redis 入力ワーカー数を 8 に変更する必要はほとんどありません。

もう 1 つのバリエーションは、JSON エンコード(1)、デコード (2)、そしてエンコード(3)です。そうです、3 回やるんです。アップストリームの Redis 出力で 1 回、ダウンストリームの Redis 入力で 1 回、Elasticsearch 出力で 1 回です。大きな文字列値を持つイベントのデコードには時間がかかるため、サイズとサイズ分布が実稼働環境のデータとできるだけ近いデータセットをテストで使用するようにしてください。

複数の Logstash インスタンスと 1 つの Redis サーバーを使用してスループットを調整する

異なるハードウェアで、同じ Redis サーバーとリストをポイントする複数の Logstash インスタンスを使ってスループットの向上を図ろうとするケースが多いようです。

確かにこの方法はうまく行くこともあります。ただ、各インスタンスに同じチューニングヒントを適用することをお勧めします。Redis inputが別々の Redis サーバーやリストに接続されていない限り、1 つの構成に複数の Redis inputを使ってもあまり意味はありません。1 つの Redis inputのスレッドを調整するだけで良いでしょう。たとえば、3 つの Logstash インスタンスが同じ Redis サーバーをポイントしていると、生成される Redis クライアント接続も同程度になります。つまり、1 つのインスタンスを調整してから、インスタンスを一緒に実行しながら、3 つすべてのインスタンスを再調整する必要があります。

まとめ

それほど遠くない将来に、人気の高い高速な Jedis Java ライブラリをRuby ラッパーでくるんで Redis 入力を強化する予定です。これで Redis クラスタモードもサポートされるというオマケもついてきます!

それまでは、Redis inputのパフォーマンスを高めるには、使用するスレッドの数を増やしてみてください。デフォルトのbatch_count以外の数字を使う場合や、1 つの Redis サーバーをポイントする複数の Logstash インスタンスを使う場合には、数字を測定してそれに応じて調整をするようにしてください。

Redis や Logstash についてご不明な点がありましたら、遠慮なくフォーラムディスカッションをオープンしてください。IRC (#logstash) や GitHub でも質問できます。


Happy Logstashing!