fluentdでElasticsearchにnginxアクセスログを流してみる

fluentdでElasticsearchにNginxログを流してみたメモです。


以前、LogstashとBeatsを利用してElasticsearchにデータ投入を試したので、
fluentdでも試してみようと思います。

https://www.fluentd.org/


Nginxのアクセスログをfluentdを利用してElasticsearchに流してみます。

下記のバージョンで試してみます。

  • Ubuntu 16.04
  • td-agent 0.12.4(fluentd-0.12.40)
  • Elasticsearch 6.1.1
  • Kibana 6.1.2
  • nginx 1.12.2

Elasticsearch

Elasticsearch 6.1.1をインストールして起動します。

$ curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.1.1.tar.gz
$ tar -xvzf elasticsearch-6.1.1.tar.gz
$ cd elasticsearch-6.1.1/
$ ./bin/elasticsearch

Kibana

Kibana 6.1.2をインストールして起動します。
以前書きましたが、ホストOSからゲストOS(Ubuntu)上のKibanaにアクセスするために
config/kibana.ymlのserver.hostにゲストOSのIPを追記して起動します。

$ wget https://artifacts.elastic.co/downloads/kibana/kibana-6.1.2-linux-x86_64.tar.gz
$ tar -xzf kibana-6.1.2-linux-x86_64.tar.gz
$ cd kibana-6.1.2-linux-x86_64/
$ ifconfig
$ vi config/kibana.yml
server.host: 10.0.2.15
$ ./bin/kibana

td-agent

fluentdの配布パッケージであるtd-agentを利用します。
td-agentを利用することでfluentdの安定版が使え、システムのRuby環境を汚さずにfluentdを利用することが出来ます。


ディストリビューションごとにインストーラが異なるので、下記のページから自分の環境用(Ubuntu)のコマンドを探します。
https://docs.fluentd.org/v0.12/articles/install-by-deb

Ubuntu16では下記のコマンドでインストールします。

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

確認

$ td-agent --version
td-agent 0.12.4
status

起動確認

$ /etc/init.d/td-agent status
● td-agent.service - LSB: data collector for Treasure Data
   Loaded: loaded (/etc/init.d/td-agent; bad; vendor preset: enabled)
   Active: active (running) since 日 2018-01-14 20:22:12 JST; 2h 0min ago
     Docs: man:systemd-sysv-generator(8)
  Process: 29470 ExecStart=/etc/init.d/td-agent start (code=exited, status=0/SUCCESS)
 Main PID: 29495 (ruby)
    Tasks: 9
   Memory: 65.0M
      CPU: 3.242s
   CGroup: /system.slice/td-agent.service
           tq29495 /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --daemon /var/run/td-agent/td-agent.pid
           mq29498 /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --daemon /var/run/td-agent/td-agent.pid

 114 20:22:11 ubuntu16 systemd[1]: Starting LSB: data collector for Treasure Data...
 114 20:22:12 ubuntu16 td-agent[29470]: Starting td-agent:  * td-agent
 114 20:22:12 ubuntu16 systemd[1]: Started LSB: data collector for Treasure Data.
stop

停止。

$ sudo /etc/init.d/td-agent stop
[ ok ] Stopping td-agent (via systemctl): td-agent.service.
$ sudo /etc/init.d/td-agent status
● td-agent.service - LSB: data collector for Treasure Data
   Loaded: loaded (/etc/init.d/td-agent; bad; vendor preset: enabled)
   Active: inactive (dead) since 日 2018-01-14 22:23:25 JST; 2s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 29716 ExecStop=/etc/init.d/td-agent stop (code=exited, status=0/SUCCESS)
  Process: 29470 ExecStart=/etc/init.d/td-agent start (code=exited, status=0/SUCCESS)
 Main PID: 29495 (code=exited, status=0/SUCCESS)

 114 20:22:11 ubuntu16 systemd[1]: Starting LSB: data collector for Treasure Data...
 114 20:22:12 ubuntu16 td-agent[29470]: Starting td-agent:  * td-agent
 114 20:22:12 ubuntu16 systemd[1]: Started LSB: data collector for Treasure Data.
 114 22:23:24 ubuntu16 systemd[1]: Stopping LSB: data collector for Treasure Data...
 114 22:23:25 ubuntu16 td-agent[29716]: Stopping td-agent:  * td-agent
 114 22:23:25 ubuntu16 systemd[1]: Stopped LSB: data collector for Treasure Data.
