LoginSignup
4

More than 5 years have passed since last update.

kamon on play2.6 で収集したデータをkibana(6.0)でみる

Last updated at Posted at 2017-12-22

Kamonとは

各種指標を計測できるツールと捉えています。

中でも、

  • JVM関連の指標(スレッド、Heap、NonHeap)
  • システム(OSレベル)の指標
  • Playframework(spray、素のakka-httpにも対応しているようですが、よくわかってないです)の処理時間計測
  • akka周りの指標
  • 採取したデータをバックエンド(fluentd、datadogなど)に送信

を計測する仕組みがモジュールの有効化だけで採取可能です。

今年の夏頃、abやjmeterを使って どこまで耐えられるものなのかを主題にした 負荷テストを行っていたとき、playframeworkの処理が滞ったのですが、どこにあるのだろうか? 
akka-stream / akka-http 周りで何か起きてるぽいんだけど、そのボトルネックを検知できるツールはないものか探していたおり、
サイバーエージェントさんのkamon.ioに関する記事を偶然見つけたのが、kamonに辿り着いた過程になります。

akka関連の指標の見方については、まだ道半ばなのですが(汗)、設定やfluentdを介したelasticsearchの連携の部分はある程度自分の中では見えてきたかな?と思えるので載せて見ましたという流れです。

play2.6 on Scala 2.12 の環境で動かす

kamonは内部でaspectjを使っているので操作を行っているようです。
以下の設定を適切にしないと、指標が十分に採取できないです。

残念ながら、公式のドキュメントやgithub上のドキュメントもやや古く、play2.6ではドキュメントどうりの方法では動かず、試行錯誤しました。
本来はsbt-aspectj-runnerのようなプラグインを使いこなせたほうが スマート なのですが、うまく機能させるに至らなかったので環境変数で逃げてます。

設定

build.sbt

kamon関連のライブラリは 気持ち的には バージョンを合わせたかったのですが、微妙に対応していないバージョン(2017年11月時点)があったので、0.6.7と0.6.8が混在しています。


libraryDependencies ++= Seq(
jdbc
,guice
,filters
,ws

,"com.typesafe.slick" %% "slick-codegen" % "3.2.0"
,"com.typesafe.play" %% "play-slick" % "3.0.0"
,"org.scalatestplus.play" %% "scalatestplus-play" % "3.1.1" % Test
,"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.8.9"
//ここから下がkamon関連のライブラリー
,"io.kamon" %% "kamon-core" % "0.6.7"
,"io.kamon" %% "kamon-play-2.6" % "0.6.8"
,"io.kamon" %% "kamon-akka-2.5" % "0.6.8"
,"io.kamon" %% "kamon-system-metrics" % "0.6.7"
,"io.kamon" %% "kamon-akka-http" % "0.6.8"
// kamonで計測したmetricsをfluentdに出力するためのライブラリ(今回のメインディッシュ)
,"io.kamon" %% "kamon-fluentd" % "0.6.7"
// kamonで計測したmetricsを標準出力に出すためのライブラリ(デバッグ用途)
,"io.kamon" %% "kamon-log-reporter" % "0.6.8"
// kamonで計測したmetricsをjmxで見えるかするためのライブラリ(今回は参考までに載せておきますレベル)
,"io.kamon" %% "kamon-jmx" % "0.6.7"
)

application.conf

  • 収集するmetricsの設定
kamon{
  metric {
    # 10秒ごとに計測
    tick-interval = 10 second
  }

  metric.filters {
    akka-actor {
      includes = [ "**" ]
    }

    akka-dispatcher {
      includes = [ "**" ]
    }

    akka-router {
      includes = [ "**" ]
    }
    trace.includes = [ "**" ] 
  }
  # system-metricsも採取
  system-metrics.sigar-enabled = false
  system-metrics.jmx-enabled = true

}

  • play向けのモジュール有効化
play.modules.enabled += "kamon.play.di.GuiceModule"
  • fluentdに転送する設定
