Red Hat で Java Platform Advocate として OpenJDK を担当している伊藤ちひろ(@chiroito)です。
この記事は、Red Hat Developerのブログ記事、Inject custom JDK Flight Recorder events in containerized applications | Red Hat Developer の翻訳記事です。
JDK Mission Control (JMC) エージェントは、Java 仮想マシンを再起動することなく、ランタイムにカスタム JDK Flight Recorder (JFR) イベントを注入できる強力なツールです。JMCエージェントプラグインがコンテナ化されていない環境でエージェントを使用するプロセスを簡素化するのと同様に、Cryostatエージェントプラグインはコンテナ化された環境でも同じことをします。
JMCエージェントのサポートはCryostatに統合され、Cryostatはコンテナ環境でJMCエージェントを使用するための様々なAPIハンドラをサポートするようになりました。本記事では、CryostatエージェントとそのAPIハンドラについて紹介します。
注:この記事で取り上げた技術について理解を深めるには、JMCエージェントによるJVM性能監視 - 赤帽エンジニアブログとContainerJFR入門: コンテナのためのJDK Flight Recorder - 赤帽エンジニアブログをご覧ください。
前提条件とセットアップ
Cryostatエージェントの使用は、Cryostatプロジェクトのアップストリームリポジトリから可能です。始めるには、最新版をpullしてビルドしてください:
$ git clone https://github.com/cryostatio/cryostat $ cd cryostat $ mvn clean verify
ビルドが完了したら、smoketest
スクリプトでCryostatのデモインスタンスを実行できます:
$ smoketest.sh
CryostatとJMCエージェントの統合を使用するためには、そのJARがアプリケーションと同じコンテナに存在することを保証しなければなりません。そして、JARを指定したjavaagent
フラグでアプリケーションを実行します:
-javaagent:target/org.openjdk.jmc.agent-1.0.0-SNAPSHOT.jar
注:JMCエージェントは、AdoptiumのJMCリリースからダウンロードできます
Cryostat JMCエージェント統合の利用
Cryostatエージェントは、APIハンドラの集合を提供します。これは JMC エージェントと対話し、カスタムイベント用のテンプレートを管理します。ProbeTemplate
ハンドラにより、ユーザは Probe テンプレートを登録または削除できます。これらのテンプレートは、注入されるカスタムイベントを記述した XML ファイルです。Cryostatに保存され、エージェントが動作している有効なターゲットに適用したり、不要になったら削除できます。同様に、TargetProbePost
、Delete
、Get
ハンドラは、ターゲットJVMからのカスタムイベント設定の追加、削除、取得を容易にします。
カスタムイベント監視
作業の流れの例として、Cryostat エージェントが、Cryostat 自体をターゲットアプリケーションとして現在動作しているとします。ここで、Cryostat にカスタムイベントを追加して、Cryostat の実行状況を監視したいとします。手始めに、作業するためのProbeテンプレートが必要なので、次のように考えてみます:
<jfragent> <!-- グローバル設定オプション --> <config> <!-- イベントクラス名を生成する際に使用する接頭辞です。 --> <classprefix>__JFREvent</classprefix> <!-- 配列やオブジェクトのパラメータを文字列として記録できるようになります。 これにより、配列の要素や文字列以外のオブジェクトに対して toString が呼び出されるようになります。 これは、toStringメソッドの実装が悪いとトラブルの原因になります。 注意して使用してください。 --> <allowtostring>true</allowtostring> <!-- 変換機能を使用できるようにする。 詳しくは org.openjdk.jmc.agent.converters パッケージをご覧ください。 --> <allowconverter>true</allowconverter> </config> <events> <event id="demo.cryostat.jfr"> <label>CryostatDemoEvent</label> <description>Event for the agent plugin demo</description> <path>demo</path> <stacktrace>true</stacktrace> <class>io.cryostat.net.web.http.generic.HealthGetHandler</class> <method> <name>handle</name> <descriptor>(io/vertx/ext/web/RoutingContext;)V</descriptor> </method> <!-- 位置 {ENTRY, EXIT, WRAP}--> <location>ENTRY</location> </event> </events> </jfragent>
これは、handl
e メソッドのエントリポイントにカスタムイベントを追加する単純な設定です。一旦注入されると、この設定は handle
メソッドが呼ばれるたびにカスタム CryostatDemoEvent
を発行します。
テンプレートの <config>
セクションは、カスタムイベントクラスに使用するプレフィックスと、高度な機能が必要であるかどうかを決定します。Cryostatエージェントは、イベントと共に発行されるフィールドとメソッドパラメータの取得をサポートします。そして、ユーザ定義の変換メソッドを利用して、これらのフィールドとパラメータをJDK Flight Recorderが使用できるコンテンツタイプに変換します。ただし、この機能は <config>
セクションで特別に有効にする必要があります。
<config>
セクションに続いて、注入するイベントをいくつでも指定できます。必要なのは、イベントID、ラベル、説明だけです。<path>
要素は、JDK Mission Control のようなグラフィカルなツールで表示されるイベントのパスに対応します。<stacktrace>
要素は、イベントと共にスタックトレースを記録するかどうかを決定します。
<class>
, <method>
, <location>
要素は、イベントが注入されるべき場所を正確に決定します。メソッドの正式な記述子をここで指定する必要があります。これは、(Parameter Descriptor*)Return Descriptor
という形式を取ります。例えば、int isAlive()
というメソッドの場合、メソッド記述子は、()Z
となります。メソッド記述子の形式についての詳細は、JVMの仕様を参照してください。
カスタムイベントの注入と記録
Probeのテンプレートがあれば、あとはCryostatに数回のAPIリクエストを送り、それをアップロードしてターゲットに適用するだけです。(今回は、ターゲットがCryostat自身であることを忘れないでください)。
$ curl -F probeTemplate=@cryostat-probe.xml http://0.0.0.0:8181/api/v2/probes/
この最初のリクエストはProbeTemplateUpload
ハンドラ(api/v2/probes
)を呼び出すものです。このハンドラは ProbeTemplate
が probeTemplate
という名前のフォームにアップロードされることを想定しています。
あとは、もう一回リクエストを送るだけです:
$ curl -X POST localhost:8181/api/v2/targets/localhost/probes/cryostat-probes
2番目のリクエストはTargetProbePost
ハンドラ(api/v2/targets/:targetID/probes/:probeTemplate
)を呼び出します。このハンドラにターゲットと Probe テンプレートを与えると、ターゲットに Probe テンプレートを適用し、カスタムイベントを注入します。次にレコーディングが実行されたとき、カスタムイベントが記録されます。
まとめ
JMCエージェントは、カスタムイベントを作成・記録するための強力なツールで、それらのイベントを手動で書き込んだり、アプリケーションを再構築する必要がありません。Cryostatエージェントは、同じ利点をコンテナ化された環境にもたらしてくれます。JMCエージェントの対応がアップストリームに組み込まれたことで、これらの機能をこれまで以上に簡単に活用できるようになりました。