ScalaアプリケーションのJVMメトリックス取得(GKE&Datadog)

Adways Advent Calendar 2019 6日目の記事です。

http://blog.engineer.adways.net/entry/advent_calendar_2019


 

こんにちは、インフラ担当の天津です。

今回はScalaアプリケーション(コンテナ)のJMXメトリック取得用の設定とDatadogでの収集と可視化についてご紹介したいと思います。

背景

弊社のいくつかのサービスではGKEでScalaコンテナが動作しています。

現時点でJVM関連のトラブルが起きているわけではないのですが、今後のトラブル発生時に備えたJVM関連メトリックの収集方法について検証していました。

TL;DR

Dockerコンテナで稼働しているScalaアプリケーションからJVMメトリックを取得するために必要な設定について記載しています。

前提

  • scala: 2.11.7
  • sbt: 1.33

設定方法(scala)

アプリケーションはPlay Frameworkのカンタンなものなので割愛します。

build.sbtにて下記の通りjavaOptionsの設定を行います。

lazy val root = (project in file("."))
    .enablePlugins(PlayScala, AshScriptPlugin)
    .settings(
      unmanagedResourceDirectories in Compile += baseDirectory.value / "../conf",
      version := sys.env.getOrElse("IMAGE_TAG", "1.0"),
      dockerBaseImage := "openjdk:8u232-jre-slim",
      routesGenerator := InjectedRoutesGenerator
    )

javaOptions in Universal ++= Seq(
  "-Djava.rmi.server.hostname=0.0.0.0",
  "-Dcom.sun.management.jmxremote.rmi.port=18080",
  "-Dcom.sun.management.jmxremote.ssl=false",
  "-Dcom.sun.management.jmxremote.local.only=false",
  "-Dcom.sun.management.jmxremote.authenticate=false",
  "-Dcom.sun.management.jmxremote",
  "-Dcom.sun.management.jmxremote.port=18080",
)

上記の設定により、JMXが使用するポートがオープンします。
これでScalaアプリ側の準備は完了です。

コンテナのビルド

早速buildしてみましょう。

$ sbt docker:publishLocal
・・・
[info] Built image development/scala-alpine with tags [1.0]
[success] Total time: 47 s, completed 2019/12/05 19:49:33
[INFO] [12/05/2019 19:49:33.420] [Thread-2] [CoordinatedShutdown(akka://sbt-web)] Starting coordinated shutdown from JVM shutdown hook

buildはうまく行ったようです。

コンテナの実行

ではdocker runしてみましょう。

$ docker run -p 18080:18080 -p 9000:9000 --rm development/scala-alpine:1.0
$ docker ps
CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS              PORTS                                              NAMES
92c9d26da73a        development/scala-alpine:1.0   "/opt/docker/bin/root"   11 seconds ago      Up 9 seconds        0.0.0.0:9000->9000/tcp, 0.0.0.0:18080->18080/tcp   unruffled_solomon

起動しましたね。

JMXへの接続確認

ではJMXのコマンドラインクライアントでアクセスして確認してみましょう。

コマンドラインクライアントは jmxtermを使用します。
下記からDLしてください。今回はjmxterm-1.0.1-uber.jarを使用します。
https://github.com/jiaqi/jmxterm/releases

ではアクセス確認してみましょう

$ java -jar jmxterm-1.0.1-uber.jar
$>open localhost:18080
#Connection to localhost:18080 is opened

コネクションオープンできたのでScavengeの値をとってみます。

$>get -b name=PS\\ Scavenge,type=GarbageCollector -d java.lang *
#mbean = java.lang:name=PS Scavenge,type=GarbageCollector:
LastGcInfo = {
  GcThreadCount = 4;
  duration = 11;
  endTime = 2939;
  id = 8;
<・・・中略 ・・・>
}
CollectionCount = 8;
CollectionTime = 75;
Valid = true;
MemoryPoolNames = [ PS Eden Space, PS Survivor Space ];
Name = PS Scavenge;
ObjectName = java.lang:type=GarbageCollector,name=PS Scavenge;
$>

無事取得できました。設定はうまく行っているようです。

GKEとdatadog-agentの設定方法

コンテナの準備ができたところで実際にDatadogへメトリックを送信してみます。

メトリック送信にはJMXインテグレーションを使用します。

agentの起動

まず、GKEでdatadog-agentを動作させます。公式ドキュメントを参考にdaemonsetを起動します。(daemonsetはリソースの節約になりますね。)

ここで注意なのですが、JMX用のagentコンテナは容量が多いためか別に存在します。

間違えないようJMX付きのイメージをお使いください(datadog/agent:latest-jmx

コンテナのデプロイ

さて、datadog-agentを動作させたら、先ほど作成したコンテナを含むdeployment.ymlを反映させます。
(事前にGCRへのpushをしておいてください)

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: scala-sample
spec:
  replicas: 1
  template:
    metadata:
      annotations:
        # Annotations should have this format: `ad.datadoghq.com/<container_name>.check_names`
        ad.datadoghq.com/scala-sample.check_names: '["jmx"]'
        ad.datadoghq.com/scala-sample.init_configs: '[{}]'
        ad.datadoghq.com/scala-sample.instances: '[{"jmx_url": "service:jmx:rmi:///jndi/rmi://%%host%%:18080/jmxrmi", "name":"scala-sample", "tags":["env:test","app:scala"]}]'
      labels:
        name: scala-sample
    spec:
      containers:
        - image: gcr.io/my-project/amatsu_satoshi/scala-hello-webapp/development/scala-slim:1.0
          imagePullPolicy: Always
          name: scala-sample
          ports:
            - containerPort: 9000
            - containerPort: 18080

キモはannotationの部分です。
ここにJMXインテグレーションの設定を記載することでdaemonsetで動作しているdatadog-agentが勝手に収集を開始してくれます。
Podが何個になろうとも、自動で収集してくれます。便利ですね。

$ kubectl apply -f deployment.yml

問題なく起動したらメトリックが収集されるまで10分程度待ちます。
Datadog側ではJMXインテグレーションをインストールしておきましょう。
そうするとJMXのビルトインダッシュボードが使用できるようになります。

f:id:AdwaysEngineerBlog:20191206134348p:plain

出てきましたね。無事にメトリックが収集されました。

まとめ

JVMのメトリック可視化は意外とカンタンでした。
ただ、JMX用のdocker imageが別であるなどハマリポイントもあるので皆さんははまらないようにしてくださいね。
次回以降でStackdriver Monitoringなど、別の監視サービスでの収集も書けたらと思います。

それではメリークリスマス。


 

次は井古田さんの記事です。

http://blog.engineer.adways.net/entry/advent_calendar_2019/07