Edited at

Docker初心者が、Nginxのログを fluentd + elasticsearch + kibana で可視化してみた

Dockerは仮想技術の一つ程度しか知らいない状態から、JJUG CCC 2018 Spring で「JavaエンジニアのためのDocker入門 ~ 仮想開発・テスト環境構築 ~ 」を聞いて少し勉強してみたのでDockerで環境を作ってみました。「今更Docker入門 - コンテナ化することで何が嬉しいか」の延長線で、Nginxのログを fluentd + elasticsearch + kibana で可視化してみました。ちなみに私は、fluentdelasticsearchkibana も聞いたことがあるだけで全く使ったことも無く予備知識もほぼないまま、環境を構築しているので、誤りがあるかもです。


環境


  • macOS

  • Docker for Mac Version 2.0.0.0-mac81 (Docker)


作るもの

nginxのログを収集して、kibanaでいつどれくらいのアクセスがあったのかを可視化します。今回は、細かい設定はなしで、可視化します。やり方として、nginxのログをfluentdで収集してelasticsearchに蓄積させます。蓄積したデータをもとにkibanaで可視化していきます。


作ったもの

gitを見てください。

cloneした後に、docker-compose buildして、docker-compose up -dで起動できます。

コマンドを叩くとこんな感じになると思います。初回のbuildは、時間が少しかかります。

$ docker-compose build

Building elasticsearch
Step 1/1 : FROM elasticsearch:5.6
---> 671bb2d7da44

Successfully built 671bb2d7da44
Successfully tagged nginx-monitoring_elasticsearch:latest
Building fluentd
Step 1/2 : FROM fluent/fluentd:v1.3.2-1.0
---> 2b942cd70a7f
Step 2/2 : RUN gem install fluent-plugin-elasticsearch
---> Using cache
---> fb7ab6e052b3

Successfully built fb7ab6e052b3
Successfully tagged nginx-monitoring_fluentd:latest
Building nginx
Step 1/1 : FROM nginx:1.15.8
---> 02256cfb0e4b

Successfully built 02256cfb0e4b
Successfully tagged nginx-monitoring_nginx:latest
Building kibana
Step 1/1 : FROM kibana:5.6.14
---> 4e429ed92aeb

Successfully built 4e429ed92aeb
Successfully tagged nginx-monitoring_kibana:latest
$ docker-compose up -d
Creating network "nginx-monitoring_default" with the default driver
Creating nginx-monitoring_elasticsearch_1 ... done
Creating nginx-monitoring_fluentd_1 ... done
Creating nginx-monitoring_kibana_1 ... done
Creating nginx-monitoring_nginx_1 ... done
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
576dd2bce127 nginx-monitoring_nginx "nginx -g 'daemon of…" 9 seconds ago Up 14 seconds 0.0.0.0:8090->80/tcp nginx-monitoring_nginx_1
ab084c4416a3 nginx-monitoring_kibana "/bin/sh -c /usr/loc…" 10 seconds ago Up 15 seconds 0.0.0.0:5601->5601/tcp nginx-monitoring_kibana_1
ef28ec21fe2b nginx-monitoring_fluentd "/bin/entrypoint.sh …" 10 seconds ago Up 15 seconds 5140/tcp, 0.0.0.0:24224->24224/tcp nginx-monitoring_fluentd_1
2550dc320be2 nginx-monitoring_elasticsearch "/bin/bash bin/es-do…" 11 seconds ago Up 16 seconds 0.0.0.0:9200->9200/tcp, 9300/tcp nginx-monitoring_elasticsearch_1

nginx、fluentd、elasticsearch、kibanaが立ち上がったら、http://localhost:8090/にアクセスしてみます。

その後、kibanaにアクセス(localhost:5601)して、設定後(あとで説明)Timelineから下図のようにアクセス数がグラフで見れます。

スクリーンショット 2018-06-04 22.18.45.png

そもそもnginx、fluentd、elasticsearch、kibanaを連携させていくために、それぞれについてある程度の知識が必要なので、簡単に次から説明していきます。


nginx

一言で言ってしまうとApacheと同じWebサーバです。が、中身はApacheとは大きく異なります。Apacheは、マルチプロセスモデルで同時アクセス数が増えると起動プロセス数×ヒープサイズの掛け算でメモリを食べにいくためメモリが枯渇しやすくなり、レスポンスが悪くなるます。世にいう、C10K(クライアント1万台)問題と呼ばれているやつです。これに比べ、nginxはC10K問題耐えうるように、イベンドドリブンモデルで同時アクセスに対する処理に特化しているので、Apacheのようにレスポンスが悪くなることはないです。シングルスレッドでプロセス数が増えないように処理しているからです。これだけだと、nginx一択に思えますが、Apacheにもメリットがあり、nginxにもデメリットはあります。nginxに比べApacheは、Webサーバとしての機能が豊富にあり、動的コンテンツの処理速度が速いようです。

