ContainerJFR入門: コンテナのためのJDK Flight Recorder

Red Hat で Solution Architect として OpenJDK を担当している伊藤ちひろ(@chiroito)です。

この記事は、Red Hat Developerのブログ記事、Introduction to ContainerJFR: JDK Flight Recorder for containers - Red Hat Developer の翻訳記事です。


https://developers.redhat.com/blog/wp-content/uploads/2021/01/2020_Java_ContainerJFR_Featured_Article__A-01.png

OpenJDKは長い間、実世界のアプリケーションやワークロードのトップに君臨してきました。これは、パフォーマンス、互換性、信頼性、そして観察可能性を一体化して選ばれています。長年にわたり、JDK Flight Recorder (JFR) と JDK Mission Control (JMC) は、OpenJDKの成功に貢献してきました。しかし最近まで、どちらも商用機能であり、特定のユーザやワークロードでしか利用できませんでした。

2018年、JDK Mission ControlとJDK Flight Recorderがオープンソース化されました。JDK Flight Recorderは現在、OpenJDK 8の後期リリースとOpenJDK 11以降のすべてのバージョンのJava Virtual Machine(JVM)に組み込まれています。これらのツールがオープンソース化されたことで、そのパワー(常時稼働、ゼロオーバーヘッドに近いプロダクション・プロファイリングとモニタリング、アプリケーション固有のカスタムイベント、統合コアJDK分析ツール)がすべてのJDKユーザーに提供されます。一方で、JDK Mission ControlとJDK Flight Recorderは、コンテナ化が急速に進む世界に登場しました。これは、これらのために設計された物の見方や捉え方ではありません。

デスクトップ専用のJDK Mission Controlアプリケーションでは、開発者や管理者がローカルディスク上のフライトレコーダにアクセスする必要があります。それ以外の場合は、クラウド上のJava Management Extensions(JMX)を介してアプリケーションに直接接続するという、複雑で潜在的に安全でない設定に頼ることになります。同様に、ベアメタルに特化したJDK Flight Recorderでは、JVMがローカル・ファイルシステムに記録をダンプできますが、アプリケーションがコンテナ内で実行されている場合はできません。その場合、ファイルシステムは外部から容易にアクセスできず、記録を取得して分析することはできません。

この記事では、Red Hatの製品になる途中の若いプロジェクトであるContainerJFRを紹介します。ContainerJFRは、クラウド上のJDK Flight Recorderと、エンドユーザーのワークステーションとの間の隙間を埋めることを目指しています。

手動でContainerJFRのインストールとセットアップ

ContainerJFRを手動でインストールするには、Quay.ioで利用可能なイメージを使用するのが簡単です。以下を実行して、基本的なインストールと製品のデモを行ってください。

$ podman run -it --rm -p 8181 -e CONTAINER_JFR_WEB_HOST=0.0.0.0 quay.io/rh-jmc-team/container-jfr:latest

より本格的なデモンストレーションを行うには、ContainerJFRのリポジトリをクローンして、そのsmoketest.shを実行します。以下では、テストやデモのために、Podmanポッドにいくつかのコンテナをセットアップしています。

$ git clone https://github.com/rh-jmc-team/container-jfr

$ cd container-jfr

$ sh smoketest.sh

ContainerJFRの認証情報は、今回の場合、smoketest:smoketestです。もう一つのアプリケーションの認証情報は admin:adminpass123 です。

Red Hat OpenShiftでのContainerJFRをデプロイ

Red Hat OpenShiftや他のKubernetesクラスタにアクセスできる場合は、ContainerJFR Operatorを使ってContainerJFRをクラスタにデプロイできます。

$ git clone https://github.com/rh-jmc-team/container-jfr-operator

$ cd container-jfr-operator

$ oc login # 最初に稼働中のOpenShiftクラスタにログインしていることを確認してください。

$ make deploy # ContainerJFR CR、必要なServiceAccountとRole/RoleBindingsなどとともに、Operatorをクラスタに直接デプロイします。

$ make catalog # make deployの代替となります。これにより、カスタムカタログソースがクラスタに追加され、管理者ビューのOperatorHubパネルからOperatorをインストールできます。

:まだOpenShiftやKubernetesのクラスタにアクセスできない場合は、Red Hat CodeReady Containersを試してみてください。

図1は、カスタムカタログソースを追加するために$ make catalogを発行した後のOpenShift OperatorHubのContainerJFRを示しています。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/operatorhub-768x387.png

図1:OpenShift OperatorHubのContainerJFR

図2は、デフォルトの名前空間にインストールされたContainerJFRインスタンスです

https://developers.redhat.com/blog/wp-content/uploads/2020/10/installed-operator-1-768x160.png

図2:ContainerJFRをプロジェクトの名前空間にインストール

