🌟

ELKスタックに入門してみた

2021/08/11に公開

まえがき

この文章は割と未完です。現在進行形で試行錯誤している状態なのです。

はじめに

多分、余計な話は良いから中身読みたいと思うので、動機とかは後ろにまとめました。

この文章は、セットアップ自体の手順より、ハマったところ、それを通じて分かったことを中心に記述します。

前提

サーバーはすべて VM で、CPU コア数は Ryzen 1700 の 1 コアです。

構築時の Elastic のバージョンは 6.4 です。

構成

  • ログ基盤サーバー (2Core , 4GB RAM, 100GB SSD, Ubuntu 18.04LTS)
  • その他いろいろなサーバー群 (Ubuntu 16.04, Windows Server 2016)

ログ基盤サーバーの構築

elasticsearch, logstash をログ基盤サーバーにインストールした。公式ドキュメント通りなのでインストール自体は省略。ハマった所だけ書いていく

elasticsearch の listen ip

初期設定は 127.0.0.1 みたいになっているので外からの接続を受け付けない。

外向きの IP アドレスに変更する必要があった。ここで、外向きの IP アドレスを指定した場合、kibana も Logstash も、接続先を外向きの IP アドレスを指定する必要がある。(localhost は通らない)

今試したところ、 0.0.0.0 と指定すれば全インターフェイスに listen してくれます。これでいいかも。

kibana の discovery を開くと何もでない

データがないからなので問題ない。あとで設定を変えれば表示されるのでくじける必要はないです。

filebeat-* みたいな表示がされているが、これは Index 名で、RDBMS で言う所のテーブル名。*がついているのは普通にワイルドカードを表している。

filebeat-2018.09.11 のように日付をつけた index が作成(といってもデータ投入時に自動で作られるので手動で作ることはしません)されるとそれ全てに検索をかける。という意味のようです。

日付を index 名に付けることで、古いログを捨てるのがとても簡単にできます。

例えば curl -X DELETE http://elasticsearch:9300/filebeat-2018.09.11 と叩けば消えてしまいます。あっという間だし、自動化も簡単。すごい便利。

kibana の用語がよくわからない

discovery・・・データをとりあえず時系列で表示して抽出等してみる画面。Excel のピボットテーブルみたいな感じ。データさえ入っていればとりあえず表示できる。

visualize・・・データをどう可視化するかを定義する。zabbix のグラフ一個が 1 visualize

dashboard・・・複数の visualize を組み合わせて画面を作ったもの

