静的またはほぼ静的にリンクされたネイティブ実行可能ファイルのビルド

GraalVMネイティブ・イメージでは、デフォルトで動的にリンクされたバイナリがビルドされます。ビルド時に、まずアプリケーション・クラスおよびインタフェースがロードされ、次に動的リンクの処理中にフックされます。

ただし、必要に応じて、静的またはほぼ静的にリンクされたネイティブ実行可能ファイルを作成できます。

静的なネイティブ実行可能ファイルとは、静的にリンクされたバイナリのことであり、追加のライブラリ依存性なしに使用できます。静的なネイティブ実行可能ファイは、スリムすなわちdistrolessのコンテナ(スクラッチ・コンテナ)に簡単に配布およびデプロイできます。静的なネイティブ実行可能ファイルを作成するには、静的にmusl-libc (軽量、高速かつ単純なlibc実装)をリンクします。

ほぼ静的なネイティブ実行可能ファイルとは、標準のCライブラリlibcを除く、ネイティブ実行可能ファイルが依存するすべての共有ライブラリ(zlib、JDK共有静的ライブラリ)をリンクするバイナリのことです。これは、すべてを静的にリンクするための代替オプションです。また、ユーザーのコードによっては、libstdc+およびlibgccをリンクできます。このアプローチは、distrolessコンテナ・イメージへのデプロイメントに役立ちます。

このガイドでは、完全に動的、完全に静的、ほぼ静的(libcを除く)などのネイティブ・イメージ・リンク・オプションを利用して、デプロイメント・シナリオに最適な実行可能ファイルを生成する方法を示します。

前提条件および準備

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

要件を設定して、デモを作成します。

静的なネイティブ実行可能ファイルのビルド

  1. 次のソース・コードを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を含むものを出力します。

  2. アプリケーションをコンパイルします:
     javac EnvMap.java
    
  3. 次のコマンドを実行して、静的なネイティブ実行可能ファイルをビルドします:
     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)が提供されるコンテナ・イメージが必要ですが、それ以外の要件はありません。どちらのケースでも、ベース・コンテナ・イメージの選択は、通常、ネイティブ実行可能ファイルの特定の要件によって異なります。