fluent+dstat+kibana4でのサーバリソース可視化

コメントいただいた id:a3no さんの記事のほうが良いので、そちらを参照しましょう。a3no.hatenablog.com



普段はmuninやzabbixのグラフでまあいいかというサーバリソースグラフが、秒単位で見たいというケースがあって色々試行錯誤してできたもの。
rrdtoolで1秒を対応するグラフを作るという手もあるのだけど、なかなか秒単位にいじれるフロントエンドツールが無くて Elasticsearch 触ったこと無かったしで、Web記事を参考にして作ってみることにしました。

参考URL

dstatをkibanaで可視化+3.0.0milestone5新機能 - ほわいとぼーど
curator で Kibana 用の elasticsearch のインデックスを定期的に削除する - Qiita
Use relative paths instead of absolute · Issue #1630 · elastic/kibana · GitHub
Elasticsearch Kibana beta behind Apache Proxy - mmbash.de

作った時期が比較的最近で、fluentdやkibanaのバージョンが違っててなんかWeb上の設定から色々変わってて、これでいいのか?という設定になったけどまあ動いてるのでとりあえずメモということで。

バージョン

OS:CentOS 6.6
elasticsearch-1.6.0
td-agent-2.2.0-0
kibana-4.0.3

注意点とか考慮点(殆ど fluentd )

  • fluent-plugin-map の設定記述方法が fluentd の v1config と合わないパターンがあるので注意
  • elasticsearch 側でデータ型自動判別が上手くいくように fluent-plugin-typecast でデータ型変更
  • elasticsearch に入れる時点で、@timestamp を予め付与してデータのタイムスタンプはなるべく正確に
  • elasticsearch に入れる時点で、ホスト名を予め付与して elasticsearch 側でホスト名フィルタを使えるように
  • kibana の設定で reverse proxy 経由アクセスを予定して elasticsearch_url の書き換えを行う
  • dstat取得ノードから直接 ES に入れることも考えたけど、テキストログの集約保存もしたかったため結局一旦 ES ホスト上の fluentd に集めてから ES に入れる方向に

インストール手順

※なるべく外に取りに行かずにできるように予めパッケージ類落としてきて入れようとしました…が、curator 忘れてて結局外部見にいってしまっていますorz 普通に使う分にはyumとか適当に使えばいいと思います!

elasticsearch

alternatives でjavaシンボリックリンク切り替えていますが、java環境の整え方はお好みでしょう。

yum install java-1.8.0-openjdk.x86_64
alternatives --display java
rpm -ivh elasticsearch-1.6.0.noarch.rpm
chkconfig --add elasticsearch
td-agent

td-agent の設定は後述します。

rpm -ivh td-agent-2.2.0-0.x86_64.rpm
chkconfig td-agent on
/opt/td-agent/embedded/bin/fluent-gem install *.gem
cd /var/log/td-agent/
cp -pr buffer/ arch
cp -pr buffer/ pos
vi /etc/td-agent/td-agent.conf

※入れたプラグインリスト:irc通知とか色々見越した余計なプラグインが入ってます
fluent-plugin-slack-0.6.1.gem
fluent-plugin-typecast-0.2.0.gem いる
fluent-plugin-dstat-0.2.5.gem いる
fluent-plugin-forest-0.3.0.gem いる
fluent-plugin-parser-0.5.0.gem
fluent-plugin-config-expander-0.1.5.gem いる
fluent-plugin-numeric-counter-0.2.2.gem
fluent-plugin-map-0.0.4.gem いる
fluent-plugin-ikachan-0.2.6.gem
fluent-plugin-snmptrap-0.0.1.gem
fluent-plugin-record-reformer-0.6.3.gem いる
fluent-plugin-notifier-0.2.3.gem
fluent-plugin-elasticsearch-0.9.0.gem いる
fluent-plugin-irc-0.0.7.gem
fluent-mixin-rewrite-tag-name-0.1.0.gem
kibana4

先の注意点に書いていますが kibana.yml で elasticsearch_url をデフォルトの localhost から変更します。
詳しい理屈と原因は追っていませんが、そうしないと reverse proxy apache からアクセスした際に上手くいきませんでした。

cd
tar xzf kibana-4.0.3-linux-x64.tar.gz
chown -R root:root kibana-4.0.3-linux-x64/
cd kibana-4.0.3-linux-x64/config
vi kibana.yml
==
elasticsearch_url: "http://[elasticsearch server ip(not localhost)]:9200"
===
curator

