Armadilloフォーラム

[Armadillo-IoT] fluentdを実行時のメモリ不足について

sisoshima

2015年7月14日 16時39分

お世話になっております。五十島と申します。

Armadillo-IoTからTreasure Dataにログデータを格納する際に、fluentdを使用しています。
fluentdのメモリ使用量がいつの間にか肥大化しており、他プログラムを実行すると
「Cannot allocate memory」というエラーが出て、実行したプログラムが異常終了します。

fluentdのメモリ使用量を減らすことはできるでしょか。

fluentdの設定は下記記事を参考に行いました。
https://users.atmark-techno.com/blog/46/1219

/etc/config/fluent.confに次のように記述しています。

<source>
type tail
format json
path /etc/config/fluent/fluent-log
pos_file /etc/config/fluent/tail.pos
tag td.db_name.table_name
</source>
 
<match td.*.*>
type tdlog
apikey ***********
buffer_type memory
flush_interval 60s
use_ssl true
</match>

以上、よろしくお願いします。

コメント

at_takashi.sasayama

2015年7月15日 16時12分

笹山です。

> Armadillo-IoTからTreasure Dataにログデータを格納する際に、fluentdを使用しています。
> fluentdのメモリ使用量がいつの間にか肥大化しており、他プログラムを実行すると
> 「Cannot allocate memory」というエラーが出て、実行したプログラムが異常終了します。

fluentd が複数起動していることは無いでしょうか?

過去に、誤って fluentd を複数起動してしまった結果、似た問題が発生した事例がありました。
同じ問題かもしれませんので、ご確認いただければと思います。

返信ありがとうございます。

psコマンドで確認してみましたが、複数起動させていないと思います。
プロセスは2つ動いていて、片方のメモリの使用量が増えていくような動きをしております。

[guest@armadillo-iotg (pts/0) ~]$ ps -o pid,rss,args | grep -e fluentd -e PID
PID   RSS  COMMAND
 1145  10m ruby /usr/bin/fluentd -d /var/run/fluentd.pid -c /etc/config/fluent.conf
 6083  33m ruby /usr/bin/fluentd -d /var/run/fluentd.pid -c /etc/config/fluent.conf

よろしくお願いします。

大澤です。
tdlog pluginの flush_intervalを 1s程度の小さい値にしてみてください。

tdlog pluginは buffer plugin で実装されているので、
flush_interval の期間内は buffer_chunk_limit * buffer_queue_limit
(=512MB: デフォルト値) までデータをバッファしてしまいます。

他のbuffer plugin実装なプラグインを使う場合についても、
同様にflush intervalを小さく取る必要があります。

また、buffer plugin以外の要因では、rubyのgcが走る閾値がデフォルト
だと高すぎるので、fluentdを実行するシェルの環境変数に、下記の値を
exportして試してみてください。

export RUBY_GC_MALLOC_LIMIT=2000000
export RUBY_GC_MALLOC_LIMIT_MAX=8000000

この値でユースケースに合うか分らないのですが、
GCが走りすぎて遅い等の問題が無ければ、そのまま使えると思います。

RUBY_GC_MALLOC_LIMIT_MAXはの目安はfluentdを起動した直後の
メモリのfreeより少ない値にしてください。

Armadillo-IoTの標準イメージだと起動直後で32MB程度freeで
fluentdを起動するだけで 19MB feeeまで消費されるので、
RUBY_GC_MALLOC_LIMIT_MAX は多くても 15MB (15 * 1024 * 1024)
くらいでしょうか。

ご返信ありがとうございます。

設定を変更してみたのですが、fluentdの子プロセスが実行直後で22MBで、
数分後には40MB以上メモリを消費するようになりました。
RUBY_GC_MALLOC_LIMIT_MAXを3000000と極端に小さな値にしてみたら、
18MBぐらいで最初は抑えられていましたが、最終的には40MB以上になりました。

色々と試してみて気づいたのですが、初期起動時は親子プロセス合わせて28MB程度しか使われないようです。
ただし、ネットワークの不具合で転送できなかったり、tailで読み込むものがなくなると増加していくようです。
これは、GCがうまく動作していないのか、fluentdがそもそもメモリ消費量を気にしない作りなのか。