スマホの登場でサイトへのアクセス数が増加しているので、nginxは知っておいて損はないですね。(むしろ知らないほうがやばい・・・?)


fluentd

OSSのデータ収集管理ツールです。fluentdの特徴は、データの収集とそのデータの記録先をカスタマイズできることです。データファイルをどこかに転送したり、収集・蓄積をリアルタイムに処理したい場合にとてもいいツールになります。ここでいうデータは主に、ログです。プラグインを使えば、Tweetデータなども収集できます。

スクリーンショット 2018-06-07 23.59.38.png

イメージとして、上図のような感じです。DBのログやLinuxのログ、Twitterのデータなどをfluentdが収集して、kafkaや、hadoop、elasticsearch、S3などに渡すことができます。

fluentdには、以下のような機能を持ち合わせています。


  • input(fluentdに入れる・収集するデータ)


    • 必要に応じてパースできる

    • タイムスタンプを管理が可能



  • output(fluentdが吐き出すデータ)


    • 必要に応じてフォーマットできる



  • buffer(収集するデータを紛失しない仕組み)


    • データのやり取りでなにかエラーがあればリトライしてくれる

    • バッファはmemory bufferとfile bufferの2種類がある




memory buffer
file buffer

バッファの場所
メモリ
ファイル

処理速度
早い
遅い

使用容量
少ない(メモリ枯渇の可能性有)
大きい(ディスク容量に依存)

運用面
運用しにくい
運用しやすい

bufferは上の表のような特徴がある。基本的には、file bufferがおすすめ。なんらかの理由で、fluentd自体が終了した場合にファイルとしてバッファが残るので確認が行いやすい。処理速度は、memory bufferに比べれば遅いというかだけでI/Oの時間を厳密にきにする状況で無ければメモリ枯渇を危険性を背負いながらmemory bufferを選ぶ理由は無いと思います。一つ注意するとすればデフォルトの設定の場合、memory bufferになっている事です。なので必要に応じてバッファの設定を書き換える必要があります。


elasticsearch

elasticsearchは、elastic社が開発しているオープンソースの全文検索エンジン(分散型RESTful検索/分析エンジン)です。RDBのようにSQLでのデータ操作は行いません。そのかわり、RESTful APIを使って操作します。そのため、様々な言語での操作が可能です。elasticsearchは、主にデータの保存と検索を担当するため、可視化やセキュリティといった部分は、Elastic Stackと呼ばれている関連システム群を使います。Elastic Stackには、elasticsearch、kinaba、Logstash、Beats、X-Pack、ECEといったものがあります。今回は、そこまで難しいことをしないので、あまり説明しません。


Kibana

elasticsearchに入っているデータを様々な形式で可視化することができます。また、elasticsearchに入っているデータの検索もできます。


ログの可視化まで


構成

.

├── docker-compose.yml
├── elasticsearch
│   └── Dockerfile
├── fluentd
│   ├── Dockerfile
│   └── config
│   └── fluent.conf
├── kibana
│   └── Dockerfile
└── nginx
├── Dockerfile
├── config
│   └── nginx.conf
└── html
   ├── index.html
   └── test.html


docker-compose.yml

version: "3.3"

services:
nginx:
build: ./nginx
volumes:
- ./nginx/config/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/log:/var/log/nginx
- ./nginx/html:/usr/share/nginx/html
ports:
- 8090:80
links:
- fluentd

fluentd:
build: ./fluentd
volumes:
- ./fluentd/config:/fluentd/etc
- ./nginx/log:/var/log/nginx
links:
- elasticsearch
ports:
- "24224:24224"
depends_on:
- elasticsearch

elasticsearch:
build: elasticsearch
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ports:
- "9200:9200"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- esdata:/usr/share/elasticsearch/data

kibana:
build: kibana
ports:
- "5601:5601"
links:
- elasticsearch:elasticsearch

volumes:
esdata:
driver: local



./elasticsearch/Dockerfile

FROM elasticsearch:5.6.14

# RUN elasticsearch-plugin remove x-pack


elasticsearch:5.6.4の頃はelasticsearchのイメージをそのまま利用するとx-packが入っているため、削除が必要でした。x-packは、有料ツールのため、ライセンスキーが必要になるため、削除または無効化が必要です。5.6.14ではremoveが不要になったようです。


./fluentd/Dockerfile

FROM fluent/fluentd:v1.3.2-1.0

RUN gem install fluent-plugin-elasticsearch


今回は、fluentdでelasticsearchにデータを送るために、fluent-plugin-elasticsearchをインストールします。


./fluentd/config/fluent.conf

<source>