kamon{
  fluentd {
    # fluentdへの接続情報
    hostname=localhost
    fluentd.port=24224
    # fluentdへの送信頻度
    # kamon.metric.tick-intervalと同じもしくは倍数でないとエラーになります
    flush-interval = 30 second

    subscriptions {

     akka-actor      = [ "**" ]
     akka-dispatcher = [ "**" ]
     akka-router     = [ "**" ]
     http-server     = [ "**" ]
     trace                = [ "**" ]
     trace-segment        = [ "**" ]
     akka-http-server     = [ "**" ]    

    }
    # 公式を見てると、 **percentiles** の部分をより細かく収集する設定になっていますが、
    # fluentdからelasticsearchへデータを登録する部分が詰まってしまったため、中央値のみ収集するようにしています。
    # サーバーのスペック強化などで回避できるのかもしれません
    histogram-stats {
      subscription = [ "min", "max", "average", "percentiles" ],
      percentiles = [50.0]
    }

  }
}

このほかにplay.http.secret.keyはプロダクションモードで動かすためにはマストですので、適切な値を設定してください

起動

開発モードでの起動

環境変数 SBT_OPTS でaspectjweaverのjarへのパスを通して run すれば動きます
aspectjweaver-1.8.10.jarをsbtプロジェクトのフォルダ lib (=アンマネージドライブラリの置き場所)に配置

export SBT_OPTS=-javaagent:./lib/aspectjweaver-1.8.10.jar
sbt run

プロダクションモードでの起動

環境変数 JAVA_OPTS でaspectjweaverのjarへのパスを通して run すれば動きます

sbt stage
export JAVA_OPTS=-javaagent:./target/universal/stage/lib/aspectjweaver-1.8.10.jar
sbt start

どうなったらうまくいっているのか?

以下のようなメッセージが でなくなったら 成功です

  ___                           _      ___   _    _                                 ___  ___ _            _
 / _ \                         | |    |_  | | |  | |                                |  \/  |(_)          (_)