もし、何かわかればご助言いただけると幸いです。
fluent-bitという軽量版があるようなので、そちらも検討しています。

> 色々と試してみて気づいたのですが、初期起動時は親子プロセス合わせて28MB程度しか使われないようです。
> ただし、ネットワークの不具合で転送できなかったり、

ネットワークの不具合がある場合、TreasureDataLogOutput は、BufferedOutput なので
メモリ使用量はある一定量(buffer_queue_limit)まで増え続けます。このメモリは、
必要なデータがはいっているバッファなので、いくらGCが動いても消せません。

fluentd的には、ネットワーク障害があってもデータをロストしないことを
売りにしている(はずな)ので、このような作りだと理解しています。

http://docs.fluentd.org/articles/buffer-plugin-overview

> tailで読み込むものがなくなると増加していくようです。

こっちは、なんでしょうね....。

ありがとうございます。段々と理解してきました。

> メモリ使用量はある一定量(buffer_queue_limit)まで増え続けます。このメモリは、
> 必要なデータがはいっているバッファなので、いくらGCが動いても消せません。

つまりは、一回メモリをある量まで確保してしまった場合、
fluentdを終了させるまで確保され続けるという理解でいいんですよね。
(送信データの有無関係なく)

buffer_chunk_limit と buffer_queue_limitのサイズを小さくすれば、使用量は抑えられるけど、
遅延などが発生してqueueに収まらない量になった場合にデータが欠落してしまうんですね。
だとすると、in_tailでレコードが追加されたら読み込むよりも、一定期間たったら読み込む等、
input側で制御すると多少はfluentdが占有してしまうメモリ量を減らせるかもしれないですね。

詳しい説明ありがとうございます。

> > メモリ使用量はある一定量(buffer_queue_limit)まで増え続けます。このメモリは、
> > 必要なデータがはいっているバッファなので、いくらGCが動いても消せません。
>
> つまりは、一回メモリをある量まで確保してしまった場合、
> fluentdを終了させるまで確保され続けるという理解でいいんですよね。
> (送信データの有無関係なく)

勘違いさせてごめんなさい。「いくらGCが動いても、『送信されていないデータが
入っているメモリー (chunk) は』消せません。」です。一度送信されてしまえばメモリーは、
Queueから外されて、GCに回収されます。コードでいうと

buffer.rb::pop() -> buffer.rb::write_chunk() -> out_tdlog.rb::write() 

という流れで、chunkが TDに uploadされます。

pop()のスコープでは次に、delete_if() で今 upload した chunk を @queue から消します。
これで、upload した chunk へのリファレンスが消えるはずなので、次の GCのタイミングで
メモリが開放されます。(ruby 2.1 からは generation gc なので、chunk の世代が進んでいたら
旧世代の sweep でしか消されませんが、概念的には次です)

> buffer_chunk_limit と buffer_queue_limitのサイズを小さくすれば、使用量は抑えられるけど、
> 遅延などが発生してqueueに収まらない量になった場合にデータが欠落してしまうんですね。

ですね。

> だとすると、in_tailでレコードが追加されたら読み込むよりも、一定期間たったら読み込む等、
> input側で制御すると多少はfluentdが占有してしまうメモリ量を減らせるかもしれないですね。

fluentd は、基本的に in 側のタイミングで処理がスタートするアーキテクチャです。TailInputは
1秒固定ですが NewTailInput の場合は refresh_interval で時間をコントロールできます。
bufferedの場合は、out 側で thread をつくるので in側とは独立で動作します。

in側のタイミングをあまり気にしないのであれば、今の tdの plugin のように buffered outputからの
派生ではなく、output からの派生の方がストレートですね。ただネットワークの遅延が
そのまま fluentd の eventloop に影響を及ぼすので、そっちはそっちで考えることがありそうです。
ただ、内部 buffer にデーターが貯まることはないはずです。

fluent-bit の初期のコードは、bufferedoutput というより non-buffered な outputの実装になっていました。