2018年6月25日 エンジニアリング

JavaアプリケーションをMetricbeatとJolokiaで監視する

著者 Jaime Soriano

Java仮想マシン(JVM)はフレームワークとして運用管理から監視まで実行できる特長があります。本記事では、JMX(Java Management Extensions)について、およびJMXがエクスポーズする情報の活用方法を説明するとともに、JolokiaとElastic StackをJMXと併用するメリットをご紹介します。JMXとJolokiaについてすでにご存じの方は前半をスキップして、Metricbeatの特長の辺りからお読みいただくことをおすすめします。

JMXとJConsole

JMXはアーキテクチャーを包括的に定義し、また一連のデザインパターンでJavaアプリケーションを監視、および管理できます。JMXはMBeans(マネージドビーンズ)を使用します。MBeansはDI(依存性の注入)によりインスタンス化されるクラスで、JVMのリソースを表します。たとえばアプリ内であるアスペクトを管理するために、あるいはリソースの使用状況の統計値を集めるためにMBeansがリソースを表すといったケースが典型的です。JMXの中心となるのはMBeanサーバーで、MBeanサーバーは同じJVM内のアプリケーションと外部の仲介役として振る舞います。MBeansとのあらゆるインタラクションは、このサーバーを介して実行します。一般的に、JMX APIに直接アクセスできるのはJavaコードだけですが、このAPIをスタンダードプロトコルに翻訳するアダプターも存在します。その一例がJolokiaで、JMX APIをHTTPに翻訳します。

JMXで作業する際に便利なツールとしてJConsoleがあり、これは通常のJavaランタイムのディストリビューションに含まれています。JConsoleを起動すると、マシン上で実行されているJavaプロセスのリストが表示されます。何も実行していない場合も、JConsole自体は表示されます。

JConsole welcome screen

リストのプロセスに接続すると別タブのウインドウが開き、メモリーやスレッドなど、プロセスの一般的な監視情報を表示します。MBeansブラウザーのタブもあります。

JConsole main window

このブラウザーには、名前空間でグループ化されたMBeansプロセスのリストが表示されます。たとえばjava.lang名前空間以下のようにリストの一部にJVMがあるものもあれば、アプリケーションに固有のプロセスであることもあります。MBeanにより属性、運用、通知のカテゴリーは異なります。監視の場合は、属性に注目します。一部のMBeansで、異なるMBeans同士が同じインターフェースで表示されることがあります。たとえばTomcatとKafkaは異なるアプリケーションで、ユースケースにより異なるガベージコレクションを使用しますが、JMXでは同じタイプのオブジェクトとして扱われ、名前だけが異なって表示されます。

MBean browser in JConsole

JConsoleではこうした情報を把握できて便利ですが、監視インフラとして使えるものではありません。また、異なるサーバーにあるさまざまなプロセスの情報を集計する必要が生じることもあるかもしれません。そこで、MetricbeatとJolokiaの出番です。

JMXメトリックをMetricbeatとJolokiaで収集する

Metricbeatは異なるサーバーから情報を収集してElasticsearchに送信でき、Elasticsearchに投入されたデータはKibanaでさまざまな形で可視化することができます。しかし先ほど説明したように、JMXはJavaアプリでしか処理できないので、Jolokiaを使うことになります。JolokiaはエージェントとしてJVMにデプロイされ、RESTでMBeansをHTTPエンドポイントにエクスポーズします。こうしてすべての情報が同じホストのJavaを使わないアプリで扱えるようになります。Jolokiaは通常のJVMエージェントとしてデプロイすることも、Java EE環境向けにWARとして、あるいはOSGI、またはMuleエージェントとしてデプロイすることもできます。

Jolokiaをエージェントとしてデプロイする場合、Javaアプリケーションの立ち上げ時に-javaagentフラグを使用する必要があります。Javaコマンドは直接使用し、引数として渡すことができますが、一部のアプリケーションは独自の起動スクリプトを持っており、それらのドキュメントで別の方法が指定されている可能性があります。たとえばKafkaの独自の起動スクリプトを**では、KAFKA_OPTS環境変数を使う必要があるとします。

export KAFKA_OPTS=-javaagent:/opt/jolokia-jvm-1.5.0-agent.jar=port=8778,host=localhost
./bin/kafka-server-start.sh ./config/server.properties

JolokiaをWARとしてデプロイするには、Java EEサーバーにインストールする必要があります。たとえばTomcatの場合、WARファイルをwebappsディレクトリにコピーすることでこの操作を行うことができます。各アプリケーションのドキュメントで、Javaエージェントを実行する最適な方法を確認することをおすすめします。また、各エージェントと使用できるオプションについて、Jolokiaのドキュメントも併せて確認することをおすすめします。同じJVM内にJolokiaをデプロイできない場合、Jolokiaをプロキシモードにして別のJVMからクエリして使うこともできます。JolokiaエージェントをアプリケーションのJVMで実行させたら、Metricbeat 5.4以降でJolokiaモジュールの[JMX metricset]を使って、手軽にJMXメトリックを収集することができます。Jolokiaモジュールを使用するには、ホストとJolokiaエージェントのポート、JMXメトリックとMetricbeatのイベントフィールド間のマッピングを設定する必要があります。以下で例を見てみましょう。

事例:JavaアプリケーションをMetricbeatとJolokiaで監視する

