読者です 読者をやめる 読者になる 読者になる

Mercari Engineering Blog

We're the engineers behind Mercari. Check out our blog to see the tech that powers our marketplace.

fluent-agent-hydraで省エネログ転送

Site Reliability Engineering Teamの@cubicdaiyaです。

今回はGo製のログ転送エージェントであるfluent-agent-hydraとメルカリでの利用事例について紹介します。

メルカリとFluentd

メルカリではAPIサーバのアクセスログやアプリケーション固有のログをはじめとする各サーバに散らばっているログデータを転送・集約するのにFluentdを活用しています。

f:id:cubicdaiya:20151214052826p:plain

具体的にはローカルに書き込まれるログファイルのin_tailやそれらを転送するための(out|in)_forward、ElasticsearchやBigQueryにログを放り込むためのプラグインを利用しているほか、いくつか特殊な用途のプラグインを独自に開発して運用してたりもします。

ログの流量とFluentdのパフォーマンス

多機能で柔軟なプラグイン機構を持つ便利なFluentdですが、ログの流量が大きくなってくるとパフォーマンスの面で辛くなることがたまにあります。そういった場合はフィルター等の重い処理の利用を見直したり、multiprocessプラグインでFluentdが複数のCPUコアを利用できるようにして性能を稼ぐといった対応が考えられます。中でもmultiprocessプラグインは1プロセスで複数のCPUコアを活用できないFluentdにとって非常に大きな性能向上をもたらしてくれます。以下は複数のFluentdプロセスをmultiprocessプラグインでforwarderとして稼働させている様子です。

f:id:cubicdaiya:20151214051718p:plain

ただ、multiprocessプラグインは効果が高い一方で若干運用しづらい面があります。例えば、

  • プロセスが多くなる(監視対象が増える)
  • listenするポートが増える(e.g. in_forwardを利用する場合)

といったことが挙げられます。また、ログのフォーマットにもよりますがin_tailとout_forwardでそれなりに書き込み量の多いログを複数個転送するようなケースだとFluentdプロセスのCPU使用率が高くなりがちです。実際、メルカリでは一部のサーバでFluentdのCPU使用率が無視できないくらい高くなるケース(in_tail & out_forwardの1プロセスで50〜60% at peak)が出てきました。

こういった事情から現在はFluentdとより低リソースで稼働するfluent-agent-hydraを併用しています。

fluent-agent-hydra

github.com

fluent-agent-hydraはFluentdやそのための各種ロガーとやりとりができるログ転送エージェントです。以下の機能や特徴を備えています。

  • in_tail, (in|out)_forwardができる
  • HTTPベースのモニタリングAPIが利用可能
  • 1プロセスで複数のログをハンドリングできる
  • ラウンドロビン転送をサポート
  • ログフォーマットとしてLTSV、JSONをサポート
  • Fluentdよりも高速・低リソースで動作する
  • TOMLによるコンフィギュレーション

ただし、今のところFluentdのpos_fileやファイルバッファに相当する機能がないのでその点には注意が必要です。

分析基盤のログ転送エージェントとして導入

以前本ブログでも紹介したメルカリのログ分析基盤Pascalですが、当時は以下のような構成になっていました。

以前の構成

f:id:cubicdaiya:20151215180256p:plain

OpenRestyが出力する複数のログファイル(JSON)をFluentdがin_tail & out_forwardで転送するという構成です。現在はこの部分がfluent-agent-hydraに置き換わっています。また、この図には描かれていませんが、Pascalにはアプリからだけでなく、内部のAPIサーバからもFluentd経由でリクエストが飛んでくるようになっています。

現在の構成

f:id:cubicdaiya:20151215224341p:plain

実際に導入するにあたって少し機能を拡張する必要がありましたが、結果的にログ転送プロセスのCPU使用率は約半分になりました。

fluent-agent-hydraのアーキテクチャ

fluent-agent-hydraはin_tailやout_forward、monitor等の機能を担う各ゴルーチンが協調動作するアーキテクチャになっています。最初に起動したゴルーチンは各機能のためのゴルーチンを起動した後シグナルを待ち受ける状態になります。

f:id:cubicdaiya:20151214165252p:plain

各ゴルーチンはチャネルを利用してデータのやり取りをします。矢印はデータの流れる向きを表していて、monitorのゴルーチンはfluent-agent-hydraの各種統計や監視に必要な情報を各ゴルーチンから受信し、out_forwardのゴルーチンはin_tailやin_forwardのゴルーチンからメッセージを受信する仕組みです。

f:id:cubicdaiya:20151214053756p:plain

TOMLによるコンフィギュレーション

fluent-agent-hydraでは動作設定をTOMLで記述することができます(設定なしでも起動できます)。以下はその例です。

ReadBufferSize = 1048576 # 1MB
ServerRoundRobin = true

# 追尾するログ
[[Logs]]
File = "/var/log/nginx/access.log"
Tag = "nginx.access.log"
Format = "LTSV" # or JSON

# 転送先ホスト
[[Servers]]
Host = "forward-server1"
Port = 24224

# 転送先ホスト
[[Servers]]
Host = "forward-server2"
Port = 24224

# モニタリングAPIのエンドポイント
[Monitor]
Host = "localhost"
Port = 24200

設定ファイルを指定して起動するには-cオプションを利用します。

$ fluent-agent-hydra -c /etc/hydra/hydra.toml

より詳細な設定例については公式のREADME.mdを参照するとよいでしょう。

fluent-agent-hydraのモニタリング

fluent-agent-hydraはモニタリング用のAPIを備えており、/systemロケーションにアクセスするとGo内部(GCやメモリアロケーション、起動しているゴルーチンの数)に関する稼働状況が確認できます。

$ curl -s http://localhost:24200/system | jq ʻ.ʼ
{
  "gc_pause": [
    0.412521
  ],
  "gc_pause_per_second": 0.412521,
  "gc_per_second": 0.12935386000134802,
  "gc_num": 5435,
  "gc_last": 1449825990702757400,
  "gc_next": 99945471,
  ...

また、それ以外のロケーションにアクセスするとfluent-agent-hydraの稼働状況(追尾しているファイルのポジション、転送したメッセージの数やバイト数等)を確認できます。

$ curl -s http://localhost:24200/ | jq ʻ.ʼ
{
 "receiver": null,
 "servers": [
 {
   "error": "",
   "alive": true,
   "address": "forward-server1:24224"
 },
 {
   "error": "",
   "alive": true,
   "address": “forward-server2:24224"
 }
 ],
 "files": {
   “/var/log/nginx/access.log“: {
   "error": "",
   "position": 472132311,
   "tag": “nginx.access_log”
 },
 …
 …

メルカリではこれらの情報を元にZabbixでfluent-agent-hydraの稼働状況を確認しています。例えば追尾しているファイルのポジションが正常に更新されているかどうかは以下のようなファイルを用意してトリガーを設定することで確認することができます。

UserParameter=hydra[*], curl -s http://127.0.0.1:24200/ | jq '.files["$1"].position'

ポジションが一定時間更新されてないのを検知するとこんな感じでSlackに通知されます。

f:id:cubicdaiya:20151214052314p:plain

まとめ

Go製のログ転送エージェントであるfluent-agent-hydraとメルカリでの利用事例について紹介しました。今回の解説は先日開催されたGo Conference 2015 Winterの資料を元にしています。興味がある方はそちらも御覧ください。