wiki:linux/Fluentd

Fluentでログ管理

CentOSにFluentdをインストール

下記のコマンドを叩くと、Fluentd開発元のTreasuredata謹製のFluentdがインストールされる。

$ curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh

プロキシ経由でWebにアクセスする場合は、/etc/yum.confにプロキシの設定を記述する。

/etc/init.d/td-agent start

でFluentdのエージェントが起動。

使ってみる

HTTP経由でログ出力、

$ curl -X POST -d 'json={"post":"message"}' http://localhost:8888/debug.post

fluent-catを利用してログ出力

$ echo '{"cat":"message"}' |/opt/td-agent/embedded/bin/fluent-cat  --json debug.cat

/var/log/td-agent/td-agent.logにログが出力される。

2015-11-07 12:53:46 +0900 debug.post: {"post":"message"}
2015-11-07 12:54:18 +0900 debug.cat: {"cat":"message"}

OpenStackのログを管理してみる

例えば、こんな感じのログがあるとする。

2014-05-21 13:01:55.215 6322 INFO nova.osapi_compute.wsgi.server [-] (6322) accepted ('192.168.66.128', 42247)
2014-05-21 13:01:55.968 6322 DEBUG nova.api.openstack.wsgi [-] No Content-Type provided in request get_body /usr/lib/python2.7/dist-packages/nova/api/openstack/wsgi.py:835
2014-05-21 13:01:55.998 6322 DEBUG nova.api.openstack.wsgi [-] Calling method <bound method Versions.multi of <nova.api.openstack.compute.versions.Versions object at 0x3908150>> _process_stack /usr/lib/python2.7/dist-packages/nova/api/openstack/wsgi.py:962
2014-05-21 13:01:56.286 6322 INFO nova.osapi_compute.wsgi.server [-] 192.168.66.128 "GET /v237da0ad900aa4119bc743d59975e1bce/images/detail HTTP/1.1" status: 300 len: 486 time: 0.6088369

Fluentdは、正規表現を利用してログを解析し、タグ(属性)をつけて保存することができる。

<source>
  type tail
  path /var/log/nova/nova-api.log
  pos_file /var/log/td-agent/nova-api.log.pos
  tag nova-api.log
  format /^(?<date>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}) (?<pid>[^ ]*) (?<level>[^ ]*) (?<class>[^ ]*) \[(?<req-id>[^]]*)] (?<message>.*)$/
</source>

<match nova-api.log>
  type file
  path /var/log/td-agent/openstack.log
</match>

/var/log/td-agent/openstack.log.xxx ファイルにログが書き出されるようになる。

スタックトレースなど、複数行に渡るログを正しく取得するには、format multilineを利用する。

<source>
  type tail
  path /var/log/nova/nova-api.log,/var/log/nova/nova-manage.log
  pos_file /var/log/td-agent/os.log.pos
  tag os.control
  format multiline
  format_firstline /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3} /
  format1 /^(?<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).\d{3} (?<pid>[^ ]*) (?<level>[^ ]*) (?<class>[^ ]*) (\[(?<req-id>[^]]*)] |)(?<message>.*)/
  time_format %Y-%m-%d %H:%M:%S
</source>

他のマシンにログを転送する場合は、fowardを設定する。

<match nova-api.**>
  type forward
  host logserver
</match>

色々なホストから送信されたログがごちゃごちゃになると嫌なので、host属性にホスト名を入れてどのホストのログか識別できるようにする。

<filter openstack>
  type record_transformer
  <record>
    host ${hostname}
  </record>
</filter>

転送されたログサーバで、matchルールを記述すれば、ログに出力できる。iptablesを利用している場合は、ログを受け取る側の設定(必要に応じて送信側も。CentOS7のデフォルト状態だと受信側のみでok)を行う。CentOS7の場合、下記の24224のポートを許可するルールを/etc/sysconfig/iptablesに記載する。ssh(22)の許可設定の次くらいに設定するとよい。

-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p udp -m state --state NEW -m udp --dport 24224 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
# systemctl restart iptables

MongoDBでログを管理

MongoDBでログを管理すると、クエリーなどで検索できるので使いやすくなる。