type tail
format ltsv
path /var/log/nginx/access.log
tag nginx
pos_file /var/log/nginx/access.log.pos
</source>

<match nginx>
type elasticsearch
host elasticsearch
buffer_type memory
port 9200
index_name fluentd
type_name nginx
logstash_format true
logstash_prefix nginx.access
</match>


fluentdの設定で<source>の部分が入力で<match>が出力に当たります。


今回指定した設定

souece
設定値
概要

type
tail
入力をtailで読む

format
ltsv
ltsv形式としてファイル読み込み(加工するわけではない)

path
/var/log/nginx/access.log
取り込むデータの場所

tag
nginx
tailしたデータの吐き出す場所の指定

pos_file
/var/log/nginx/access.log.pos
どこまで読み込んだか記録しているposファイルを指定

match
設定値
概要

type
elasticsearch
fluent-plugin-elasticsearchプラグインで追加されたもので、elasticsearchに転送します

host
elasticsearch
転送先のホスト

buffer_type
memory
メモリバッファを指定

port
9200
転送先のポート

index_name
fluentd
elasticsearchでのindex名、logstash_formatがtrueの場合は、利用されない

type_name
nginx
elasticsearchのタイプ名

logstash_format
true
kibanaにlogstashフォーマットで出力するオプション

logstash_prefix
nginx.access
kibanaに転送するデータのプレフィックス

sourcetagに書かれているタグによって出口のmatchを選択する。mathc<match nginx>と設定している。なのでsourcetag nginxで収集したデータは<match nginx>で転送先を仕分けることになります。以下のように複数の出口を設定することも可能です。


fluent.conf

<source>

tag hoge.foo
・・・
</source>

<source>
tag hoge.huga
・・・
</source>

# tag hoge.fooの出口
<match hoge.foo>
・・・
</match>

# tag hoge.fooとtag hoge.hugaの両方の出口
<match hoge.**>
・・・
</match>



./kibana/Dockerfile

FROM kibana:5.6.14

# RUN kibana-plugin remove x-pack


kibanaも5.6.4の頃はx-packの削除が必要でしたが、5.6.14で不要のようです。


./nginx/config/nginx.conf

user  nginx;

worker_processes auto;

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

events {
worker_connections 1024;
}

http {
# 他のファイルから設定読み込み
# mime.types : MIMEタイプと拡張子の関連付けを定義したファイル
include /etc/nginx/mime.types;

# レスポンスのデフォルトMIMEタイプ設定
default_type application/octet-stream;

# アクセスログの書式設定(ltsv)
log_format ltsv 'time:$time_iso8601\t'
'remote_addr:$remote_addr\t'
'request_method:$request_method\t'
'request_length:$request_length\t'
'request_uri:$request_uri\t'
'https:$https\t'
'uri:$uri\t'
'query_string:$query_string\t'
'status:$status\t'
'bytes_sent:$bytes_sent\t'
'body_bytes_sent:$body_bytes_sent\t'
'referer:$http_referer\t'
'useragent:$http_user_agent\t'
'forwardedfor:$http_x_forwarded_for\t'
'request_time:$request_time\t'
'upstream_response_time:$upstream_response_time\t'
'host:$host';

# アクセスログの出力先
access_log /var/log/nginx/access.log ltsv;

# コンテンツのファイルの読み込みとクライアントへのレスポンスの送信にsendfile() APIを使うかを設定
# デフォルトは off
sendfile on;

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



./nginx/Dockerfile

FROM nginx:1.15.8


これで下準備完了。あとはdocker-compose buildしてdocker-compose up -dで起動します。起動したらnginxにアクセスしてログを吐かせます。次にkibanaにアクセスします。kibanaは、127.0.0.1:5601でアクセスできます。

127.0.0.1_5601_app_kibana(Laptop with HiDPI screen).png

アクセスするとこんな感じになると思います。この画面から、elasticsearchのインデックスを指定します。fluentdの設定でlogstash_prefixに指定したnginx.accessをindex patterに記述し、Createします。

127.0.0.1_5601_app_kibana(Laptop with HiDPI screen) (1).png

Timelineに移動するとグラフでアクセス数が見れます

127.0.0.1_5601_app_timelion(Laptop with HiDPI screen).png

次に、Visualizeから簡単な集計を行って見ます。ログからuseragentを見てブラウザ毎のアクセス数を集計してみます。VisualizeからPieを選択。Split Slicesを追加、AggregationにTermを指定、Fieldにuseragent.keywordを指定、Order ByにCustom Metricを指定してAggregationをCountに

127.0.0.1_5601_app_kibana(Laptop with HiDPI screen) (2).png

あとは、各種ブラウザで適当にアクセスしてログを作り、Dashboardにグラフ登録したりすれば、可視化完了です。ログファイルを見るより、明らかに可視化した方が見やすく情報も読み取りやすいですね。