catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

dockerでElasticsearch+logstash+kibanaを立ち上げる

新年一発目のエントリは少々新しいことに挑戦しようということにしました。かたりぃなです。

今回はElasticsearchとC#でのログ分析基盤の作成をやってみます。

まだ実践的なことは何もできていない状態ですが、できたことを少しずつブログに残していこうと思います。

(完成してからとかやっていると、昨年末みたいに更新頻度が下がってしまいますので。)

今回利用するパッケージや技術要素はこんな感じです。

  • Elasticsearch
  • kibana
  • logstash
  • docker
  • .Net core(C#)

なぜログ分析が重要か

ログというのは、何が起きたかを分析するのに重要です。 特に問題が起きた場合にはログから分析する場合がほとんどで、これは分野を問わず同じだと思っています。(WebならApacheログ、組み込みならカーネルログとか)

ログ分析で得た結果は、エンジニア・経営どちらにも波及していくものだと考えています。

具体的には

  • サービスの構成や仕組みを変える必要がある
  • サービス運用方針を変える

などです。

エンジニア視点と経営視点でそれぞれ何をしたいかを明確にします。

エンジニア視点

不具合調査などで必要です。

私が考えているカードゲームARのサービス構成では、複数のAPIとクライアントアプリが存在してるので、バグなどが見つかった場合それぞれのログを順に見ていく必要があります。 これが結構手間です。たとえば、、、

  • Hololensのログを抽出して、APIを叩いた順序を確認
  • 叩かれたサービス(物体検出 or カテゴリ分類)とパラメタを特定
  • 各サービスのログを収集
  • ログを結合して、全体のシーケンスを見直す

と、いろいろと面倒です。

一か所で集中的にログを解析できればもっと効率上がると思いますし、サービス開始した後でもこういったことは必要になってきますので、今のうちに基盤を整えたいといったところです。

経営視点

個人開発サービスではリスクをできるだけ避けたいです。

リスクを避けるためにはスモールスタートが基本だと思っていて、ユーザーが増えてきたらインスタンスを増やすなどの対策をしていけばよいと考えます。 この「ユーザーが増えてきたら」というのはログ分析などからも得られるので、ログはどんどん蓄積していきたいところです。

なぜElasticSearch?(しかもC#)

ログ収集と分析に使うOSSは個人的には何でもよくて、AWSGCPなどのクラウドサービスでも提供されているログ監視サービスでもいいんじゃないかなと思っています。

Elasticsearchでなければならないということは全くありません。本業でも応用できる可能性があるためですが、これも確定ではないので本当に好みの問題かと思います。

クライアントソフトの実装言語としてC#を選んだ理由も特にありません。しいて言うならHololens+Unityでも使うから、少しずつ慣れたいなといったところです。

Elasticsearchを立ててみる(dockerコマンドで直接)

早速立ててみます。 Elasticsearchはdockerイメージが公開されているので、チュートリアルどおりにやればOKです。

https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-prod-cluster-composefile

手元の環境は次のとおりです

ElasticSearchをDockerで動かして、Windows上の.NetCoreからこのDockerを叩いていきます。

さっそくDockerコンテナを動かしてみましょう。

docker pull docker.elastic.co/elasticsearch/elasticsearch:6.5.4

docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.5.4
# ログがいっぱい流れる
# OpenJDKがAVX要求してるけど、マシンがAVX対応してないからオプション指定しろとか
# OpenJDKのオプションにdeprectedなのが指定されてるからうんたら
# あとは色々な情報

これでElasticSearchは立ち上がったみたいなので、まずはステートを問い合わせてみます。 WSLのbashからcurlでいきます。

curl http://127.0.0.1:9200/_cat/health
1472225929 15:38:49 docker-cluster green 2 2 4 2 0 0 0 0 - 100.0%

チュートリアルのとおりですね。ひとまず立ち上がったようです。 次のステップとしてdocker-composeで起動できるようにしたいので、コンテナは一旦落とします。

Elasticsearchを立てる(docker-compose)

公式のインストールガイドにも書かれていますが、そのままやればOKです。 ただし公式のガイドでは2つのコンテナでクラスタを組むようになっている点に注意です。

ここでは入門として、一台のdockerで最低限の設定だけで立てることにします。つまり、先のコマンドをdocker-compose化するだけです。

version: "3"
services:
  elastic-search:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.5.4
    ports: 
      - "9200:9200"
      - "9300:9300"
    environment:
      - discovery.type=single-node
    volumes:
      - "<windows側の適当なディレクトリ>:/usr/share/elasticsearch/data"

できました。 このファイルをdocker-compose.ymlとして保存し、

docker-compose up -d

で起動します。

動作確認コマンドは先のものと同じCurlからのチェックで、注意点は2点です。

  1. デタッチモードなのでElasticSearchの起動時ログが出てきません。 ログなしだと起動完了したか見えなくて困りますが、elasticsearchが起動するまで数秒かかるので、起動完了を待ちます。 先のコマンドをしばらく叩いていればそのうち応答返ってくるようになります。
  2. Elasticsearchは複数インスタンスクラスタ組むのが基本らしく、ステートがyellowと返ってくることがあります。 勉強段階であれば単一インスタンスのほうが色々とやりやすいので、今回はstate=yellowでも良しとします。

ログ収集用のクライアントを作成するための準備

ログ収集といえばfluentdですが、入れるの面倒なので後回しです。 一旦SCPでAWS上からコピーしてくることにします。 .NetCoreの勉強もしたいので、C#でいきます。

プロジェクトを作る

.NetCoreのプロジェクトを作って、必要なパッケージを入れます。

dotnet new console
dotnet add package SSH.NET --version 2016.1.0 
dotnet add package Elasticsearch.Net --version 6.4.1 
dotnet add package NEST --version 6.4.1

プロジェクトディレクトリをVSCodeで開くとデバッガもインストールされます(選択肢でYesを選択すればよい) デバッガは本家VisualStudioと同じ感覚で操作できます。

scpでログ収集してくる

AWSに接続してログを集めてくることにします。 SSH.NETでだいたい面倒見てくれるので、外からパラメータ与えてAPIを順に叩いていくだけです。

  • SCPで接続するホスト、ポート、ユーザー、秘密鍵を指定して設定を行う
  • SSHClientに設定を渡して接続する
  • SSHClientのDownloadでファイルをダウンロード -> ストリームになる
  • ストリームからローカルファイルに保存する

といった流れです。

using System;
using System.IO;
using System.Linq;
using Renci.SshNet;
using Elasticsearch;
using Nest;

  // memo : ここらでHostAddressをとかを適当に設定する

  static void Main(string[] args)
  {
    // SSH接続のための設定
    var ConnNfo = new ConnectionInfo(HostAddress, HostPort, SshUser,
        new AuthenticationMethod[]{
            new PrivateKeyAuthenticationMethod(SshUser,new PrivateKeyFile[]{
                new PrivateKeyFile(SshPrivateKeyFileName, SshPassword)
            }),
        }
    );

    // SCPでログ収集
    using (var client = new ScpClient(ConnNfo)){
        client.RemotePathTransformation = RemotePathTransformation.ShellQuote;
        client.Connect();
        try
        {
            if (!client.IsConnected)
            {
                Console.WriteLine("[NG] SSH Connection failed!!");
            }
            using (var ms = new MemoryStream())
            {
                client.Download("ログファイル名", ms);
                using (FileStream file = new FileStream("testfile", FileMode.Create, FileAccess.Write)) {
                    ms.WriteTo(file);
                }
            }
        }
        finally
        {
        }
    }
  }

コードを書いたらF5でデバッグして、問題なければ次のコマンドで実行できます。

dotnet run

C#からElasticSearchを叩く

dotnet用のパッケージが入っているので、それを使って叩くことができました。

気を付けるポイントとして、ElasticSearchのクライアントは単一ホストで構築されたクラスタであってもコレクションを返してくる(要素数=1)ため、Linqで片づけます。

Linq初めてなのでよくわかりませんが、このあたりをよしなにしてくれるもののようです。

コード

using System;
using System.IO;
using System.Linq;
using Renci.SshNet;
using Elasticsearch;
using Nest;

  static void Main(string[] args)
  {
    //Elasticsearchの状態確認
    var elastic_settings = new ConnectionSettings(new Uri("http://127.0.0.1:9200"));
    var esclient = new ElasticClient(elastic_settings);
    var health = esclient.CatHealth().Records.SingleOrDefault();
    Console.WriteLine("status : " + health.Status);
  }

これで "status : green"と返ってくればOKです。C#からElasticsearchを叩く用意が整いました。

ログを注入する用意

Elasticsearch単体では大した事はできません。ただのRDBだけでは面白くもなんともないのと同じです。

ログの集計等を試したいところですが、そのためにはログが必要ですので注入していきましょう。

どうやってログをElasticsearchに入れていくのか

方法は色々あるみたいで、軽く調べた感じ次の方法がありました

  • ElasticsearchのAPI
  • fluentd
  • logstash

まあfluentdもlogstashもAPI叩いているのだろうとは思うので、使うとしたらこのどちらかかなと思います。

今回はlogstashを使ってみます。

logstashはElasticsearch開発元のElastic社が作ってるので、公式リファレンスが充実していて、Elasticsearchと組み合わせたときに問題が起きにくいと考えられます。

たとえばElastic公式ではこういうdockerサンプルを出してたりするので、何か困ったらこいつを動かせば済むと思います。

https://github.com/elastic/logstash-docker

logstashのインストールと設定

docker-compose.ymlにlogstashも追加するとこんな感じのファイルになりました。

version: "3"
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.5.4
    ports: 
      - "9200:9200"
      - "9300:9300"
    environment:
      - discovery.type=single-node
    volumes:
      - "./data:/usr/share/elasticsearch/data"
    networks: 
      - elastic-stack

  logstach:
    image: docker.elastic.co/logstash/logstash:6.5.4
    volumes:
      - "./pipeline/:/usr/share/logstash/pipeline/"
    networks:
      - elastic-stack

networks:
  elastic-stack:

logstashそのままでは意味がないので、一定周期でログを送り続けるパイプライン設定ファイルを食べさせます。

設定ファイルはこれをコピーしてきました。

https://github.com/elastic/logstash-docker/blob/master/examples/logstash.conf

このファイルをdocker-compose.ymlディレクトリをカレントとして、./pipeline/logstash.confに配置します。

logstash側が見てくれる設定ファイルの置き場所は

https://www.elastic.co/guide/en/logstash/current/dir-layout.html

とのことなので、/usr/share/logstash/pipeline/logstash.confに先ほど作った./pipeline/logstash.confが来るようにマウントします。

最後にdocker-compose up -dすればOKです。

logstashの動作確認のためにkibanaをインストール

logstashが5秒周期でelasticsearchにログを送っていると思うので動作確認をしたいです。

先の手順であったサンプルコード見たときに、docker-composeに書いてあるのを見つけたので試してみた次第です。

API叩こうにもまだそこまで理解が進んでいないので、GUIから簡単に操作できるkibanaを使ってみます。 kibanaもelastic社がつくってるので、たぶん動くでしょう。

https://github.com/elastic/logstash-docker/blob/master/templates/docker-compose.yml.j2

pythonのjinjaテンプレート展開でごにょごにょしてますが、必要な設定だけ持ってくればOKでした。

というわけでdocker-compose.ymlはこうなりました。

version: "3"
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.5.4
    ports: 
      - "9200:9200"
      - "9300:9300"
    environment:
      - discovery.type=single-node
    volumes:
      - "./data:/usr/share/elasticsearch/data"
    networks: 
      - elastic-stack

  kibana:
    image: docker.elastic.co/kibana/kibana:6.5.4
    ports:
      - "5601:5601"
    networks: 
      - elastic-stack

  logstach:
    image: docker.elastic.co/logstash/logstash:6.5.4
    volumes:
      - "./pipeline/:/usr/share/logstash/pipeline/"
    networks:
      - elastic-stack

networks:
  elastic-stack:

これでdocker-compose upしたDockerホストの5601にブラウザでアクセスすれば、kibanaの画面が開きます。

ログを確認

GUI操作はSS撮るの面倒なので文字でメモだけ残します。

最低限の設定

managementからkibanaのindex Patternsを設定します。 これはログを成形して内部的に管理しやすい形にするものです。

今回の動作確認をするだけなら"*"で全部見れるようになるので、これで十分です。

ログを見る

discoverから先ほど設定したフィルタを選択します("*")。これで送られてきたログをすべてみることができます。

https://github.com/elastic/logstash-docker/blob/master/examples/logstash.conf

のinputにある文字列がintervalの周期で送られてきていればOKです。

まとめ

今回はサービスを運用していくためのログ分析基盤であるElasticSearchを試してみました。 まだデータは入れていませんが、今後どんどんログ入れていこうと思います。

ただし、無秩序にログを入れると分析の時に泣きを見るので検討してからですね。

dockerなので環境は作って壊してを簡単にできる利点を生かして、どんどん試していきたいと思います。

それでは今回はこれくらいで。