ContainerJFRをインストールしたら、図3に示すように、そのためのカスタムリソースを作成します。名前は好きなものを選び、minimalの設定はfalseのままにしておきましょう。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/create-containerjfr-cr-768x325.png

図3:ContainerJFRのカスタムリソースの作成と設定

しばらくすると、OperatorはContainerJFRとそのサービスのデプロイを終了します。公開されたルートURLを表示する任意のビューを使用して、ContainerJFRのWebクライアントを見られます。図4は、OpenShiftのトポロジービューでContainerJFRを示しています。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/topology-view-768x388.png

図4:OpenShiftのトポロジービューでのContainerJFR

次に、ContainerJFRの主な機能を紹介し、コンテナ管理されたOpenJDKアプリケーションに設定する方法を紹介します。

ContainerJFRによる発見

ContainerJFRは、OpenJDKアプリケーションと一緒に「サイドカー」として動作するコンテナ型のJVMです。ランタイム環境に応じて、JMX対応のアプリケーションを検出するための最適な戦略を自動的に選択します。docker-composepodman-composeで動作するアプリケーションの場合、ContainerJFRはJava Discovery Protocol (JDP)を使用します。KubernetesやOpenShiftで動作するアプリケーションの場合は、エンドポイントを使用することになります。これらは、ContainerJFRが現在提供しているプラットフォーム実装の一例に過ぎません。異なるコンテナプラットフォームのためにカスタマイズされたサポートが必要な場合は、簡単に拡張できます。

Java Management Extensions

アプリケーションでJMXが有効になっており、JMXポートが公開されていてContainerJFRが到達可能であることを確認してください。現実的には、アプリケーションの起動時に以下のJVMフラグを渡すことを意味します。

-Dcom.sun.management.jmxremote.port=9091 -Dcom.sun.management.jmxremote.rmi.port=9091

次に、ポートを公開します。これには、お使いのコンテナプラットフォームが採用しているメカニズムを使用します。OpenShiftやKubernetesでは、デプロイメント用のサービスを作成し、そのサービスに公開ポートを追加します。デフォルトでは、ContainerJFRはJMXのために9091ポートを使用しますが、ポートにjfr-jmxという名前をつければ、任意のポート番号を使用できます。

図5は、OpenShift TopologyビューでContainerJFRと2つのサンプルアプリケーションを表示しています。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/topology-with-apps-768x388.png

図5:ContainerJFRと2つのサンプルアプリケーション。

Java Discovery Protocol

PodmanやDockerで動作させている場合や、ホストマシン上でローカルのJVMプロセスを直接動作させている場合は、Java Discovery Protocol(JDP)も有効にしておく必要があります。

-Dcom.sun.management.jmxremote.autodiscovery=true

ContainerJFRのイベントテンプレート

ContainerJFRは、JDK Flight Recorderのイベントテンプレートをサポートしています。これは、サポートされるイベントタイプと構成プロパティを事前に設定するものです。イベントテンプレートを使用することで、アプリケーションにとって意味のあるデータを取得する作業が簡単になります。また、ContainerJFRには、ターゲットJVMのJDK Flight Recorderフレームワークに登録されているすべてのイベントタイプを表示するビューがあります。このビューは、独自のイベントテンプレートを作成または修正する際に便利です。

図7のEvent Templatesビューには、あらかじめ設定されたイベントテンプレートが表示され、ローカルマシンにダウンロードして検証または修正できます。テンプレートを修正した後は、アップロードして再利用できます。また、テンプレートから記録を作成できます。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/event-templates-768x388.png

図7:ContainerJFRのイベントテンプレート

図8のEvent Typesビューには、選択したターゲットJVMに登録されているすべてのイベントタイプが表示されます。このビューを使用して、カテゴリ、キーワード、またはプロバイダでイベントを検索できます。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/event-types-768x388.png

図8:ContainerJFRのイベントタイプ

テンプレートの編集

ContainerJFRを使って、ターゲットJVMからローカルマシンにテンプレートをダウンロードし、お気に入りのテキストエディタでテンプレートのXMLドキュメントを開いて確認できます。JDK Mission Controlを使ってテンプレートをインポートし、編集することもできます。図9は、イベントテンプレートをダウンロードするためのダイアログです。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/event-template-download-768x182.png

図9:イベントテンプレートをローカルディスクにダウンロードする

カスタムテンプレート

カスタムテンプレートを作成したり、既存のテンプレートに変更を加えた後、ContainerJFRに再アップロードすると、そのテンプレートは今後も使用できるように保持されます。このテンプレートは、JVMアプリケーションで新しいレコーディングを行う際に適用できます。

