fluentdの負荷分散のためにmulti processを有効にする
ログ -> fluentd -> fluentd -> ストレージ のような流れで fluentd を酷使していると、1コアしか使えない fluentd が悲鳴を上げて
そこがボトルネックとなってスループットが上がらない問題にぶつかります。
そこで使えるのが fluent-plugin-multiprocess というプラグインです。
他の input plugin と同様に source を定義して複数の fluentd の子プロセスを呼び出すことでマルチコアの利用が可能になります。
docker-fluentd-multiprocess/fluent.conf at master · innossh/docker-fluentd-multiprocess · GitHub
<source> @type multiprocess <process> cmdline -c /fluentd/etc/fluent1.conf --log /fluentd/log/fluent1.log pid_file /fluentd/run/fluentd1.pid </process> <process> cmdline -c /fluentd/etc/fluent2.conf --log /fluentd/log/fluent2.log pid_file /fluentd/run/fluentd2.pid </process> </source>
文字通り ruby で子プロセスを呼び出しているので、それぞれ log や pid の指定をしてあげて logrotate などを設定する必要があります。
また、よく使うであろう monitor_agent
の設定も各子プロセスごとに別のポートで定義する必要があります。
こうして multiprocess 設定を行う時に、なんとか複数の設定ファイルをうまいディレクトリ構造に配置できないか考えたところ、次のようになりました。
├── fluent.conf : メインの設定ファイル ├── fluent.d : 全子プロセス共通の設定ファイル用ディレクトリ │ └── out-forward.conf ├── fluent1.conf : 子プロセス1の設定ファイル ├── fluent1.d : 子プロセス1の設定ファイル用ディレクトリ │ ├── in-forward.conf │ └── include.conf ├── fluent2.conf : 子プロセス2の設定ファイル └── fluent2.d : 子プロセス2の設定ファイル用ディレクトリ ├── in-forward.conf └── include.conf
プロセスの数が増えると結局地獄が待っているのですが、設定を増やす時に対象のディレクトリに置くだけで良いというメリットがあります。 include.conf の中身はこのようになっています。
docker-fluentd-multiprocess/include.conf at master · innossh/docker-fluentd-multiprocess · GitHub
@include ../fluent.d/*.conf
また一つのポイントとして、 全子プロセス共通の設定ファイル用ディレクトリ
があることで、設定ファイルの数を減らすことができます。(やりたい設定の数*プロセスの数
から (やりたい設定の数-共通化できる設定の数)*プロセスの数+共通化できる設定の数
に減る)
tail input plugin などは1つのファイルを読むのを多重化することはできないので共通化できない設定となりますが、 output plugin では負荷分散が目的の場合、大抵のものは同じ設定を複数のプロセスで読み込んでくれた方が管理が楽なわけです。
しかしここで問題となるのが buffer_path
の設定です。 mysql, postgresql, s3, elasticsearch にしても何にしてもデータをロストさせないためにbuffer fileの設定をしているかと思います。
buffer_type
を file にしている場合に、先ほどの共通化できる設定で buffer_path
を定義していると、複数の子プロセスで同じ buffer_path
を参照してしまってエラーが発生してしまいます。
困ったもんだということで、その解決方法は以下です。
buffer_path
に tag 名を含めるようにして、各子プロセスで別々の tag を設定する- 設定の共通化を諦めるw 別々の設定ファイルで別々の
buffer_path
を設定する
前者は今回のサンプルリポジトリを参考にしてください。ただ tag 書き換えてるだけなんですけど。
もっとうまい方法はないのだろうかと環境変数を使う方法なども考えてはみましたがうまくできませんでした。
やっぱり plugin で実現しているだけあってつらみが多いです。とはいえちゃんとマルチコアを利用できてスループットが上がったので良かったのですが。
もんもんとしたままでベストな方法が見つからなかったのですが、 fluentd バージョン0.14系では本体で multiprocess 化が対応しています。やったね!
forward input plugin を複数定義して別々のポート設定してみたいなことが必要なくなり、それはつまり転送元の forward output plugin で複数のポートへ転送する設定を定義しなくてよいことにもなります。さらにv0.14.15からは <worker N>
で指定した worker だけで動かす source の定義も可能です。
また monitor_agent
も自動で worker ごとにポートを連番で設定してくれるので特に定義する必要がありません。
サンプルではv0.14の設定ファイルがとても簡潔になっています。
docker-fluentd-multiprocess/fluent.conf at master · innossh/docker-fluentd-multiprocess · GitHub
<system> workers 2 </system> <source> @type monitor_agent port 25000 </source> <source> @type forward port 24224 </source> <filter docker.**> @type parser format /^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*) +\S*)?" (?<code>[^ ]*) .*$/ time_format %d/%b/%Y:%H:%M:%S %z key_name log </filter> <filter docker.**> @type record_transformer remove_keys container_id,container_name,source,remote,host,user </filter> <match docker.**> @type forward <server> host fluentd-out port 24224 </server> buffer_type file buffer_path /fluentd/log/buffer/ </match>
ちなみに buffer file のディレクトリ構造はこんな感じです。
# v0.12 : tag の設定でがんばったやつ fluentd-multi/log/buffer fluentd-multi/log/buffer/child1.docker.dockerfluentdmultiprocess_nginx_1 fluentd-multi/log/buffer/child1.docker.dockerfluentdmultiprocess_nginx_1/.child1.docker.dockerfluentdmultiprocess_nginx_1.b550bf3334898a188.log fluentd-multi/log/buffer/child2.docker.dockerfluentdmultiprocess_nginx_1 # v0.14 fluentd14-multi/log/buffer fluentd14-multi/log/buffer/worker0 fluentd14-multi/log/buffer/worker0/buffer.b550bf3334ce14b5232c3c1601c6c06b9.log fluentd14-multi/log/buffer/worker0/buffer.b550bf3334ce14b5232c3c1601c6c06b9.log.meta fluentd14-multi/log/buffer/worker1
良き fluentd ライフを。