静的またはほぼ静的にリンクされたネイティブ実行可能ファイルのビルド
GraalVMネイティブ・イメージでは、デフォルトで動的にリンクされたバイナリがビルドされます。ビルド時に、まずアプリケーション・クラスおよびインタフェースがロードされ、次に動的リンクの処理中にフックされます。
ただし、必要に応じて、静的またはほぼ静的にリンクされたネイティブ実行可能ファイルを作成できます。
静的なネイティブ実行可能ファイルとは、静的にリンクされたバイナリのことであり、追加のライブラリ依存性なしに使用できます。静的なネイティブ実行可能ファイは、スリムすなわちdistrolessのコンテナ(スクラッチ・コンテナ)に簡単に配布およびデプロイできます。静的なネイティブ実行可能ファイルを作成するには、静的にmusl-libc (軽量、高速かつ単純なlibc
実装)をリンクします。
ほぼ静的なネイティブ実行可能ファイルとは、標準のCライブラリlibc
を除く、ネイティブ実行可能ファイルが依存するすべての共有ライブラリ(zlib
、JDK共有静的ライブラリ)をリンクするバイナリのことです。これは、すべてを静的にリンクするための代替オプションです。また、ユーザーのコードによっては、libstdc+
およびlibgcc
をリンクできます。このアプローチは、distrolessコンテナ・イメージへのデプロイメントに役立ちます。
このガイドでは、完全に動的、完全に静的、ほぼ静的(libc
を除く)などのネイティブ・イメージ・リンク・オプションを利用して、デプロイメント・シナリオに最適な実行可能ファイルを生成する方法を示します。
前提条件および準備
- Linux x64オペレーティング・システム
- Java 17以上のGraalVMディストリビューション
- 64ビットの
musl
ツールチェーン、make
およびconfigure
- 最新の
zlib
ライブラリ
GraalVMをインストールする最も簡単な方法は、SDKMAN!を使用することです。その他のインストール・オプションについては、「ダウンロード」セクションを参照してください。
ネイティブ・イメージを使用して静的にリンクされたアプリケーションを作成するには、zlib
ライブラリを含むmusl
ツールチェーンが必要です。最適な互換性のために、musl-1.2.4以降を使用してください。次に示すように、ソースからmusl
をビルドすることをお薦めします:
# Specify an installation directory for musl:
export MUSL_HOME=$PWD/musl-toolchain
# Download musl and zlib sources:
curl -O https://musl.libc.org/releases/musl-1.2.4.tar.gz
curl -O https://zlib.net/fossils/zlib-1.2.13.tar.gz
# Build musl from source
tar -xzvf musl-1.2.4.tar.gz
pushd musl-1.2.4
./configure --prefix=$MUSL_HOME --static
# The next operation may require privileged access to system resources, so use sudo
sudo make && make install
popd
# Install a symlink for use by native-image
ln -s $MUSL_HOME/bin/musl-gcc $MUSL_HOME/bin/x86_64-linux-musl-gcc
# Extend the system path and confirm that musl is available by printing its version
export PATH="$MUSL_HOME/bin:$PATH"
x86_64-linux-musl-gcc --version
# Build zlib with musl from source and install into the MUSL_HOME directory
tar -xzvf zlib-1.2.13.tar.gz
pushd zlib-1.2.13
CC=musl-gcc ./configure --prefix=$MUSL_HOME --static
make && make install
popd
要件を設定して、デモを作成します。
静的なネイティブ実行可能ファイルのビルド
- 次のソース・コードをEnvMap.javaという名前のファイルに保存します:
import java.util.Map; public class EnvMap { public static void main (String[] args) { var filter = args.length > 0 ? args[0] : ""; Map<String, String> env = System.getenv(); for (String envName : env.keySet()) { if(envName.contains(filter)) { System.out.format("%s=%s%n", envName, env.get(envName)); } } } }
このアプリケーションは、環境変数を繰り返し処理し、コマンドライン引数として渡される文字の
String
を含むものを出力します。 - アプリケーションをコンパイルします:
javac EnvMap.java
- 次のコマンドを実行して、静的なネイティブ実行可能ファイルをビルドします:
native-image --static --libc=musl EnvMap
これにより、静的にリンクされたシステムライブラリを持つネイティブ実行可能ファイルが生成されます。
./envmap
を使用して実行します。ldd
コマンドを使用して、アプリケーションが完全に静的にリンクされていることを確認できます:ldd EnvMap
出力は"not a dynamic executable"となります。
ほぼ静的なネイティブ実行可能ファイルのビルド
GraalVMネイティブ・イメージを使用すると、libc
以外のすべてを静的にリンクするほぼ静的なネイティブ実行可能ファイルをビルドできます。libc
以外のすべてのライブラリを静的にリンクすることで、アプリケーションがLinux libc
ベースのディストリビューションで実行するために必要なライブラリがすべて存在することが保証されます。
ほぼ静的なネイティブ実行可能ファイルをビルドするには、このコマンドを使用します:
native-image --static-nolibc [other arguments] <Class>
前述のEnvMap
デモ用のほぼ静的なネイティブ実行可能ファイルをビルドするには、次を実行します:
native-image --static-nolibc EnvMap
これにより、libc
以外のすべての関連ライブラリ(JDK共有静的ライブラリを含む)に静的にリンクするネイティブ実行可能ファイルが生成されます。これにはzlib
が含まれます。また、ユーザーのコードによっては、libstdc+
およびlibgcc
をリンクできます。アプリケーションが依存する動的ライブラリを確認する方法の1つは、ネイティブ実行可能ファイルでldd
を実行することです(ldd envmap
など)。
よくある質問
静的なネイティブ実行可能ファイルまたはほぼ静的なネイティブ実行可能ファイルをデプロイするために推奨されるベース・コンテナ・イメージは何ですか。
完全に静的なネイティブ実行可能ファイルでは、ベース・コンテナ・イメージを選択する際の柔軟性が最も高くなります。これは、scratch
イメージでも実行できます。ほぼ静的なネイティブ実行可能ファイルでは、libc
(特にglibc
)が提供されるコンテナ・イメージが必要ですが、それ以外の要件はありません。どちらのケースでも、ベース・コンテナ・イメージの選択は、通常、ネイティブ実行可能ファイルの特定の要件によって異なります。
関連ドキュメント
- Tiny Java Containersのデモでは、シンプルなJavaアプリケーションとシンプルなWebサーバーをコンパイルして、様々な軽量ベース・イメージを使用して非常に小さなDockerコンテナ・イメージを生成する方法を示します。
- Spring Bootアプリケーションのほぼ静的な実行可能ファイルをビルドするためのGraalVMネイティブ・イメージ、Springおよびコンテナ化対話型ラボ。