/ /_\ \ ___  _ __    ___   ___ | |_     | | | |  | |  ___   __ _ __   __ ___  _ __  | .  . | _  ___  ___  _  _ __    __ _
|  _  |/ __|| '_ \  / _ \ / __|| __|    | | | |/\| | / _ \ / _` |\ \ / // _ \| '__| | |\/| || |/ __|/ __|| || '_ \  / _` |
| | | |\__ \| |_) ||  __/| (__ | |_ /\__/ / \  /\  /|  __/| (_| | \ V /|  __/| |    | |  | || |\__ \\__ \| || | | || (_| |
\_| |_/|___/| .__/  \___| \___| \__|\____/   \/  \/  \___| \__,_|  \_/  \___||_|    \_|  |_/|_||___/|___/|_||_| |_| \__, |
            | |                                                                                                      __/ |
            |_|                                                                                                     |___/

elasticsearchに流してKibanaでみる

elasticsearch / kibanaも最近メジャーアップデートがありましたので、試しもかねて6.0系で試してます。
kamon関連、system情報(JVMの情報など)、その他で展開する先(index)を分離しています

<source>
  @type forward
  port 24224
  @label @fw  
</source>

<label @fw>
  # kamon関連のmetrics
  <match kamon.fluentd.my-app.akka-actor.kamon/** >
    @type copy

    <store>
      @type elasticsearch
      host es
      port 9200

      # インデックス名はlogstash形式(下だとapplog-{yyyymmdd})
      # 日ごとにインデックスが作成されます
      logstash_format true
      logstash_prefix kamon
      logstash_dateformat %Y%m%d
      # 保存先のタイプ名を指定
      type_name kamon_log
      # タグの内容は「@log_name」という名前のフィールドに格納されてElasticSearchへ展開される
      include_tag_key true
      tag_key @log_name
      # 10秒ごとにElasticSearchへ転送
      flush_interval 10s
      #bufferの形式、パス
      buffer_type file
      buffer_path /fluentd/buffer/kamon-es.*.buffer
    </store>

  </match>

  <match kamon.fluentd.my-app.system-metric.** >
    @type copy

    <store>
      @type elasticsearch
      host es
      port 9200

      # インデックス名はlogstash形式(下だとapplog-{yyyymmdd})
      logstash_format true
      logstash_prefix system-metric
      logstash_dateformat %Y%m%d
      # 保存先のタイプ名を指定
      type_name system-metric
      # タグの内容は「@log_name」という名前のフィールドに格納されてElasticSearchへ展開される
      include_tag_key true
      tag_key @log_name
      # 10秒ごとにElasticSearchへ転送
      flush_interval 10s
      #bufferの形式、パス
      buffer_type file
      buffer_path /fluentd/buffer/system-metric-es.*.buffer
    </store>

  </match>

  <match >
    @type copy

    <store>
    @type elasticsearch
    host es
    port 9200

    # インデックス名はlogstash形式(下だとapplog-{yyyymmdd})
    logstash_format true
    logstash_prefix applog
    logstash_dateformat %Y%m%d
    # 保存先のタイプ名を指定
    type_name app_log
    # タグの内容は「@log_name」という名前のフィールドに格納されてElasticSearchへ展開される
    include_tag_key true
    tag_key @log_name
    # 10秒ごとにElasticSearchへ転送
    flush_interval 10s
    #bufferの形式、パス
    buffer_type file
    buffer_path /fluentd/buffer/app-es.*.buffer
    </store>
  </match>

</label>

攻撃してみました

playframeworkのCPU使用率をかけつつ、abコマンドで負荷をかけたときに、akka / akka-streamでとれるメトリックスをkibanaでグラフ化したりしながら・・・という話を書きたかったのですが、
絶賛勉強中+文章が長くなりそうなので今回は飛ばさせていただきます。<(_ _)>

ハマったところ

elasticsearchでフィールド数の上限を超える

kamonはfluentdにデータをロードするにあたり多くのフィールドを出力します。
この関係上、デフォルトのフィールド数の上限に速攻で達してしまいます。
以下はエラーログの一例です。

{"index"=>{
  "_index"=>"applog-20171123", "_type"=>"app_log", "_id"=>"NnDe6F8B5y9U0V8wC4qG", "status"=>400
  , "error"=>{"type"=>"illegal_argument_exception"
  , "reason"=>"Limit of total fields [1000] in index [applog-20171223] has been exceeded"}}}]}

回避には以下のようなelasticsearchの設定変更をすることで回避できます。

PUT /_settings
{
  "index.mapping.total_fields.limit": 10000
}

5~10分程度でkamonからのデータがElasticSearchにデータが来なくなる??

正直なところ、この問題の原因究明にかなりの時間を要しました。
docker for Macやdocker on Linuxな環境では起きていないことや、
最近自分のPCが壊れたため、リプレイスしたのですが、それ以降問題は起きてません。

私のPCの老朽化が原因だった・・・というオチで収束したので、皆さんには関係のない悲しいオチで収束しました

Tips

ElasticSearchで使えるクエリー

どういった項目があるのか。SQL的にはGroup Byのようなものです。
以下はすべてkibanaのdevtoolで実行してます。
また、インデックス「applog-* 」(インデックス名はlogstash形式にしてますので、*の部分には年月日が入ります)にデータを格納してます。

  • metrics一覧を取得
GET /applog-*/_search
{
  "from": 0,
  "size": 0,
  "sort": [],
  "aggregations": {
    "agg01": {
      "terms": {
        "field": "canonical_metric.name.keyword"
      ,"size": 10000
      }
    }
  }
}
  • カテゴリ一覧を取得
GET /applog-*/_search
{
  "size": 0,
  "aggregations": {
    "agg01": {
      "terms": {
        "field": "category.name.keyword"
      }
    }
  }
}
  • カテゴリ、メトリックス一覧を取得
GET /applog-*/_search
{
  "from": 0,
  "size": 0,
  "sort": [],
  "aggregations": {
    "agg01": {
      "terms": {
        "field": "category.name.keyword"
      ,"size": 10000
      },
      "aggs":
      {
      "agg02": {
        "terms": {
          "field": "canonical_metric.name.keyword"
        ,"size": 10000
        }
      }
      }
    }
  }
}

採取するmetricsを限定する

以下にあるように、entitynameをベースにフィルタリングができます。

* match any number of characters up to the next ‘/’ character found in the entity name.
? match exactly one character, other than ‘/’.
** match any number of characters, regardless of any ‘/’ character found after this wildcard.
exact entity name match if not wildcards are provided.

例1/kamonそのものに関するmetricsをオフにする

kamon{
  metric {
    tick-interval = 10 second
  }

  metric.filters {
    akka-actor {
      includes = [ "**" ]
      excludes = [ "kamon/user/**"  ]
    }
    akka-dispatcher {
      includes = [ "**" ]
    }

    akka-router {
      includes = [ "**" ]
    }
    trace.includes = [ "**" ] 

    system-metric {
      includes = [ "**" ]
    }

  }

}

例2/採取する情報をJVMのメモリに関するものに限定

kamon{
  metric {
    tick-interval = 10 second
  }

  metric.filters {
    system-metric {
      includes = [ "jmx-memory" ]
    }
  }
  system-metrics.sigar-enabled = false
  system-metrics.jmx-enabled = true

}

リンク

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4