Timelion・・・すごそう (参考1)[http://acro-engineer.hatenablog.com/entry/2016/02/04/121500"] (参考2)[http://acro-engineer.hatenablog.com/entry/2016/02/04/121500]

APM・・・各アプリケーションにモジュールを入れると性能情報が取得できる・・・ぽい。

Dev Tool・・・便利ツール。Grok Debugger は logstash の設定するときに便利

Monitoring・・・ログ基盤自体のモニタリング。

Management・・・elasticsearch の index を一覧したり消したりできる。あと kibana の設定。Index-pattern の設定を変えれば discovery に色々表示できる。

X-Pack

以前のバージョンでは拡張機能を使う場合には X-Pack を入れる必要があった。この部分が商用だと有償だったみたいな経緯があってあちこちに X-Pack という記述があるが、6.4 では本体と一緒にインストールされるようになった。この部分は、ライセンスが異なったりするかもしれないので利用する際は注意が必要。

可視化回り(サーバーの負荷状況等)

metricbeat のインストール

何はともあれ、何かが可視化されないとモチベーションが沸かないので、metricbeat から試した。

インストールは単純に、apt・・・ではない。いきなりの落とし穴があった。
要約:apt で入れたあと、tar.gz に入っている設定ファイルをコピーする必要があった。

設定ファイルは /etc/metricbeat/metricbeat.yml で、必要なのは kibana の URL と elasticsearch に接続するエンドポイントくらい。ハマりそうなのは以下の場所くらいか

設定ファイルはサーバーが複数あってもまったく同じで OK なので、Ansible で入れるのが楽

`output.elasticsearch:
  # Array of hosts to connect to.
  hosts: ["192.168.10.20:9200"]
  protocol: "http"     # 内部なのでhttpsにしていない

設定変更後、一度だけどこかのサーバーで metricbeat setup を実行すると kibana にダッシュボードが作成される。サーバーの追加は metricbeat を入れるだけで自動的に認識される。

簡単に、かなりのビジュアライズがされるのですごさを簡単に味わえる

地味なポイント

metricbeat, packetbeat 等は、データを構造化(決まった形)で送信するので、elasticsearch に直接繋いで OK。

いろいろなログを elasticsearch に入れる

前説

一番やりたいことだが最難関。

何が難しいかというとログファイルは形式が一定ではない上に、どういう情報が含まれているのかも決まっていない。かといってそのままでは扱いにくいので、内容をある程度解釈してあげる必要がある(=どう解釈するか設定が必要)。

データの流れ

ログの取込には、filebeats と logstash が登場するが、役割は filebeat でログ内容を(ちょっとだけメタ情報を付加して(解釈はせずに) logstash に送信、logstash で内容を解釈して構造化した状態で保存。という流れが基本(細かくは蛇足参照)

ログ形式

ログ出力側でログを構造化できるのであればそれを行った方がよい。

個人的には LTSV 形式おすすめ。

容量的には多少大きくなるけれども、人間が読んでも読みやすく、機械にも優しいフォーマットだと思います。(なお、項目数が必ず一定である必要はありません。)

後述しますが、普通にログを解釈するのは割と大変です。

index 名

ログの種別ごとに index 名を切り替えたくなりますが、慣れるまでは辞めておきましょう。
デフォルトの filebeat-*を使わないと、kibana の visualization が適用できません。
幸い、いろいろな種類のログを混ぜても問題ないようにできています。
※というより、ログ種別の区分用のフィールドが自動で付加されています。

蛇足

最初はまず 1 台分の 1 ログだけをターゲットにして、感じに慣れた方がよい。
2 ログを流し込もうとするとハマるので注意

最初のログ取込: Nginx アクセスログ

満足度が高そうなので Nginx のアクセスログを取り込むことにした。

nginx ログ形式を LTSV に変更

全サービスが通過するリバースプロクシがあるのでそこに以下の設定を入れた。

conf.d 以下にある他の設定ファイルで access_log を指定している場合、そちらも設定変更が必要なことに注意

<code class="language-/etc/nginx/nginx.conf">    log_format ltsv 'time:$time_iso8601\t'
                    'http_host:"$host"\t'
                    'remote_addr:$remote_addr\t'
                    'method:"$request_method"\t'
                    'request:"$request"\t'
                    'request_length:$request_length\t'
                    'status:$status\t'
                    'bytes_sent:$bytes_sent\t'
                    'body_bytes_sent:$body_bytes_sent\t'
                    'referer:"$http_referer"\t'
                    'user_agent:"$http_user_agent"\t'
                    'upstream_addr:"$upstream_addr"\t'
                    'upstream_status:$upstream_status\t'
                    'request_time:$request_time\t'
                    'upstream_response_time:$upstream_response_time\t'
                    'upstream_connect_time:$upstream_connect_time\t'
                    'upstream_header_time:$upstream_header_time';

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

設定してみるとわかるが、人間から見ても可読性は悪くない

filebeat インストール&設定

filebeat のインストールと、 /etc/filebeat/filebeat.yml の設定は省略。

ハマりどころとしては、 elasticsearch と logstash の出力はどちらか片方のみ。

両方設定すると起動時に落ちます。filebeat は出力先を 1 つしか設定できません。

※ファイルごとに 1 つとかではなく、サーバーごとに 1 つだけ。つらい。

/etc/filebeat/module.d/nginx.yml
- module: nginx
  # Access logs
  access:
    enabled: true

    # Set custom paths for the log files. If left empty,
    # Filebeat will choose the paths depending on your OS.
    var.paths:
      - /var/log/nginx/access.log

  # Error logs
  # 複数ログを同時に送らないように enabled : false にしておく
  error:
    enabled: false

    # Set custom paths for the log files. If left empty,
    # Filebeat will choose the paths depending on your OS.
    var.paths:
      - /var/log/nginx/error.log

logstash 設定

logstash は conf.d 以下のファイルを読み込むので複数種類突っ込むときは

それなりの書き方をしないと混ざる。

なお、filebeat の nginx モジュールで送信するときに既に構造化はされていて、いくつかのメタ情報が付加されている。例えば、 [fileset][module] は filebeat がセットしている値。

元のログは message に格納されているので、そこを LTSV として解釈して elasticsearch に投入。という流れ。

/etc/logstash/conf.d/nginx.accesslog.conf
input {
    beats {
        port => 5400
    }
}

filter {

  if [fileset][module] == "nginx" {
    if [fileset][name] == "access" {

      kv {
        field_split => "\t"
        value_split => ":"
      }

      mutate {
        convert => ["request_length", "integer"]
        convert => ["status", "integer"]
        convert => ["bytes_sent", "integer"]
        convert => ["body_bytes_sent", "integer"]
        convert => ["upstream_status", "integer"]
        convert => ["request_time", "float"]
        convert => ["upstream_response_time", "float"]
        convert => ["upstream_connect_time", "float"]
        convert => ["upstream_header_time", "float"]
      }

      geoip {
        source => "remote_addr"
        target => "geoip"
        add_tag => [ "nginx-geoip" ]
      }

      date {
        match => [ "time" , "ISO8601" ]
      }

      useragent {
        source => "user_agent"
      }
    }
  }
}

output {
 elasticsearch {
   hosts => ["192.168.10.20:9200"]
   index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}"
   document_type => "nginx_logs"
 }
}

要注意;logstash の pipeline について

ログが入力されてからどう処理するかの設定は /etc/logstash/pipelines.yml に定義されているが、デフォルトでは、全てのログが conf.d 以下の config ファイル全てを通過する。

なので、設定上で、どのログのときに処理を行うのかを判定する必要がある。

私は思いっきり勘違いをしたのだが、上の設定で言うと、 port 5400 に飛んできたログは、このファイルの定義で解釈する。のではなく、どこから飛んできたログであろうと、conf.d 以下の全てのファイルの設定を通過するので必ず対象でなければ何も filter しないように書かなければいけないようだ。ようするに・・・ログ種別の分だけ if 文が増える。 これはつらい。なので以下の記事参照・・・

https://qiita.com/micci184/items/24e197a168891f089b3d

でも、filebeat は出力先 1 個しか指定できない、どうすればいいの?

蛇足

動機

最近、Mastodon を運用始めたり、色々と Web アプリを動かしていたりサーバーが増えたりでログを見るところまで手が回らなくなってきたので、ログ基盤というのを作ってみたくなった。

※ ゆくゆくは、zabbix から Kibana に移行できたらなぁと思っている。

beats ではなく beat

名前をよく間違えて怒られた。 metricbeat"s"とか filebeat"s"ではなく単数形。

filebeat と logstash

logstash は自分でログファイルを開いて解釈して、elasticsearch に入れるところまで出来る。

ようするに logstash が入ってるのであれば filebeat は不要。では filebeat はなんで存在するのかというと、logstash は Java 製で、多機能で重量級なので全部のサーバーに入れるには重すぎる。

なので、filebeat で最低現のメタ情報だけを付加して、後はどこかの logstash に任せる。という運用がよい。

logstash の運用

(こういうことはできません)

ログの種類 1 種類ごとに pipeline を 1 個つくって、別のポートで待受させて、filebeat でどこに投げ込むかを決めればスッキリするかなと思った(filebeat は出力先を 1 個しか指定できない)

しかし、logstash はスッキリするけど filebeat 側がスッキリしないのであえてこうなってるのかなぁと思った。

謝辞

Twitter で色々と教えて下さった Jun Ohtani さん ( https://twitter.com/johtani )

多分、色々と教わらなかったらとっくに心折れてたと思います。ありがとうございます。

Discussion