イベント・テンプレートは、任意のプレーンテキスト・エディターで開いて編集できます。また、JDK Mission Controlのグラフィカルなテンプレート・エディタを使用して、テンプレートのインポート、編集、エクスポートを行えます。図10は、プレーンテキスト・エディターのイベント・テンプレートを示しています。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/template-in-editor-768x417.png

図10:任意のエディタを使用して、XML形式のイベントテンプレートを修正

新規または修正したテンプレートをアップロードすると、ContainerJFRは図11に示すようにテンプレートを検証します。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/template-to-upload-768x342.png

図11:テンプレートは、サーバーが受信すると検証される

図12は、ContainerJFRインスタンスで利用可能なすべてのテンプレートを示しています。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/template-uploaded-768x175.png

図12:後で使用できるテンプレートの一覧。

これらの機能はすべて、JDK Flight RecorderのCustom Events APIとも連携しています。アプリケーションの開発中にアプリケーション固有のイベントタイプを作成し、これらのイベントを含むカスタムイベントテンプレートを作成して、独自の継続的な本番環境の記録を仕立てられます。

ContainerJFRでの録音

ContainerJFRでは、カスタムレコーディング、スナップショット、アーカイブなど、いくつかの方法でレコーディングをキャプチャし、保存できます。

カスタムレコーディング

図13は、新しいカスタムレコーディングを設定する際に定義する構成プロパティです。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/create-custom-recording-768x388.png

図13:カスタムレコーディングの作成

まず、記録の名前ですが、ContainerJFRはターゲットごとの一意性を確保するために使用します。また、記録が自動的に停止されるまでの期間や、手動で停止されるまで継続して実行するかどうかを設定します。記録するイベントのイベント指定文字列またはテンプレートを設定する必要があります。高度なプロパティには、"to disk"、"max size"、"max age "などがあります。これらのプロパティの詳細については、JDK Flight Recorderのドキュメントをご参照ください。

スナップショット

図14は、新しいスナップショット記録を作成するためのダイアログです。スナップショットとは、他の記録で取得したすべての情報の概要です。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/snapshot-recording-768x218.png

図14:ワンクリックでスナップショット記録を作成

ターゲットで複数のカスタム記録を同時に実行している場合、スナップショットを使用して、その時点で新しい記録ファイルを作成できます。スナップショットには、他のすべての記録のデータがマージされています。スナップショットは、特定の時点での単一の連続記録のデータを保存するのにも便利です。

データの流れ

記録を作成する際、ContainerJFRにフライトレコーダを開始する命令をターゲットJVMに送信するよう依頼します。この時点では、アプリケーションの外部にデータは転送されず、記録の名前、状態、開始時間、およびその他の基本的なメタデータのみが転送されます。記録データは、コンテナ内のターゲットアプリケーションのメモリにのみ保存されます。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/recording-list-768x388.png

図15:ContainerJFRは、選択したターゲットJVMに存在するすべてのレコーディングを表示

アーカイビング

アーカイブは、記録のスナップショットをアプリケーションからContainerJFRにストリーミングします。ContainerJFRは直ちにスナップショットをローカルディスク(OpenShiftやKubernetesでは永続ボリューム)に書き込み、メモリから削除します。アプリケーションがスケールダウンするなどして消えてしまっても、分析のために記録データにアクセスできます。誤って.jfrファイルを削除してしまっても、ワークステーションのローカルディスクからアーカイブに再アップロードできます。これは、ContainerJFRをクラスタから削除して、後で再インストールした場合にも有効です。

図16のアーカイブされた記録リストには、ContainerJFRのパーシステントストレージに保存されたすべての記録が表示されます。これは、すべてのターゲットJVMで共通です。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/archived-list-768x388.png

図16:ContainerJFRの永続ストレージに保存された記録データ

ContainerJFRによる自動解析

ContainerJFRでは、ローカルマシンやクラスター外部にデータを転送することなく、クラウドのデプロイメント内でフライトレコーディングの自動分析を実行できます。この機能を使えば、接続速度の遅いホテルや空港から、スマホやタブレットだけでアプリケーションの健全性をチェックできます。

図17のアクティブな記録とアーカイブされた記録のリストを展開すると、クラスター内で生成された自動分析結果が表示されます。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/automated-report-768x388.png

図17: アクティブな記録とアーカイブされた記録の自動分析レポート

記録を拡張すると、ContainerJFRはJDK Mission Controlのバックエンドを使用して自動分析レポートを生成し、アプリケーションの明らかな問題や可能性のある問題を警告します。また、レポートをHTML形式で保存し、後で参照できます。

分析にGrafanaを使う

自動分析レポートに十分な情報が含まれていない場合や、より詳細に検証したい問題が指摘された場合は、ContainerJFRポッド内のjfr-datasourceエクスポータに記録を送れます。そこから、Grafanaを使ってデータを見られます。図18は、記録リストアイテムのアクションメニューを示しており、これを使って記録をGrafanaダッシュボードに送信できます。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/view-in-grafana-768x197.png