start

起動。

$ sudo /etc/init.d/td-agent start
[ ok ] Starting td-agent (via systemctl): td-agent.service.
$ sudo /etc/init.d/td-agent status
● td-agent.service - LSB: data collector for Treasure Data
   Loaded: loaded (/etc/init.d/td-agent; bad; vendor preset: enabled)
   Active: active (running) since 日 2018-01-14 22:23:51 JST; 2s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 29716 ExecStop=/etc/init.d/td-agent stop (code=exited, status=0/SUCCESS)
  Process: 29794 ExecStart=/etc/init.d/td-agent start (code=exited, status=0/SUCCESS)
 Main PID: 29819 (ruby)
    Tasks: 9
   Memory: 65.1M
      CPU: 327ms
   CGroup: /system.slice/td-agent.service
           tq29819 /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --daemon /var/run/td-agent/td-agent.pid
           mq29822 /opt/td-agent/embedded/bin/ruby /usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --daemon /var/run/td-agent/td-agent.pid

 114 22:23:51 ubuntu16 systemd[1]: Starting LSB: data collector for Treasure Data...
 114 22:23:51 ubuntu16 td-agent[29794]: Starting td-agent:  * td-agent
 114 22:23:51 ubuntu16 systemd[1]: Started LSB: data collector for Treasure Data.
fluentd test

動作確認用のjsoncurlで流してみます。
td-agent.logに出力されればOK.

$ curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test
$ tail /var/log/td-agent/td-agent.log
2018-01-14 22:31:45 +0900 debug.test: {"json":"message"}

fluent-plugin-elasticsearch install

fluentdでは入出力、フィルター、変換などがplugin形式で提供されており、それらを組み合わせて組み込むことが出来ます。
デフォルトで利用できるplugin以外はインストールする必要があります。
今回Elasticsearchに出力するためにfluent-plugin-elasticsearchをインストールします。
(td-agent3系だとデフォルトで入っているようです)

$ sudo td-agent-gem install fluent-plugin-elasticsearch --version="1.13.1"

確認

$ td-agent-gem list | grep elastic
elasticsearch (6.0.0)
elasticsearch-api (6.0.0)
elasticsearch-transport (6.0.0)
fluent-plugin-elasticsearch (1.13.1)

nginx

nginxを下記の手順でインストールします。
https://www.nginx.com/resources/wiki/start/topics/tutorials/install/#official-debian-ubuntu-packages

$ sudo vi /etc/td-agent/td-agent.conf
$ wget -qO - https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
$ sudo vim /etc/apt/sources.list.d/nginx.list
deb http://nginx.org/packages/ubuntu/ xenial nginx
deb-src http://nginx.org/packages/ubuntu/ xenial nginx
$ sudo apt-get update
$ sudo apt-get install nginx
$ sudo nginx -v
nginx version: nginx/1.12.2
$ sudo /etc/init.d/nginx start
[ ok ] Starting nginx (via systemctl): nginx.service.

nginx.confはインストール時のままです。

$ cat /etc/nginx/nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}
td-agent設定

td-agent.confを設定します。

sourceディレクティブでtail inputプラグインの設定を記述します。

$ sudo vi /etc/td-agent/td-agent.conf
# input from nginx access_log
<source>
  @type tail
  path /var/log/nginx/access.log
  format nginx
  tag nginx.access
  pos_file /var/log/nginx/access.log.pos
</source>
  • path: 読み込み対象のファイルを指定。
  • format: nginxを指定。
  • tag: 何でもいいですが、nginx.accessを指定。この名前をmatchディレクティブで使用します。
  • pos_file: tail inputプラグインがどこまで読み込んだかを記録しておくファイルです。ファイルの出力先を指定。


matchディレクティブでElasticsearchに出力する設定を記述します。

# output to elasticsearch
<match nginx.access>
  @type elasticsearch

  logstash_format true
  logstash_prefix nginx

  host localhost
  port 9200

  type_name accesslog