後付で入れたので外に取りに行ってしまった残念?なもの。

wget --no-check-certificate https://raw.github.com/pypa/pip/master/contrib/get-pip.py
python get-pip.py
pip install elasticsearch-curator
curator --version
curator show dstat*

ホストリソース取得ノードの td-agent 設定

<source>
  type config_expander
  <config>
    type dstat
    tag host.dstat.__HOSTNAME__
    option  -tclmsr -dD sda --disk-util -nN bond0,bond1
    delay 5
  </config>
</source>

<match host.**>
  type forward
  send_timeout 60s
  recover_wait 10s
  heartbeat_type tcp
  heartbeat_interval 1s
  phi_threshold 16
  hard_timeout 60s

  <server>
    name es-host
    host [fluent server]
    port 24224
    weight 60
  </server>
</match>

ホストリソース取得ノードの td-agent 設定

<source>
  type forward
  port 24224
</source>

<match host.dstat.*>
  type copy
  <store>
    type forest
    subtype file
    <template>
      time_slice_format  %Y%m%d
      append true
      buffer_type file
      buffer_chunk_limit 100m
      flush_interval 30s
    </template>
    <case **>
      path /var/log/td-agent/arch/__TAG__
      symlink_path /var/log/td-agent/arch/__TAG__.log
      buffer_path /var/log/td-agent/buffer/__TAG__/
    </case>
  </store>
  <store>
    type map
    map '["map." + tag + ".cpu_idl", time , "cpu_idl" => record["dstat"]["total cpu usage"]["idl"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".loadavg_1m", time , "loadavg_1m" => record["dstat"]["load avg"]["1m"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".mem_free", time , "mem_free" => record["dstat"]["memory usage"]["free"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".swap_used", time , "swap_used" => record["dstat"]["swap"]["used"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".io_sda_read", time , "io_sda_read" => record["dstat"]["io/sda"]["read"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".io_sda_write", time , "io_sda_write" => record["dstat"]["io/sda"]["writ"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".dsk_sda_read", time , "dsk_sda_read" => record["dstat"]["dsk/sda"]["read"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".dsk_sda_write", time , "dsk_sda_write" => record["dstat"]["dsk/sda"]["writ"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".sda_util", time , "sda_util" => record["dstat"]["sda"]["util"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".net_bond0_recv", time , "net_bond0_recv" => record["dstat"]["net/bond0"]["recv"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".net_bond0_send", time , "net_bond0_send" => record["dstat"]["net/bond0"]["send"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".net_bond1_recv", time , "net_bond1_recv" => record["dstat"]["net/bond1"]["recv"]]'
  </store>
  <store>
    type map
    map '["map." + tag + ".net_bond1_send", time , "net_bond1_send" => record["dstat"]["net/bond1"]["send"]]'
  </store>
</match>

<match map.host.dstat.**>
  type record_reformer
  enable_ruby true
  tag  timeadd.map.host.dstat
  <record>
    hostname ${tag_parts[3]}
    @timestamp ${time.strftime('%Y-%m-%dT%H:%M:%S%z')}
  </record>
</match>

<match timeadd.map.host.dstat.**>
  type typecast
  item_types cpu_idl:float,loadavg_1m:integer,mem_free:integer,swap_used:integer,io_sda_read:integer,io_sda_write:integer,dsk_sda_read:integer,dsk_sda_write:integer,sda_util:float,net_bond0_recv:integer,net_bond0_send:integer,net_bond1_recv:integer,net_bond1_send:integer
  tag  typecast.timeadd.map.host.dstat
</match>

<match typecast.timeadd.map.host.dstat.**>
  type copy
#  <store>
#    type stdout
#  </store>
  <store>
    type elasticsearch
    type_name       dstat
    host            localhost
    port            9200
    logstash_format true
    logstash_prefix dstat
    flush_interval  3s
  </store>
</match>

ちなみにelasticsearchに入るデータ構造はこんな感じ