前出のKafkaの例のように、Jolokiaはローカルホストのポート8778でリッスンしているとします。監視対象のMBeansと属性はJConsoleで確認することができます。MBeanを選択して名前が表示されたら、それを設定ファイルに直接コピーすることができます。

Threading MBean in JConsole

この例では、java.lang:type=Threading MBeanから複数のスレッドを、java.lang:type=Memoryからヒープメモリーの使用状況を監視します。次のようにして設定することができます。

- module: jolokia
  metricsets: ["jmx"]
  hosts: ["localhost:8778"]
  period: 10s
  namespace: "jvm"
  jmx.mappings:
  - mbean: "java.lang:type=Memory"
    attributes:
    - attr: "HeapMemoryUsage"
        field: "memory.heap"
    - attr: "NonHeapMemoryUsage"
        field: "memory.nonheap"
  - mbean: "java.lang:type=Threading"
    attributes:
    - attr: "ThreadCount"
        field: "thread.count"
    - attr: "DaemonThreadCount"
        field: "thread.daemon"

Metricbeatがこの情報を定期的に収集し、4つの値のイベントを送信します。さてここで、JConsoleに表示されるメモリー使用量属性が、スレッドカウントのようなプレーンな値ではなく、4つのフィールドを含むオブジェクトであることに気づいたでしょうか。Metricbeatがイベント中のデータを再構成し、jolokia.jvm名前空間以下に10個のフィールドが含まれるように変換しています。

Event with the collected jolokia metrics

より高度な設定

さまざまなイベントにフィールドを持たせたいという場合があるかもしれません。Metricbeat 6.3以降では、JMXマッピングでイベント設定を使用し、フィールドのグループ化を定義することができるようになりました。たとえば、同じ値を持つ2つのイベントをグループ化することができます。以下は、メモリーフィールドと、スレッドフィールドという2つの異なるイベントを生成する設定の例です。

  jmx.mappings:
  - mbean: "java.lang:type=Memory"
    attributes:
    - attr: "HeapMemoryUsage"
        field: "memory.heap"
        event: "memory"
    - attr: "NonHeapMemoryUsage"
        field: "memory.nonheap"
        event: "memory"
  - mbean: "java.lang:type=Threading"
    attributes:
    - attr: "ThreadCount"
      field: "thread.count"
      event: "threads"
    - attr: "DaemonThreadCount"
      field: "thread.daemon"
      event: "threads"

Metricbeat 6.3で登場した機能のもう1つが、ワイルドカードです。複数のMBeansに対して1つのマッピングを使用できることから、アプリケーションが同じタイプの複数のインスタンスを含む場合や、指定先が事前にわからない場合に便利です。たとえばTomcatが複数のスレッドプールを持つ場合、既存のマッピングに設定を追加して拡張し、プールごとのスレッドとコネクションの数を取得できるように設定できます。

  - mbean: "Catalina:name=*,type=ThreadPool"
    attributes:
    - attr: "currentThreadCount"
      field: "thread.count"
    - attr: "maxThreads"
      field: "thread.max"
    - attr: "connectionCount"
      field: "connection.count"

このように設定すると、新規イベントが一致するMBeanに送信される際、新しいフィールドがそのMBeanの名前を含むようにできます。

Event with the collected Jolokia metrics when using wildcards

Jolokiaのサポート拡充に向け、Metricbeat 6.4ではコミュニティの貢献によるプロキシモードが加わる予定です。さらに、より動的な環境でJavaアプリケーションを監視するJolokia Discoveryも実装される予定です。

Jolokia DiscoveryはUDPマルチキャストのテクノロジーを使い、Jolokiaエージェントにエンドポイントや組み込んだサービスに関するその他の情報をアナウンスさせることができます。実装には、KubernetesやDockerでおなじみのautodiscoverフレームワークが使われ、テンプレートに基づいて、あらゆる機能に対応する動的な設定を行うことができます。前出の例なら、次のように設定してJolokia Discoveryを活用することができます(スレッドカウントのみの収集に変更)。

metricbeat.autodiscover:
  providers:
  - type: jolokia
    templates:
    - condition:
        contains:
          jolokia.agent.version: "1.5.0"
      config:
      - module: "jolokia"
        metricsets: ["jmx"]
        hosts: ["${data.jolokia.url}"]
        namespace: "jvm"
        jmx.mappings: 
        - mbean: "java.lang:type=Threading"
          attributes:
          - attr: "ThreadCount"
            field: "thread.count"
    - condition:
        contains:
          jolokia.server.product: "tomcat"
      config:
      - module: "jolokia"
        metricsets: ["jmx"]
        hosts: ["${data.jolokia.url}"]
        namespace: "jvm"
        jmx.mappings:
        - mbean: "Catalina:name=*,type=ThreadPool"
          attributes:
          - attr: "currentThreadCount"
            field: "thread.count"

この設定は2つのテンプレートで構成されています。1つは検出されるすべてのJolokiaインスタンスに適用され、もう1つはTomcatインスタンスにのみ適用されます。テンプレートの設定は前出の例とほぼ同じですが、ホストに変数を使用している点に注目しましょう。この機能はJMX metricsetで使用することを想定していますが、他のモジュールと組み合わせてクリエイティブな使用方法を生み出すことも、filebeatと使うこともできます。Jolokia moduleautodiscoverのドキュメントで詳しい内容を読むことも、質問することもできます。.