MongoDBをインストールする。

# yum install epel-release
# yum install mongodb mongodb-server
# systemctl start mongod.service

ログ用のデータベース作成

# mongo openstack-log
MongoDB shell version: 2.6.11
connecting to: openstack-log
> use openstack-log
switched to db openstack-log

Fluentdの設定

<match nova-api.log>
  type mongo
  database openstack-log
  collection log
  host localhost
  port 27017
  flush_interval 10s
</match>

ログの確認

# mongo openstack-log
MongoDB shell version: 2.6.11
connecting to: openstack-log
> show collections;
log
system.indexes
> db.log.find();
{ "_id" : ObjectId("56693d4308f6ff315e008110"), "pid" : "3927", "level" : "INFO", "class" : "nova.api.ec2", "req-id" : "-", "message" : "0.983s 172.20.132.214 GET /latest/meta-data/public-keys/ None:None 200 [Python-httplib2/0.7.7 (gzip)] text/plain text/plain", "host" : "control", "tag" : "os.control", "time" : ISODate("2015-12-10T03:33:45Z") }
{ "_id" : ObjectId("56693d4308f6ff315e00810e"), "pid" : "3927", "level" : "INFO", "class" : "nova.api.ec2", "req-id" : "-", "message" : "0.3768s 172.20.132.214 GET /latest/meta-data/placement/availability-zone None:None 200 [Python-httplib2/0.7.7 (gzip)] text/plain text/plain", "host" : "control", "tag" : "os.control", "time" : ISODate("2015-12-10T03:33:44Z") }

....

Fluentdでログを検索しよう

levelがERRORのログのみを出力

> db.log.find({level: "ERROR"}) ;

ERRORとWARNのログを出力。$inを使う。

> db.log.find({level: {$in: ["ERROR","WARN"]}});

時間で絞り込み検索$gt(以上:grater than),$lt(以下:lesser than)を使う。

> db.log.find({time: {$gt: ISODate("2015-12-10T03:20:00Z"),$lt: ISODate("2015-12-10T03:30:00Z")}})

複数の条件のコンビネーション(and条件)は、カンマで区切ってならべる。

> db.log.find({time: {$gt: ISODate("2015-12-10T03:20:00Z"),$lt: ISODate("2015-12-10T03:30:00Z")}, level: "INFO"})

ログから文字列にマッチするメッセージを検索する。例えば、インスタンスIDなどを指定する例。

> db.log.find({"message":/940f3b2f-bd74-45ad-bee7-eb0a7318aa84/});

mongoコマンドの出力は、古いログから○件と決まっている。最新のログ100件を例えば出力するには、次のようにする。

> db.log.find().sort({$natural:-1}).limit(100)

ログ検索が遅いときは

インデックスを張る。例えば、時間(time)に対してインデックスを張るには、

> db.log.createIndex({time:1})

とする。

mongodサービスが起動しない

/var/log/mongodb/mongodb.logに下記のようなメッセージを吐いて停止したときは、

2015-12-10T18:05:07.169+0900 [initandlisten] exception in initAndListen: 12596 old lock file, terminating
2015-12-10T18:05:07.169+0900 [initandlisten] dbexit:
2015-12-10T18:05:07.169+0900 [initandlisten] shutdown: going to close listening sockets...
2015-12-10T18:05:07.169+0900 [initandlisten] shutdown: going to flush diaglog...
2015-12-10T18:05:07.169+0900 [initandlisten] shutdown: going to close sockets...
2015-12-10T18:05:07.169+0900 [initandlisten] shutdown: waiting for fs preallocator...
2015-12-10T18:05:07.169+0900 [initandlisten] shutdown: lock for final commit...
2015-12-10T18:05:07.169+0900 [initandlisten] shutdown: final commit...
2015-12-10T18:05:07.169+0900 [initandlisten] shutdown: closing all files...
2015-12-10T18:05:07.169+0900 [initandlisten] closeAllFiles() finished
2015-12-10T18:05:07.169+0900 [initandlisten] dbexit: really exiting now

次のコマンドでリペアする。

# sudo -u mongodb mongod --repair --dbpath /var/lib/mongodb/