図 18: さらなる分析のために記録を Grafana ダッシュボードに送信

ContainerJFRは、時系列データを含む構成済みのダッシュボードを提供していますが、重要なメトリクスを含む独自のダッシュボードを作成することが推奨されます。繰り返しになりますが、あなたのデータはクラスタから出ないことに注意してください。.jfrファイルからGrafanaのメトリクスへの変換を行うjfr-datasourceはContainerJFRポッド内に隠されており、Grafanaダッシュボードのインスタンスは生成された認証情報(OpenShiftやKubernetesのシークレットに保存されている)で保護されてから外部に公開されます。その生成された認証情報は、以下のコマンドで簡単に取り出すことができます。

$ oc get secret containerjfr-grafana-basic -o json | jq -crM .data.GF_SECURITY_ADMIN_USER | base64 -d
$ oc get secret containerjfr-grafana-basic -o json | jq -crM .data.GF_SECURITY_ADMIN_PASSWORD | base64 -d

認証情報を取得したら、図19に示すように、Grafanaダッシュボードのログインページにプラグインして、メトリクスの表示を開始します。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/grafana-dashboard-1-768x388.png

図19:設定済みのGrafanaダッシュボードでは、アプリケーションのパフォーマンスをより詳細に把握できます(バッテリーが含まれインストール済)。

解析にJDK Mission Controlを使う

最後に、さらに詳細な情報が必要な場合は、ContainerJFRから記録ファイルをローカルディスクにダウンロードし、フル機能を備えたオフラインのJDK Mission Controlデスクトップ・アプリケーションで開くことができます。これは、記録ファイルが実際にクラスタを離れる唯一のシナリオです。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/jmc-768x417.png

図20:JDK Mission Controlデスクトップ・アプリケーションを使用して、クラウド・アプリケーションから収集したデータを深堀り

JDK Mission Controlデスクトップ・アプリケーションは豊富な機能を提供していますが、その議論は別の記事に譲りましょう。

ContainerJFRによるセキュアな認証

JDK Flight Recorderは、大きなランタイムオーバーヘッドなしに膨大な量のデータを取得します。データを安全に保ち、その完全性を確保することは極めて重要です。図21に示すように、ContainerJFRは、アプリケーションがJMX接続を世界中に公開する必要はなく、OpenShift名前空間やDockerまたはPodmanネットワーク内からの接続にのみ公開します。

https://developers.redhat.com/blog/wp-content/uploads/2021/01/graph0-768x650.png

図21: 安全なContainerJFRの展開の概要

ContainerJFRは、Java Flight Recorder(原文のまま)のデータのコピーを受け取った後は、指示に基づいて、セキュアなAPIリクエストを通じてのみデータにアクセスできます。セキュリティで保護されたAPIリクエストは、アプリケーションに接続するためのJMX認証と、ContainerJFRに接続するための別の認証層をサポートしています。

OpenShiftで実行する場合、図22に示すように、すべての機密性の高いAPIリクエストは、認証のためにユーザーアカウントトークンを必要とします。なお、HTTPまたはWebSocketを介したクライアントからContainerJFRへのリクエスト、およびJMXを介したContainerJFRからターゲットへのリクエストは、すべてデフォルトでSecure Socket LayerまたはTransport Layer Security(SSL/TLS)プロトコルをサポートし、有効になっています。

https://developers.redhat.com/blog/wp-content/uploads/2020/10/cjfr-login-768x388.png

図22:ContainerJFRはOpenShiftクラスタの認証サーバを利用してユーザ認証

ContainerJFRの今後の予定

ContainerJFRはまだ若いプロジェクトであり、コンテナやモニタリングの世界は常に拡大しているため、私たちの視野には多くのものがあります。将来的には、ContainerJFRに以下のような変更や改良を加えていきたいと考えています。

  • ContainerJFRが対象となるアプリケーションを識別し、グループ化し、ラベル付けするためのより優れた柔軟な方法を実装します。例えば、OpenShiftやKubernetesのラベルやアノテーションを調べられます。
  • 1回のリクエストでターゲットグループ全体の記録を管理するために使用される、バッチ操作のサポートを追加します。
  • トリガー機能の追加により、事前に定義されたイベントが発生したときに、ターゲットまたはターゲットグループで記録を自動的に開始および停止できるようになりました。例えば、新しいターゲットが現れたときに、事前に定義されたテンプレートで自動的に記録を開始できます。
  • Grafanaのビューやその他のビジュアライゼーションをContainerJFRのWebクライアントに直接埋め込む。
  • デスクトップのJDK Mission Controlアプリケーションに統合またはディープリンクを提供する。

今後のアップデートについては、ContainerJFRリポジトリをご覧ください。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。