</match>
  • logstash_format: trueに指定して、インデックス名がlogstash-YYYY.MM.DDになります。
  • logstash_prefix: nginxを指定することで、logstash_formatがnginx-YYYY.MM.DDになります。
  • host,port: Elasticsearchのhostとportを指定。
  • type_name: ElasticsearchのTypeを指定します。ここではaccesslogにしました。


普通にtd-agentを実行すると、nginxのアクセスログが読めなかったり、
posファイルが書き込めなかったりするので、簡単のためにtd-agentのユーザをrootに変更してしまいます。

TD_AGENT_USER=rootに変更

$ sudo vi /etc/init.d/td-agent
#!/bin/sh
export PATH=/sbin:/usr/sbin:/bin:/usr/bin

TD_AGENT_NAME=td-agent
TD_AGENT_HOME=/opt/td-agent
TD_AGENT_DEFAULT=/etc/default/td-agent
#TD_AGENT_USER=td-agent
TD_AGENT_USER=root
TD_AGENT_GROUP=td-agent
TD_AGENT_RUBY=/opt/td-agent/embedded/bin/ruby
TD_AGENT_BIN_FILE=/usr/sbin/td-agent
TD_AGENT_LOG_FILE=/var/log/td-agent/td-agent.log
TD_AGENT_PID_FILE=/var/run/td-agent/td-agent.pid
TD_AGENT_OPTIONS="--use-v1-config"

td-agent再起動

$ sudo service td-agent restart
Elasticsearch mapping定義

Elaticsearchのmappingを定義します。

mappingファイルを作成し、フィールドの型を定義します。
defaultのマッピング(text)以外のフィールドだけ定義します。

$ vi nginx_mapping.json
{
  "template": "nginx*",
  "mappings": {
    "accesslog": {
      "properties": {
        "remote": {"type": "ip"},
        "code": {"type": "integer"},
        "size": {"type": "integer"},
        "@timestamp": {"type": "date", "index": "false"}
      }
    }
  }
}

PUTでmappingを登録します。
が、下記のようなエラーが出てしまいました。

$ curl -X PUT 'http://localhost:9200/nginx_access_log/' -d @nginx_mapping.json
{"error":"Content-Type header [application/x-www-form-urlencoded] is not supported","status":406}

どうやらElasticsearch6からContent-Typeヘッダが必要となったようです。
という事でContent-Typeを指定して再実行すると登録出来ました。

$ curl -X PUT -H "Content-Type: application/json" 'http://localhost:9200/nginx_access_log/' -d @nginx_mapping.json
[2018-01-29T02:40:37,822][INFO ][o.e.c.m.MetaDataCreateIndexService] [V3FBp3U] [nginx_access_log] creating index, cause [api], templates [], shards [5]/[1], mappings [nginx]
{"acknowledged":true,"shards_acknowledged":true,"index":"nginx_access_log"}

下記のエンドポイントにアクセスし、mapping定義を確認。

