nginx
fluentd
Elasticsearch
docker
kibana

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

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

環境

  • macOS
  • Docker for Mac Version 18.03.1-ce-mac65 (Docker)

作るもの

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

作ったもの

gitを見てください。
cloneした後に、docker-compose buildして、docker-compose up -dで起動できます。
コマンドを叩くとこんな感じになると思います。初回のbuildは、時間が少しかかります。

$ docker-compose build
Building elasticsearch
Step 1/2 : FROM docker.elastic.co/elasticsearch/elasticsearch:5.6.4
 ---> a9a9a2620d66
Step 2/2 : RUN elasticsearch-plugin remove x-pack
 ---> Using cache
 ---> 47aecdc03d2e

Successfully built 47aecdc03d2e
Successfully tagged nginx-monitoring_elasticsearch:latest
Building fluentd
Step 1/2 : FROM fluent/fluentd:v1.1.3
 ---> b31456486ec2
Step 2/2 : RUN gem install fluent-plugin-elasticsearch
 ---> Using cache
 ---> 72246463cb70

Successfully built 72246463cb70
Successfully tagged nginx-monitoring_fluentd:latest
Building nginx
Step 1/1 : FROM nginx:1.14.0
 ---> f759510436c8

Successfully built f759510436c8
Successfully tagged nginx-monitoring_nginx:latest
Building kibana
Step 1/2 : FROM docker.elastic.co/kibana/kibana:5.6.4
 ---> ec9ec2836846
Step 2/2 : RUN kibana-plugin remove x-pack
 ---> Using cache
 ---> 8e973b505d6d

Successfully built 8e973b505d6d
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 docker.elastic.co/elasticsearch/elasticsearch:5.6.4

RUN elasticsearch-plugin remove x-pack

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

./fluentd/Dockerfile
FROM fluent/fluentd:v1.1.3

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 docker.elastic.co/kibana/kibana:5.6.4

RUN kibana-plugin remove x-pack
./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.14.0

これで下準備完了。あとは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にグラフ登録したりすれば、可視化完了です。ログファイルを見るより、明らかに可視化した方が見やすく情報も読み取りやすいですね。