GraalVM テクノロジーは、Java アプリケーションに静的コンパイルを使用して、コールドスタートの遅延や実行時のメモリ使用量の多さなどの問題を解決します。GraalVM アプリケーション向けに、Application Real-Time Monitoring Service (ARMS) は静的インストルメンテーションソリューションを提供します。このソリューションは、通常 Java エージェントによって実行時に行われるバイトコードの変更を、静的コンパイルのフェーズに移行します。このプロセスは静的エンハンスメントとして知られ、標準で提供される可観測性を実現します。
GraalVM は最先端技術です。本番環境で使用したことがない場合は、本番環境での使用を検討する前に、テスト環境で十分に検証することを推奨します。また、このソリューションを使用して ARMS と統合する際に質問がある場合は、DingTalk サポートグループ (グループ番号: 80805000690) を通じてお気軽にお問い合わせください。
制限事項
-
アプリケーションは GraalVM の静的コンパイルに対応している必要があります。Spring Boot アプリケーションについては、「関連ドキュメント」の手順をご参照ください。
-
ARMS が提供する特定の GraalVM JDK バージョンを使用する必要があります。
-
GraalVM の静的コンパイルには、特定の環境前提条件があります。詳細については、GraalVM の「公式ドキュメント」をご参照ください。
-
GraalVM アプリケーションに対して、ARMS は現在、コア機能であるトレースとメトリクスのみをサポートしています。Arthas、継続的プロファイリング、メモリのスナップショットなどの機能はサポートされていません。さらに、GraalVM アプリケーションのメモリ構造は標準の JVM アプリケーションとは異なります。そのため、JVM 監視では、Metaspace、非ヒープメモリ、ダイレクトバッファのデータは表示されません。
操作手順
手順 1:依存関係のインストール
GraalVM 環境では、まず以下の依存関係をインストールします。
-
アプリケーションのリージョンに対応する GraalVM 用の ARMS エージェントをダウンロードします。
現在、サポートされているのは以下のリージョンのみです。他のリージョンのサポートをリクエストするには、DingTalk サポートグループ (ID:80805000690) までお問い合わせください。
リージョン
パブリック URL
VPC アドレス
中国 (杭州)
wget "http://arms-apm-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zipwget "http://arms-apm-cn-hangzhou.oss-cn-hangzhou-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip中国 (上海)
wget "http://arms-apm-cn-shanghai.oss-cn-shanghai.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zipwget "http://arms-apm-cn-shanghai.oss-cn-shanghai-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip中国 (北京)
wget "http://arms-apm-cn-beijing.oss-cn-beijing.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zipwget "http://arms-apm-cn-beijing.oss-cn-beijing-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip中国 (張家口)
wget "http://arms-apm-cn-zhangjiakou.oss-cn-zhangjiakou.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zipwget "http://arms-apm-cn-zhangjiakou.oss-cn-zhangjiakou-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip中国 (深圳)
wget "http://arms-apm-cn-shenzhen.oss-cn-shenzhen.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zipwget "http://arms-apm-cn-shenzhen.oss-cn-shenzhen-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zipファイルを解凍した後、
ArmsAgentNativeディレクトリに移動し、次のコマンドを実行してエージェントをローカルにインストールします。sh install.sh -
可観測性機能を備えた ARMS 拡張版の GraalVM JDK バージョンをダウンロードします: graalvm-java17-23.0.4-ali-1.2b.tar.gz。
ファイルを解凍した後、ディレクトリ内で次のコマンドを実行します。
graalvm-java17-23.0.4-ali-1.2b/bin/native-image --versionインストールが成功すると、次の出力が返されます。
native-image 17.0.9 2023-10-17 ali-1.2b OpenJDK Runtime Environment GraalVM CE 17.0.9-dev+9.1 (build 17.0.9+9-jvmci-23.0-b22) OpenJDK 64-Bit Server VM GraalVM CE 17.0.9-dev+9.1 (build 17.0.9+9-jvmci-23.0-b22, mixed mode, sharing) -
環境にまだインストールされていない場合は、Maven をダウンロードします: apache-maven-3.8.4-bin.tar.gz。
ファイルを解凍した後、
JAVA_HOMEおよびMAVEN_HOME環境変数をそれぞれのパスに設定します。/xxx/を実際のパスに置き換えてください。export MAVEN_HOME=/xxx/apache-maven-3.8.4 export PATH=$PATH:$MAVEN_HOME/bin export JAVA_HOME=/xxx/graalvm-java17-23.0.4-ali-1.2b export PATH=$PATH:$JAVA_HOME/bin
手順 2:依存関係の追加
次の依存関係をアプリケーションに追加します。
コード内で、/xxx/dynamic-configs をアプリケーションの元の動的設定ファイルのパスに置き換えてください。
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>arms-javaagent-native</artifactId>
<version>4.1.11</version>
<type>pom</type>
</dependency>
</dependencies>
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<fallback>false</fallback>
<buildArgs>
<arg>-H:ConfigurationFileDirectories=native-configs,/xxx/dynamic-configs</arg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
手順 3:access-filter-file.json ファイルの追加
アプリケーションのルートディレクトリに、access-filter-file.json という名前のファイルを次の内容で作成します。
{ "rules": [
{"excludeClasses": "sun.launcher.LauncherHelper"}
]
}
access-filter-file.json ファイルは、GraalVM が動的機能に関する情報を収集するために使用するエージェントが、sun.launcher.LauncherHelper からリフレクションデータを収集しないようにします。sun.launcher.LauncherHelper クラスは JVM の起動時に使用され、静的にコンパイルされたネイティブイメージにはそのリフレクションデータは必要ありません。このデータを含めると、コンパイルエラーが発生する可能性があります。
手順 4:アプリケーションの事前実行
ARMS エージェントからの動的エンハンスメントコードが最終的なネイティブイメージにコンパイルされるようにするには、まずエージェントをアタッチしてアプリケーションを実行する必要があります。この事前実行ステップにより、エージェントはアプリケーションのコアコードパスを実行して設定データを収集できます。このプロセスを支援するためのスクリプトが提供されています。スクリプト内でアプリケーションのすべての RESTful エンドポイントを宣言し、実行中に関連インターフェースを呼び出してビジネスロジックをトリガーします。
-
次のスクリプトを参考に、コメントに基づいてパラメータを更新してください。
######## ご使用の環境に合わせて以下のパラメータを変更してください。 # ARMS 接続パラメータ。LicenseKey は DescribeTraceLicenseKey API を呼び出して取得できます。 # AppName パラメータは ARMS でのアプリケーション名を指定します。分散アーキテクチャでは、単一のアプリケーションが複数のピアインスタンスを持つことがあります。 export ARMS_LICENSEKEY= export ARMS_APPNAME= # アプリケーションインターフェースのリスト。例:PS=(interface1 interface2 interface3 interface4) export PS= # アプリケーションポート。例:PORT="8080" export PORT= # ネイティブイメージファイルのパス。静的コンパイル後、ネイティブイメージファイルがアプリケーションの target ディレクトリに生成されます。 # 例:NATIVE_IMAGE_FILE="target/graalvm-demo" export NATIVE_IMAGE_FILE= # ARMS Native Agent を実行するコマンド。例:JAVA_CMD="-javaagent:./arms-native/aliyun-java-agent-native.jar -jar target/graalvm-demo-1.0.0.jar" export JAVA_CMD= ######## -
ARMS の Java エージェントをアタッチして事前実行を開始し、静的コンパイルの設定を収集します。
sh ArmsAgentNative/run.sh --collect --jvm --Carms
手順 5:アプリケーションの静的コンパイル
依存関係を追加した後、次の手順に従ってアプリケーションを静的にコンパイルします。
-
静的コンパイルを開始します。
mvn -Pnative package -
静的にコンパイルされたプロジェクトを実行します。
sh ArmsAgentNative/run.sh --native --Carms
関連操作
Docker イメージのビルド
静的にコンパイルされた GraalVM アプリケーション用の Docker イメージをビルドするには、最終的なネイティブイメージファイルを自己完結型の実行可能ファイルとして扱うことができます。このファイルには必要なランタイム情報がすべて含まれているため、イメージにコピーして、他の実行可能ファイルと同様にビルドできます。
Dockerfile の例:
-Darms.licenseKey および -Darms.appName パラメータの値を、実際のライセンスキーとアプリケーション名に置き換えてください。
FROM centos:latest
WORKDIR /app
COPY ./target/graalvm-demo /app
CMD ["/app/graalvm-demo","-Darms.licenseKey=xxx","-Darms.appName=xxx"]
ネイティブイメージの圧縮
ネイティブイメージと従来の Java アプリケーションのディスク容量を比較する場合、Java アプリケーションのサイズには通常 JDK が含まれます。これは、Java アプリケーションの実行には JDK が必要なのに対し、ネイティブイメージはすべての依存関係を含む自己完結型の実行可能ファイルであるためです。
しかし、ネイティブイメージはアセンブリコードで構成されており、Java バイトコードよりも密度が低くなっています。これは、ネイティブイメージが同じロジックを表現するためにより多くのスペースを必要とすることを意味します。アプリケーションが大きくなるにつれて、そのネイティブイメージのサイズは、Java アプリケーションとその JDK の合計サイズを超える可能性があり、デプロイと転送のオーバーヘッドが増加します。この問題を軽減するために、UPX のようなツールを使用してネイティブイメージのサイズを縮小できます。UPX は、バイナリ実行可能ファイルをより小さなものに圧縮します。圧縮されたファイルは、解凍せずに直接実行でき、実行時のパフォーマンスへの影響は最小限です。
次の出力は、ネイティブイメージを圧縮した効果を示しています。
$~/tools/upx-4.2.4-amd64_linux/upx -9 -o graalvm-demo-compressed graalvm-demo
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2024
UPX 4.2.4 Markus Oberhumer, Laszlo Molnar & John Reiser May 9th 2024
File size Ratio Format Name
-------------------- ------ ----------- -----------
172061328 -> 48870860 28.40% linux/amd64 graalvm-demo-compressed
Packed 1 file.
元の graalvm-demo ファイルは graalvm-demo-compressed ファイルに圧縮されます。圧縮後のファイルサイズは、元のサイズのわずか 28.4% です。
Fat JAR との比較:
$ls -lh
total 429M
drwxr-xr-x 5 cengfeng.lzy users 4.0K Aug 16 10:20 classes
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:19 generated-sources
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:19 generated-test-sources
-rwxr-xr-x 1 cengfeng.lzy users 165M Aug 16 10:22 graalvm-demo
-rw-r--r-- 1 cengfeng.lzy users 216M Aug 16 10:20 graalvm-demo-1.0.0.jar
-rwxr-xr-x 1 cengfeng.lzy users 47M Aug 16 10:22 graalvm-demo-compressed
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:20 graalvm-reachability-metadata
-rw-r--r-- 1 cengfeng.lzy users 38K Aug 16 10:22 libawt_headless.so
-rw-r--r-- 1 cengfeng.lzy users 879K Aug 16 10:22 libawt.so
-rw-r--r-- 1 cengfeng.lzy users 463K Aug 16 10:22 libawt_xawt.so
-rwxr-xr-x 1 cengfeng.lzy users 7.2K Aug 16 10:22 libjava.so
-rwxr-xr-x 1 cengfeng.lzy users 7.2K Aug 16 10:22 libjvm.so
drwxr-xr-x 2 cengfeng.lzy users 4.0K Aug 16 10:19 maven-archiver
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:19 maven-status
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:20 spring-aot
drwxr-xr-x 2 cengfeng.lzy users 4.0K Aug 16 10:19 test-classes
Spring Boot や RocketMQ などのすべての依存関係を含む元の Fat JAR は 216 MB です。圧縮されたネイティブイメージはわずか 47 MB です。
UPX の使用方法:
-
UPX ツールをダウンロードして解凍します。
-
UPX が
$UPX_HOMEディレクトリに解凍されていると仮定し、次のコマンドを実行してファイルを圧縮します。$UPX_HOME/upx -9 -o path/to/output-file path/to/original-file-
-9:圧縮レベルを 1 から 9 までで指定します。値が大きいほど圧縮率は高くなりますが、時間がかかります。 -
-o path/to/output-file:出力ファイルのパスを指定します。path/to/output-fileを目的のファイルパスに置き換えてください。 -
path/to/original-file:圧縮するファイルのパスを指定します。path/to/original-fileを実際のファイルパスに置き換えてください。
-