$ curl -X GET 'localhost:9200/nginx_access_log/?pretty'
{
  "nginx_access_log" : {
    "aliases" : { },
    "mappings" : {
      "accesslog" : {
        "properties" : {
          "@timestamp" : {
            "type" : "date",
            "index" : false
          },
          "code" : {
            "type" : "integer"
          },
          "remote" : {
            "type" : "ip"
          },
          "size" : {
            "type" : "integer"
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1517328346452",
        "number_of_shards" : "5",
        "number_of_replicas" : "1",
        "uuid" : "ldnnJY0rQ_OClhGB7tGrqA",
        "version" : {
          "created" : "6010199"
        },
        "provided_name" : "nginx_access_log"
      }
    }
  }
}
確認

curlでnginxにアクセスしてログを流してみます。

$ curl -s http://localhost/index.html > /dev/null

すると、下記のようにwarnになってしまいました。
どうやらfluentdのnginxのログ形式とaccess_logの形式が合ってないようです。

$ sudo tail -f /var/log/td-agent/td-agent.log
2018-01-31 01:34:52 +0900 [warn]: pattern not match: "127.0.0.1 - - [31/Jan/2018:01:34:52 +0900] \"GET /index.html HTTP/1.1\" 200 612 \"-\" \"curl/7.47.0\" \"-\""

そのため、
①nginxのログ形式をfluentdのpattern matchの定義に合わせる。
または
②fluentdのpattern matchの定義をnginxの形式に変更して合わせる。
のどちらかが必要そうです。

①が簡単そうです。
nginxをインストールするとデフォルトのlog_formatがmain形式になっているので、defaultで定義されているcombined形式に変更します。

http://nginx.org/en/docs/http/ngx_http_log_module.html

combined(default)形式 nginx.conf

log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

main形式 nginx.conf

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';

combined(default)形式 ログ

$ cat /var/log/nginx/access.log
127.0.0.1 - - [02/Feb/2018:02:23:16 +0900] "GET /index.html HTTP/1.1" 200 612 "-" "curl/7.47.0"

main形式 ログ

$ cat /var/log/nginx/access.log.1 | grep -v 400
127.0.0.1 - - [31/Jan/2018:01:34:52 +0900] "GET /index.html HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"


access_logのフォーマット形式を消せばcombined形式が利用されます。

$ sudo vi /etc/nginx/nginx.conf
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

#    access_log  /var/log/nginx/access.log  main;
    access_log  /var/log/nginx/access.log;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

nginx再起動

$ sudo /etc/init.d/nginx restart
[ ok ] Restarting nginx (via systemctl): nginx.service.

再度ログを流してみます。

$ curl -s http://localhost/index.html

うまくいきました。

$ sudo tail -f /var/log/td-agent/td-agent.log
2018-01-31 01:48:45 +0900 [info]: Connection opened to Elasticsearch cluster => {:host=>"localhost", :port=>9200, :scheme=>"http"}

②fluentdのpattern matchの定義をnginxの形式に変更して合わせる
でもやってみます。

nginxのaccess_logのフォーマット形式を元のmain形式に戻します。

$ sudo vi /etc/nginx/nginx.conf
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

下記を参考にtd-agent.confのtail inputプラグインのformatを変更します。
https://joppot.info/2017/03/10/3480

$ sudo vi /etc/td-agent/td-agent.conf
# input from nginx access_log
<source>
  @type tail
  path /var/log/nginx/access.log
  format /^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<timestamp>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)" "(?<forwarder>[^\"]*)")?/
  tag nginx.access
  pos_file /var/log/nginx/access.log.pos
</source>

td-agent再起動

$ sudo /etc/init.d/td-agent restart

再度ログを流してみます。

$ curl -s http://localhost/index.html

うまくいきました。

$ sudo tail -f /var/log/td-agent/td-agent.log
2018-01-31 01:48:45 +0900 [info]: Connection opened to Elasticsearch cluster => {:host=>"localhost", :port=>9200, :scheme=>"http"}


posファイルを確認してみます。
fluentdでログが読み込まれるとposファイルも更新されています。

$ cat /var/log/nginx/access.log.pos
/var/log/nginx/access.log       00000000000005f2        000000000002168e
$ curl -s http://localhost/index.html > /dev/null
$ curl -s http://localhost/service/index.html > /dev/null
$ curl -s http://localhost/service/list.html > /dev/null
$ curl -s http://localhost/service/detail.html?id=21 > /dev/null
$ curl -s http://localhost/index.html > /dev/null
$ curl -s http://localhost/service/index.html > /dev/null
$ curl -s http://localhost/service/list.html > /dev/null
$ curl -s http://localhost/service/detail.html?id=21 > /dev/null
$ cat /var/log/nginx/access.log.pos
/var/log/nginx/access.log       000000000000094e        000000000002168e

Kibana確認

Kibanaで確認してみます。

http://localhost:5601でkibanaにアクセスし、
左のManagementからIndex patternにnginx*を入力
f:id:pppurple:20180206031038p:plain

Time Filter field nameはそのまま@timestampを選択。
f:id:pppurple:20180206031025p:plain

作成したindex patternが表示される。
f:id:pppurple:20180206031016p:plain

左のDiscoverから作成したnginx*のindex patternを表示。
f:id:pppurple:20180206031059p:plain

うまく表示出来ています。

おわり。