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

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

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

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

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

ノート: 現在は、libcにリンクされている場合のみ機能します。

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

目次

前提条件および準備

次の前提条件を満たす必要があります:

  1. GraalVM JDKがインストール済であることを確認します。最も簡単に始めるには、SDKMAN!を使用します。その他のインストール・オプションについては、「ダウンロード」セクションを参照してください。

  2. 次に、muslツールチェーンをインストールし、zlibをコンパイルしてツールチェーンにインストールする必要があります。musl.ccからmuslツールチェーンをダウンロードします。(こちらをお薦めします)。ツールチェーンを任意のディレクトリに解凍します。このディレクトリは$TOOLCHAIN_DIRとして参照されます。

  3. 最新のzlibライブラリ・ソースをzlib.netからダウンロードして解凍します。(このドキュメントでは、zlib-1.2.11を使用します。)

  4. CCという名前の新しい環境変数を作成します:
     CC=$TOOLCHAIN_DIR/bin/gcc
    
  5. zlibディレクトリに移動し、次のコマンドを実行してzlibをコンパイルし、ツールチェーンにインストールします:
     ./configure --prefix=$TOOLCHAIN_DIR --static
     make
     make install
    

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

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

  1. 最初に、PATH$TOOLCHAIN_DIR/binという名前のディレクトリが存在することを確認します。確認するには、次のコマンドを実行します。
     x86_64-linux-musl-gcc
    

    次のような出力が表示されます。

     x86_64-linux-musl-gcc: fatal error: no input files
     compilation terminated.
    
  2. ファイルをコンパイルします:
     javac EnvMap.java
    
  3. 次のコマンドを実行して、静的なネイティブ実行可能ファイルをビルドします:
     native-image --static --libc=musl EnvMap
    

    これにより、静的にリンクされたシステムライブラリを持つネイティブ実行可能ファイルが生成されます。クラスまたはJARファイルの前に他の引数を渡すことができます。

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

GraalVMネイティブ・イメージを使用すると、libc以外のすべてを静的にリンクするほぼ静的なネイティブ実行可能ファイルをビルドできます。libc以外のすべてのライブラリを静的にリンクすることで、アプリケーションがLinux libcベースのディストリビューションで実行するために必要なライブラリがすべて存在することが保証されます。

ほぼ静的なネイティブ実行可能ファイルをビルドするには、このコマンドを使用します:

native-image -H:+StaticExecutableWithDynamicLibC [other arguments] <Class>

前述のEnvMapデモ用のほぼ静的なネイティブ実行可能ファイルをビルドするには、次を実行します:

native-image -H:+StaticExecutableWithDynamicLibC EnvMap

これにより、libc以外のすべての関連ライブラリ(JDK共有ライブラリを含む)に静的にリンクするネイティブ実行可能ファイルが生成されます。これにはzlibが含まれます。また、ユーザーのコードによっては、libstdc+およびlibgccをリンクできます。アプリケーションが依存する動的ライブラリを確認する方法の1つは、ネイティブ実行可能ファイルでlddを実行することです(ldd helloworldなど)。

よくある質問

完全に静的なネイティブ実行可能ファイルでは、ベース・コンテナ・イメージを選択する際の柔軟性が最も高くなります。これは、FROM scratchイメージを含めあらゆるイメージで実行できます。ほぼ静的なネイティブ実行可能ファイルでは、libcが提供されるコンテナ・イメージが必要ですが、それ以外の要件はありません。どちらのケースでも、ベース・コンテナ・イメージの選択は、通常、ネイティブ実行可能ファイルの特定の要件によって異なります。