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
}
リンク
-
今回のプロジェクトについて
https://github.com/yurayuramen/try-kamon
プロジェクト一式をgithubにプッシュしました。
readme.mdに簡単なセットアップ方法を記載しましたので、煮るなり焼くなり好きに使ってください。 -
kamon.io
そもそもなんて読むんですかね。ググっても出てこないので、だれか教えてください。
http://kamon.io/documentation/get-started/ -
kamon.io のgithub公式
https://github.com/kamon-io/Kamon -
elasticsearch / kibana の dockerについて
https://www.elastic.co/guide/en/elasticsearch/reference/6.0/docker.html
https://www.elastic.co/guide/en/kibana/6.0/docker.html -
kamonの存在を知るきっかけとなったサイバーエージェントさんのサイト
https://adtech.cyberagent.io/scalablog/2015/09/24/kamon_datadog_akka_performance/ -
fluentdのdockerイメージ
https://hub.docker.com/r/fluent/fluentd/ -
fluentdからelasticsearchにロードするgem
https://github.com/uken/fluent-plugin-elasticsearch
最新は2.x系だとdocker buildでエラーが出た問題を解消できなかったので、1.x系で逃げてます。 -
playframework
https://www.playframework.com/documentation/2.6.x/Home -
sbt
http://www.scala-sbt.org/1.x/docs/index.html
playframeworkの2.6.6からsbt1.0をサポートしたとのことなので、今回は1.0系にしてます。