私は、OpenJDKのCommitter業や仕事でミドルウェアのSolution Architectとして活動している関係上、最近はコンテナ上でJavaアプリケーションを動かすことが非常に多いです。
KubernetesでJavaアプリを監視する場合には、Elasticsearch+KibanaやPrometheus+GrafanaなどでログやMBeanを監視する方法が一般的に行われています。 Java 11では有償JDKに含まれていた機能がOpenJDKへ寄贈され、JDK Flight Recorder (JFR)として生まれ変わりました。JFRはJVMの内部の情報やその上で動くJavaアプリケーションの様々な情報をほとんど負荷無く記録し、ファイルとして取得できます。このファイルをJDK Mission Controlなどのツールを使って確認し、これまでより詳細に分析できます。
これまででも、コンテナ環境においてもJFRを使用できました。しかし、記録の開始・停止などの管理やファイルとしての取得などを運用する環境を効率よく構築するのは非常に大変でした。
そこで誕生したのが Cryostat (旧名:ContainerJFR)です。Cryostatはコンテナ上で動いているJavaアプリケーションに接続して、JFRを管理します。また、JFRの情報分析結果の簡易レポートの表示やJFRファイルのダウンロードができるWeb UIが用意されている他、JFRで記録された情報をGrafanaへ取込できるため、問題発生時に非常に役立つツールになっています。これはKubernetes Operatorも用意されており、環境の構築も簡単にできます。
今回は、Kubernetes上にCryostatをインストールする方法、サンプルのJavaアプリケーションを監視する方法、分析に役立つツールへのアクセス方法を紹介します。
また、今回は用意した環境の都合上、KubernetesとしてOpenShiftを使用します。
今回の環境
- OpenShift 4.5のクラスタ(Azure Red Hat OpenShift、Red Hat OpenShift Service on AWSも可)
- クライアントとなるRHEL 8.2
OpenShiftへCryostatをインストール
Kubernetes上でCryostatを使う場合にはCryostat Operatorを使用すると便利です。Cryostat OperatorをはOperatorHub.ioに無いため、自らビルドする必要があります。
Operator SDKをインストール
Cryostat OperatorはOperator SDKに依存関係を持っています。そのため、Cryostat Operatorをインストールするには、まずOperator SDKをインストールします。
export ARCH=$(case $(uname -m) in x86_64) echo -n amd64 ;; aarch64) echo -n arm64 ;; *) echo -n $(uname -m) ;; esac) export OS=$(uname | awk '{print tolower($0)}') export OPERATOR_SDK_DL_URL=https://github.com/operator-framework/operator-sdk/releases/download/v1.8.0 curl -LO ${OPERATOR_SDK_DL_URL}/operator-sdk_${OS}_${ARCH} chmod +x operator-sdk_${OS}_${ARCH} && sudo mv operator-sdk_${OS}_${ARCH} /usr/local/bin/operator-sdk
参考:Installation | Operator SDK
Cryostat OperatorをOpenShiftへインストール
Operator SDKがインストールし終わったらCryostat Operatorをインストールしましょう。
まずは、OpenShiftへログインして今回使用したいプロジェクト(名前空間)を指定します。
oc login -u [ユーザ名] -p [パスワード] [APIのURL] oc project [プロジェクト名]
次に、Cryostat Operatorのビルドに必要なmake
とgo
をインストールします。そして、Cryostat Operatorのソースコードを落としてからmake
コマンドでビルドしてインストールします。
sudo yum install -y make go git clone https://github.com/cryostatio/cryostat-operator.git cd cryostat-operator make cert_manager make deploy_bundle
Cryostat Operatorがインストールされたことを確認します。
oc get deployments cryostat-operator-controller-manager
以下のように表示されていれば成功です。
NAME READY UP-TO-DATE AVAILABLE AGE cryostat-operator-controller-manager 1/1 1 1 1h
OpenShiftの場合はWebコンソールのInstalled Operators
で表示される一覧に追加されています。
Cryostatをデプロイ
Cryostat Operatorのインストールが終わったので、Cryostatをデプロイしてみましょう。
cat <<EOF > cryostat.yaml apiVersion: operator.cryostat.io/v1beta1 kind: Cryostat metadata: name: cryostat-sample spec: minimal: false EOF oc create -f cryostat.yaml
参考:cryostat-operator/config.md at main · cryostatio/cryostat-operator · GitHub
ここではmetadata.name
に名前を設定します。spec.minimal
は最小構成でインストールするかどうかを表します。最小構成はCryostatのみをインストールします。最小構成ではカスタマイズされたGrafanaとその関連ツールはインストールされません。カスタマイズされたGrafanaがあると便利なのでfalse
にする事をオススメします。
Cryostatがデプロイされたことを確認します。
oc get deployments cryostat-sample
以下のようになっていればデプロイ成功です。
NAME READY UP-TO-DATE AVAILABLE AGE cryostat-sample 1/1 1 1 20h
こちらもOpenShiftの場合はWebコンソールのInstalled Operator
からGUIで作成できます。Cryostat
タブを選択するとCryostat
の一覧を表示します。そこでCreate Cryostat
と言うボタンがあるので、ここから作成画面へ遷移します。
Cryostatを作成するページに移動するので、必要な情報を入力してCreate
ボタンを押せば作成されます。
ここではCLIで作成したとおり、名前をcryostat-sample
、最小構成をfalse
にして作成しています。
ここまでの作業が完了するとCryostatを使用する準備が完了しました。Webコンソール上のトポロジでは以下のようになっています。
環境構築はこれで終わりです。JFRを効率よく管理する環境が簡単に作れました。
Cryostat Web UIへのログイン
Cryostatは便利なWeb の UIを提供しており、デプロイが正常に完了していると、このUIへアクセスできるようになっています。このUIへアクセスするにはそのURLが必要になりますが、URLはoc
コマンドやWebコンソールから確認できます。
oc
コマンドでURLを取得する場合は、先ほど作成したcryostat-sample
に対して以下のように実行します。
oc get route cryostat-sample
出力結果のHOST/PORT
がCryostatのUIのURLになります。
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD cryostat-sample cryostat-sample-jmx-sample3.apps.cluster-2ab2.2ab2.sandbox1193.opentlc.com cryostat-sample 8181 reencrypt None
また、OpenShiftのWebコンソールから確認するには、Installed Operators
のCryostatからCryostat
タブを選択しcryostat-sample
をクリックします。
右下のApplication URL
にURLが記載され、リンクもされているのでクリックします。
このUIへアクセスすると以下のようにトークンを求められます。
以下のコマンドを事項するとトークンが得られます。
oc whoami --show-token
出力されたトークンを入力してログインしましょう。
監視対象のアプリケーションをデプロイ
それでは、Cryostatで監視するJavaアプリケーションをデプロイしましょう。
CryostatではJava Management Extentions (JMX)を介してJFRを管理します。そのため、アプリケーションではJVM引数でJMXを有効化しなければなりません。JMXのポートはデフォルトで9091を監視します。他のポート番号にする場合はServiceのspec.ports.name
をjfr-jmx
にして下さい。また、Java Discovery Protocol (JDP)でもJavaアプリケーションを検知できます。こちらについては難易度が高いのでServiceの名前にだけ注意するようにしましょう。
今回はサンプルイメージを用意しましたのでそのイメージを使用します。このサンプルでは以下のようにJMXを有効化しています。
-Dcom.sun.management.jmxremote.port=9091 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
設定の簡素化のために認証は無効化していますが、CryostatはJMXの認証にも対応しているので必要な場合は設定して下さい。
認証の設定方法:https://github.com/cryostatio/cryostat#monitoring-applications
サンプルアプリをデプロイ
それではサンプルアプリケーションをデプロイします。
cat <<EOF > myapp.yaml apiVersion: v1 kind: List items: - apiVersion: apps/v1 kind: Deployment metadata: labels: app: jmx-container-sample app.kubernetes.io/component: jmx-container-sample app.kubernetes.io/instance: jmx-container-sample name: jmx-container-sample spec: replicas: 1 selector: matchLabels: deployment: jmx-container-sample template: metadata: labels: deployment: jmx-container-sample spec: containers: - image: 'quay.io/cito/jmx-container-sample:1.0-SNAPSHOT' name: jmx-container-sample ports: - containerPort: 8080 protocol: TCP - containerPort: 9091 protocol: TCP - apiVersion: v1 kind: Service metadata: labels: app: jmx-container-sample app.kubernetes.io/component: jmx-container-sample app.kubernetes.io/instance: jmx-container-sample name: jmx-container-sample spec: ports: - name: 8080-tcp port: 8080 protocol: TCP targetPort: 8080 - name: jfr-jmx port: 9091 protocol: TCP targetPort: 9091 selector: deployment: jmx-container-sample EOF oc apply -f myapp.yaml
サンプルアプリケーションがデプロイされたことを確認します。
oc get deployments jmx-container-sample
以下のように出力されればデプロイされています。
NAME READY UP-TO-DATE AVAILABLE AGE jmx-container-sample 1/1 1 1 20h
JFRの管理
サンプルアプリケーションを起動すると、Cryostatによって自動で検知されJFRが有効なJavaアプリケーションと言うことで管理の対象となります。
サンプルアプリケーションがCryostatで管理の対象となったことを確認します。
oc get flightrecorders
以下のようにサンプルアプリのpod名と同じものがあれば成功です。今回はjmx-container-sample
で始まるものがサンプルアプリケーションになります。
NAME AGE cryostat-sample-5b674ff96c-rshvn 4m10s jmx-container-sample-8649b7699d-9v2fl 4m21s
CryostatではCryostat自身も管理されるため、先程デプロイしたcryostat-sample
のpodも含まれます。
また、この情報もWebコンソールから確認できます。Installed Operators
のCryostat
からFlight Recorder
タブを選択して下さい。
JFRを起動
CryostatはRecordingというリソースとCryostat Web UIのどちらかでJFRを起動できます。 Recordingリソースでは記録したいJFRのイベントを詳細に指定しなければなりませんが、記録の情報はKubernetesのリソースとして管理されます。Cryostat Web UIではRecordingリソース同様にイベントを詳細に指定することもできますが、イベントのテンプレートも指定できます。しかし、こちらの方法ではRecordingリソースは生成されないためKubernetes上で記録を管理できません。
リソースとしての起動
全てのイベントを記録するのは非常に膨大になるので、試しに今回はサンプルアプリケーションに対し通信の入出力の情報を記録を試みます。対象となるJFRはspec.flightRecorder.name
に先程のoc get flightrecorders
で出力されたものを指定します。記録するイベントはspec.eventOptions
で指定します。どの様なイベントがあるかはこちらを参照して下さい。
cat <<EOF > recording.yaml apiVersion: operator.cryostat.io/v1beta1 kind: Recording metadata: name: cont-recording spec: name: cont-recording eventOptions: - "jdk.SocketRead:enabled=true" - "jdk.SocketWrite:enabled=true" duration: 0s archive: true flightRecorder: name: jmx-container-sample-8649b7699d-9v2fl EOF oc create -f recording.yaml
参照:cryostat-operator/api.md at main · cryostatio/cryostat-operator · GitHub
記録が開始できているかを確認します。
oc get recordings
出力結果は以下のようになります。
NAME AGE cont-recording 25h
Cryostat Web UIで起動
Cryostat Web UIでJFRを起動する場合には、Cryostat Web UIにログインしてから左側のメニューからRecordingsを選択します。右側にFlightRecorderとして管理されているリソースのリストがあるので、そこからJFRを起動したいJVMを指定します。サンプルJavaアプリをデプロイした場合はjmx-container-sample
から始まるJVMを指定して下さい。そうすると既に動いているJFRが表示されます。
この例では起動時に起動したのでそのJFRが記載されています。
Cryostat Web UIでJFRを起動
JFRを起動するにはCreate
ボタンを押して、作成画面へ行きます。
この画面では記録の名前や記録の期間、記録するイベントなどJFR自体の設定が行なえます。最低限はこの3つを設定すれば大丈夫です。Continuous
のチェックボックスは継続して記録を取り、ファイルをダンプする時点から逆算して指定した期間分の情報が取れるようになっています。
オススメの設定は、名前は人間が分かりやすい名前が良いです。期間はContinuousを設定しつつ、障害を検知してからどれくらいの時間あればJFRファイルをダウンロードできるかを逆算しておき余裕を持って設定しておきましょう。イベントは通常はContinuousを選択し、具体的なトラブルがあったらProfilingやAlllを設定しましょう。
設定内容の詳細についてはJFRのドキュメントを参照下さい。
Grafanaへアクセスする
Cryostatの便利なポイントの1つは、JFRの内容をGrafanaで見られることです。JFRの内容をGrafanaで見るには、Cryostat Web UIのRecordingタブから見たいRecordingsの右側にあるメニューをクリックしてView in Grafana ...
を選択します。そうすると、Grafanaのページへ遷移します。
Grafanaにアクセスすると認証情報の入力が求められます。この認証情報は、以下のコマンドを実行すると得られます。ユーザ名はadmin
ですが、パスワードは環境によって異なります。
oc get secret cryostat-sample-grafana-basic -o json | jq -crM .data.GF_SECURITY_ADMIN_USER | base64 -d oc get secret cryostat-sample-grafana-basic -o json | jq -crM .data.GF_SECURITY_ADMIN_PASSWORD | base64 -d
Grafanaへログインが成功すると、通常のGrafanaと同じように操作ができます。JFRのイベントはjfr-datasource
というデータソースに含まれています。好きなようにダッシュボードなどを作成してみて下さい。
JFRをダウンロード
Grafanaは非常に便利ですが、JFRの情報を詳細に分析するには、現時点では専用のJDK Mission Controlの方が優れています。 Grafanaを使った分析で気になる点が見つけられたら、これまで通りJDK Mission Controlを使ってより詳細に分析しましょう。
Cryostatでは取得していたJFRのファイルをダウンロードできます。Cryostat Web UIのRecordingsでダウンロードしたいRecordingの右側をクリックするとメニューが出るので、Download Recording
をクリックしてダウンロードします。
ダウンロードしたファイルはJDK Mission Controlで開けますので、JMCを使用して詳細に分析ができるようになります。
最後に
これでコンテナ上において JDK Flight Recorder をより便利に使うためのツールである Cryostat の紹介は終わります。 CryostatとCryostat Operatorの開発はGithub上で行われているので、興味がある場合にはぜひスターを付けて下さい。
また、Cryostatの開発者が書いた記事の翻訳もありますので興味のある方はぜひご覧下さい。