2015-06-20 11:21:24 +0900 typecast.timeadd.map.host.dstat: {"sda_util":2.18,"hostname":"dstathost","@timestamp":"2015-06-20T11:21:24+0900"}
2015-06-20 11:21:29 +0900 typecast.timeadd.map.host.dstat: {"sda_util":0.72,"hostname":"dstathost","@timestamp":"2015-06-20T11:21:29+0900"}
2015-06-20 11:21:34 +0900 typecast.timeadd.map.host.dstat: {"sda_util":0.3,"hostname":"dstathost","@timestamp":"2015-06-20T11:21:34+0900"}
2015-06-20 11:21:39 +0900 typecast.timeadd.map.host.dstat: {"sda_util":0.08,"hostname":"dstathost","@timestamp":"2015-06-20T11:21:39+0900"}
2015-06-20 11:21:44 +0900 typecast.timeadd.map.host.dstat: {"sda_util":0.06,"hostname":"dstathost","@timestamp":"2015-06-20T11:21:44+0900"}
2015-06-20 11:21:49 +0900 typecast.timeadd.map.host.dstat: {"sda_util":1.84,"hostname":"dstathost","@timestamp":"2015-06-20T11:21:49+0900"}
2015-06-20 11:21:54 +0900 typecast.timeadd.map.host.dstat: {"sda_util":2.26,"hostname":"dstathost","@timestamp":"2015-06-20T11:21:54+0900"}
2015-06-20 11:21:59 +0900 typecast.timeadd.map.host.dstat: {"sda_util":0.06,"hostname":"dstathost","@timestamp":"2015-06-20T11:21:59+0900"}
2015-06-20 11:22:04 +0900 typecast.timeadd.map.host.dstat: {"sda_util":0.32,"hostname":"dstathost","@timestamp":"2015-06-20T11:22:04+0900"}
2015-06-20 11:22:09 +0900 typecast.timeadd.map.host.dstat: {"sda_util":1.42,"hostname":"dstathost","@timestamp":"2015-06-20T11:22:09+0900"}
2015-06-20 11:22:14 +0900 typecast.timeadd.map.host.dstat: {"sda_util":1.32,"hostname":"dstathost","@timestamp":"2015-06-20T11:22:14+0900"}
2015-06-20 11:22:19 +0900 typecast.timeadd.map.host.dstat: {"sda_util":0.12,"hostname":"dstathost","@timestamp":"2015-06-20T11:22:19+0900"}
2015-06-20 11:22:24 +0900 typecast.timeadd.map.host.dstat: {"sda_util":1.72,"hostname":"dstathost","@timestamp":"2015-06-20T11:22:24+0900"}

elasticsearchのインデックス定義

name 	type 	analyzed  	indexed  	
------------------------------------
_index  	string	false	false	
loadavg_1m  	number	false	true	
hostname  	string	true	true	
cpu_idl  	number	false	true	
swap_used  	number	false	true	
io_sda_write  	number	false	true	
_type  	string	false	true	
io_sda_read  	number	false	true	
dsk_sda_read  	number	false	true	
dsk_sda_write  	number	false	true	
@timestamp   	date	false	true	
mem_free  	number	false	true	
_source  	string	false	false	
_id  	string	false	false	
sda_util  	number	false	true	
net_bond0_recv  	number	false	true	
net_bond1_send  	number	false	true	
net_bond0_send  	number	false	true	
net_bond1_recv  	number	false	true

kibana4設定

画像かき集めて無いので無し

kibanaアクセスのためのReverseProxy?定義

nginx
 location ~* /kb4/.* {
       rewrite ^/kb4/(.*) /$1 break;
           proxy_pass http://[kibana server]:5601;
           proxy_set_header Host $host;
           proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_send_timeout 600s;
       proxy_read_timeout 600s;
 }
apache
ProxyRequests On
ProxyPreserveHost On
ProxyPass               /       http://[kibana server]:5601/
ProxyPassReverse        /       http://[kibana server]:5601/
RewriteEngine on
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule .* http://[kibana server]:5601%{REQUEST_URI} [P,QSA]

作ってみて

dstat + fluentd + Graphite + Grafana でサーバモニタリングする - blog.nomadscafe.jp の記事にありますが、ESのCPU使用率がきついです。
あと、dstat 5秒間隔取得データで、ES のヒープ 2G で 20日データ保存が安定稼動ラインで、それを超えるとメモリ不足や search の queue size エラーが出はじめます。ES のチューニングやトラブル対応のネタ的にはいいのですが、予想以上にデータ溜められないかもな…という印象。とりあえず数日の可視化はできているので、目標的にはOK。ES の調整は30日までは頑張って、後は fluentd で集めたテキストログ見ればいいやという割り切りでしばらく使うことに。