LoginSignup
13
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

}

リンク

13
4
0

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
  3. You can use dark theme
What you can do with signing up
13
4