Google Cloudとコンテナの継続的なセキュリティ
本文の内容は、2019年4月24日にFede Barcelonaが投稿したブログ(https://sysdig.com/blog/jmx-monitoring-custom-metrics/)を元に日本語に翻訳・再構成した内容となっております。
このチュートリアルでは、JMXモニタリングを使用してアプリケーションのメトリクスを公開するためにJavaコードをインストルメントする方法を学んでいきたいと思います。コードの例に従うと、Javaアプリケーションの可用性、健全性、パフォーマンスを監視することができます。
Java 1.5では、JMX - Java Management eXtensions - JREの世界でコードを計測するための標準的な方法です。これにより、MBeansを使用して標準的なメトリクスとカスタムメトリクスをモニタリングシステムにエクスポートすることができ、アプリケーションがどのように実行されているかを理解することができます(メモリ使用量、CPU使用量、スレッド数、RESTエンドポイントでのAPIコール...計測したいものは何でも、実際に)。
このブログでは、JMXモニタリングがどのように機能するか、また、JavaアプリケーションにJMXメトリクスを実装する方法について説明します。
大規模なJavaアプリケーションは、さまざまなメトリクスのニーズをもたらします。すべてがあるべき姿で動作していることを確認したいのか、パフォーマンス情報を収集したいのか、接続されているユーザー数などの使用パターンを理解したいのか、どのエンドポイントがREST APIで最も使用されているのか、などです。
例えば、データベースから毎秒何度も読み書きするような大規模なアプリケーションでは、出力メッセージをログに記録する代わりに、Java Management Extensions のようなモニタリング・インターフェースを提供するのが良い方法です。カスタムJavaメトリクスは、メンテナンス、トラブルシューティング、外部監視ツールとの統合が容易になります。JMX にデータをエクスポートするクラスは、マネージド・ビーン(MBean)と呼ばれます。これらの MBean は、Java プラットフォームによって提供される MBean サーバにそのメトリクスを公開(エクスポート)します。
MBeanServerは、構成されたエンドポイント(MBeans)をスクレイプし、Java RMIやHTTP/SOAPのような異なるプロトコルを使用してメトリクスを公開するメトリクス収集エージェントに同化することができます。
これは、JMX モニタリング/MBean アーキテクチャの基本的な表現になります。
MBeanは、情報を返すメソッドを持つオブジェクトで、MBeanServerを介して外部に情報をエクスポートすることができます。
MBeanには4つのタイプがあります。
Standard MBean:
Standard MBean: これは、アプリケーションをインストルメントする最も一般的で簡単な方法です。これは、メトリクスの現在の値を取得し、それらを変更することができます。標準MBeansはまた、実行中にプログラムの外部から呼び出すことができるいくつかの操作を実装しています。
余分な動作を必要としない場合は、これを使用してください。必要なのは、必要な情報をエクスポートするゲッター関数を持つインターフェイスを作成することだけです。
ダイナミック MBean:
この種のMBeanは、javax.management.DynamicMBeanインターフェイスを実装することで自動検出されるメトリクスを取得または変更するためのゲッターとセッターも実装しています。
これらは、メトリクスを公開するためにMBean標準に従っていないクラスがある場合に便利です。MBeanServer がこのクラス内でどのように値を公開しなければならないかを手動で定義することができます。
インターフェイスをコーディングしたくない場合や、どのフィールドをエクスポートする必要があるか事前にわからない場合は、このクラスを使用してください。
モデル MBean:
これは、リソースを計測するために使用されるMBeanの一般的で、動的で、実行時に設定可能なタイプのMBeanです。これにより、Java Dynamic Managementエージェントに接続されているマネージャは、MBeanを動的に作成して設定することができます。
JMXに接続している誰もがプログラムでMBeanをインスタンス化したり設定したりできるようにする必要がある場合に使用してください。
Open MBean:
これらは Dynamic MBean の一種であるため、javax.management.DynamicMBean インターフェースを実装する必要があります。
このタイプでは、事前に定義されたJavaクラスのセットのみを使用して、実行時に発見されるオブジェクトをインストルメントすることができます。
アプリケーションから特定のJavaクラスをロードする必要がないので、JMX仕様を使用するアプリケーションの広い範囲に開放する必要があるリソースを計装する柔軟な手段が必要な場合は、この1つを使用してください。
これがどのように実装されているかを理解するために、標準MBeanを利用した小さなサンプル・アプリケーションを作成します。
まず、MBeanServer が情報を取得するインターフェイスを作成する必要があります。
public interface SystemStatusMBean { Integer getNumberOfSecondsRunning(); String getProgramName(); Long getNumberOfUnixSecondsRunning(); Boolean getSwitchStatus();
}
これは、依存性インジェクションを使用して、このオブジェクトが4つのメトリクスをエクスポートすることをMBeansServerに伝えます。
実際にこの動作を実装したオブジェクトを書いてみましょう:
先ほどのコードを見ると、インターフェイスのすべてのメトリクスを実装したオブジェクトを作成しているだけで、バックグラウンド・スレッドを使って毎秒更新していることがわかります。
最後に、MBeanオブジェクトのインスタンスを作成し、MBeanServerに登録します。
MBean は、MBeanServer 内で識別するための ObjectName が必要です。名前には、衝突やキーを避けるためにドメインを含める必要があります。この例では、ドメインは com.sysdig.app、キーは name=SystemStatusExample となります。
ローカル環境にJavaコンパイラやランタイムがない場合は、Dockerを使うことができます。
$ docker run -d --rm --name jmx -p 5000:5000 sysdiglabs/jmx-metrics:0.1
jconsoleを実行してローカルプロセスに接続し、jmxのモニタリングメトリクスをチェックします。
Jconsole JMXセキュリティ警告(no-SSL endpoint)が表示されますが、この場合は続行しても問題ありません。
今すぐMBeansタブに移動し、左側のパネルを使用してcom.sysdig.app, SystemStatusExample, Attributesに移動します。
Jconsole mbeans私たちが作成したカスタムJMXメトリクスを見ることができ、その値をライブで更新することができるはずです。
jconsoleインターフェイスを使用して見ることができるように、余分なインスツルメンテーションなしですぐに監視できるいくつかのデフォルトのメトリクスがあり、これらのデフォルトのMBeansについて詳しく知りたい場合は、このドキュメントの付録があります。
JREやPrometheusやStatsDのような他のモニタリングシステムと一緒に、モニタリングシステムにJMXメトリクスを表示させたいと思うでしょう。コンテナ内でJavaアプリケーションを実行したり、Kubernetesの上でJavaアプリケーションを実行したりすると、より複雑になる可能性があります。
これらは、JMXメトリクスを使用してJavaコードを計測する際に遭遇する可能性のある障害のいくつかです。
それはもう簡単ではありませんよね?Sysdig Monitorはここであなたを助けてくれます。開発者の視点から見ると、Sysdig Monitorを使用することで、いくつかの直接的な利点があります。
Sysdigエージェントは、JMXカスタムメトリックコレクションを起動して実行するためにいくつかの設定が必要です。
この例では、先ほどの例からいくつかのカスタムJMXメトリックを収集する方法を示しています。dragent.yamlまたはそれを扱うKubernetes ConfigMapで、jmxセクションの下に追加します。
jmx: per_process_beans: systemStatus: pattern: "Main" beans: - query: "com.sysdig.app:name=SystemStatusExample" attributes: - name: NumberOfSecondsRunning alias: java.app.numberOfSecondsRunning - name: NumberOfUnixSecondsRunning alias: java.app.numberOfUnixSecondsRunning - name: SwitchStatus
alias: java.app.switchStatus
ご覧のように、クエリはMBeanServerに登録されているMBeanの名前で、属性はMBeanがエクスポートしているメトリクスだけです。
数秒後には、メトリクスがSysdig Monitorのインターフェイスに表示されるので、ライブで参照することもできます。
またはダッシュボードで整理することができます。
Java Monitoring Extensions を使用すると、Java アプリケーションにプロファイリングとアプリケーションレベルのモニタリングを簡単に追加することができます。また、CPU 負荷、メモリ使用量、クラスロード、スレッドをトラブルシューティングするための一般的な JMX メトリクスのセットが装備されています。
JavaアプリケーションからJMXメトリクスをインポートしたいが、Prometheusやstatsdのような他のメトリクスソースも持っていますか?Sysdig MonitorはJMXメトリクスのスクレイピングをサポートしているので、すべてのメトリクスを統一し、単一のプラットフォームからダッシュボードやアラートを生成することができます。SysdigのJMXクエリ言語を使用することで、環境上でスポーンする新しいJavaプロセスやメトリクスを自動的にスクレイピングすることができます(例えば、DockerやKubernetesでサービスの自動スケーリングを使用している場合など)。
MBeansを通じてエクスポートされるクラスのプロパティは属性と呼ばれ、MBeansを通じてエクスポートされるメソッドは操作と呼ばれます。
MBeans と属性の集合体がありますが、これらはすでに Java プラットフォームによって提供されています。独自のメトリクスを作成する前に、これらのクラスから収集できるパフォーマンスとトラブルシューティング情報を見てみましょう。
LoadedClassCount
MBean: java.lang:type=ClassLoading
これは現在JVMにロードされているクラスの数です。
この数が増え続ける場合、複数のクラスローダが異なる時間に同じクラスをロードすることに問題がある可能性があります。
TotalLoadedClassCount
MBean: java.lang:type=ClassLoading
これは、JVMが起動してからロードされたクラスの数です。
UnloadedClassCount
MBean: java.lang:type=ClassLoading
クラスは、それらをロードしたクラスローダがガーベージコレクトされたときにアンロードされます。これは、JVMが起動してからアンロードされたクラスの数です。
TotalCompilationTime
MBean: java.lang:type=コンパイル
TotalCompilationTimeは、JITのコンパイルに費やした時間をミリ秒単位で累積したものです。これは、シナリオの JIT パフォーマンスを監視するために使用できます。
Garbage Collector - CollectionCount
MBean: java.lang:type=GarbageCollector,name=[あなたのGC名]です。
これは、JVMが起動してから発生したガベージコレクションイベントの数です。
ガベージコレクタ - CollectionTime
MBean: java.lang:type=GarbageCollector,name=[あなたのGC名]です。
CollectionTimeは、ミリ秒単位でのガベージコレクションの実行に費やされた時間の合計で、Javaアプリケーションの動作をプロファイルするためのもう一つの良い候補です。
Sysdig MonitorでJava GCの収集時間(PS Scavengeアルゴリズム)を監視する。
Garbage Collector - LastGcInfo
MBean: java.lang:type=GarbageCollector,name=[あなたのGC名]です。
LastGcInfoは、以下のデータで実行された最後のガベージコレクションイベントに関する情報の構造体です。
GcThreadCount
GCを実行したスレッド数
durarion
GCイベントの合計期間
startTime
JVMが起動してからの起動時間(ミリ秒単位)
endTime
JVMが起動してからの終了時間(ミリ秒単位
memoryUsageBeforeGc
イベント前にコミットされたメモリ、初期メモリ、最大メモリ、使用済みメモリに関する情報の構造
memoryUsageAfterGc
イベント後にコミットされたメモリ、初期メモリ、最大メモリ、使用済みメモリに関する情報の構造
Arch
MBean: java.lang:type=OperatingSystem
プロセッサのアーキテクチャ。
AvailableProcessors
MBean: java.lang:type=OperatingSystem
利用可能な中央処理装置の数。
CommittedVirtualMemorySize
MBean: java.lang:type=OperatingSystem
これは、JVMが使用可能であることが保証されているメモリ量(バイト単位)を表します。
FreePhysicalMemorySize
MBean: java.lang:type=OperatingSystem
これは、ホスト内の空き物理メモリのメモリ量を表します(free = total - (used + shared + cached + buffered) )。
MaxFileDescriptorCount
MBean: java.lang:type=OperatingSystem
これは、オペレーティングシステムによって決定される、同じプロセスで開くことができるファイル記述子の数です。この数以上のファイルディスクリプタを持つことはできません。
OpenFileDescriptorCount
MBean: java.lang:type=OperatingSystem
これは現時点で開いているファイルディスクリプタの数で、これがMaxFileDescriptorCountに達するとアプリケーションはIOExceptionをスローします。開いているファイルが多すぎます。これは、ファイルディスクリプタを開いているのに閉じていないことを意味します。この変数を監視し、MaxFileDescriptorCountの値に近づいた場合に警告を出すことができます。
FreeSwapSpaceSize
MBean: java.lang:type=OperatingSystem
FreeSwapSpaceSizeは、ホストで利用可能なスワップメモリの量(バイト単位)です。
ProcessCpuLoad
MBean: java.lang:type=OperatingSystem
ProcessCpuLoadは、このプロセスのCPU負荷を表します。
ProcessCpuTime
MBean: java.lang:type=OperatingSystem
これは、CPUがこのプロセスを実行するのに費やした時間です。
SystemCpuLoad
MBean: java.lang:type=OperatingSystem
ホストシステムのCPUの現在の負荷を表します。
SystemLoadAverage
MBean: java.lang:type=OperatingSystem
ホストシステムの平均負荷を表します。コマンドで与えられた値と一致します: cat /proc/loadavg | awk '{print $1}'
TotalPhysicalMemorySize
MBean: java.lang:type=OperatingSystem
ホストのメモリサイズをバイト単位で指定します。
TotalSwapSpaceSize
MBean: java.lang:type=OperatingSystem
ホストのスワップメモリサイズをバイト単位で指定します。
Version
MBean: java.lang:type=OperatingSystem
ホストのカーネルバージョン。uname -r で指定された値と一致します。
InputArguments
MBean: java.lang:type=Runtime
InputArgumentsは、JVMが起動された引数の配列です。
SpecVersion
MBean: java.lang:type=Runtime
SpecVersion は JVM のバージョンを表します。
StartTime
MBean: java.lang:type=Runtime
JVMがUnix Epoch形式で起動した時刻。
Uptime
MBean: java.lang:type=Runtime
JVMが実行されているミリ秒数。
DaemonThreadCount
MBean: java.lang:type=Threading
実行しているデーモンのスレッド数。
PeakThreadCount
MBean: java.lang:type=Threading
JVMが起動されてから、またはピークがリセットされてから、同時に実行されているスレッドの最大数。このメトリック(次の2つと同様に)は、Javaアプリケーションの動的な動作を監視し、プロファイリングするのに役立ちます。
ThreadCount
MBean: java.lang:type=Threading
TotalStartedThreadCount
TotalStartedThreadCount
MBean: java.lang:type=Threading
JVMが起動してから開始されたスレッドの数です。