【GraalVM(AOT)、ハンズオン】①Javaアプリ構築 → ②Nativeイメージ化 → ③Dockerコンテナ化により、超高速起動を実現。

環境は、OCI（オラクルクラウド）上で構築した、Ubuntu22.04。

このイメージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2634867/2ee9334f-aa37-5734-0057-4c5230e9b548.png)


＜事前準備＞ GraalVM及び必要なコンポーネントのインストール
まず、GraalVMをインストールしてください。
GraalVMには、Community EditionとOracleが提供するGraal VM　Enterprise Edition(EE)があるが、
ここでは、EEを使います。
DLサイト　→　　https://www.oracle.com/jp/downloads/graalvm-downloads.html

インストール方法は、こちらを参照してください。
GraalVM　EEの他に、Native Image及び、Native Image依存ライブラリーのインストールも必要です。
https://qiita.com/Oracle_Java_Nishikawa/items/846b0b641a158cd25a1f


手順１
GraalVMが適切にインストールしてあることを確認してください。

```
ubuntu@instance-4apr2023:/opt/code$ java -version
java version "17.0.7" 2023-04-18 LTS
Java(TM) SE Runtime Environment GraalVM EE 22.3.2 (build 17.0.7+8-LTS-jvmci-22.3-b15)
Java HotSpot(TM) 64-Bit Server VM GraalVM EE 22.3.2 (build 17.0.7+8-LTS-jvmci-22.3-b15, mixed mode, sharing)
ubuntu@instance-4apr2023:/opt/code$ 
```

手順２
下記は、Java１７で動作する、スロットマシンゲームです。このコードをコピペしてください。
今回は、これをNativeイメージ化した上で、Dockerコンテナ化します。
コインがなくなるまで、回し続けるスロットゲームとなります。
```
ubuntu@instance-4apr2023:/opt/code$ cat SlotMachineX.java
```

```SlotMachineX.java
import java.util.Random;
import java.util.Scanner;

public class SlotMachineX {
    private String[] symbols = {"Cherry", "Lemon", "Orange", "Plum", "Bell", "Bar", "7"};
    private Random random = new Random();

    public String[] spinReels() {
        String[] reels = new String[3];
        for (int i = 0; i < 3; i++) {
            int index = random.nextInt(symbols.length);
            reels[i] = symbols[index];
        }
        return reels;
    }

    public int calculateWinnings(String[] reels, int betAmount) {
        // Implement the logic to calculate the winnings based on the symbols that appear on the reels
        // and the bet amount.
        // Return the amount of money the player has won or lost.
        return 0;
    }

    private static class Player {
        private int balance;

        public Player(int startingBalance) {
            balance = startingBalance;
        }

        public void placeBet(int amount) {
            balance -= amount;
        }

        public void addWinnings(int amount) {
            balance += amount;
        }

        public int getBalance() {
            return balance;
        }
    }

    public static void main(String[] args) {
        int startingBalance = 100;
        Player player = new Player(startingBalance);
        SlotMachineX slotMachine = new SlotMachineX();

        while (player.getBalance() > 0) {
            System.out.println("Current balance: $" + player.getBalance());
            player.placeBet(1); // Automatically place a bet of $1
            String[] reels = slotMachine.spinReels();
            System.out.println("Reels: " + String.join(", ", reels));
            int winnings = slotMachine.calculateWinnings(reels, 1);
            player.addWinnings(winnings);
            if (winnings > 0) {
                System.out.println("Congratulations, you won $" + winnings + "!");
            } else {
                System.out.println("Sorry, you lost $1.");
            }
        }

        System.out.println("Game over. You have run out of money.");
    }
}
```


手順３.
上記のJavaコードは、下記のDirectoryに、"SlotMachineX.java"として設置した上で、
javacコマンドで、クラスファイルを作成します。

```
ubuntu@instance-4apr2023:/opt/code %ls
SlotMachineX$Player.class	SlotMachineX.class		SlotMachineX.java
ubuntu@instance-4apr2023:/opt/code % pwd
/opt/code
```

手順4. timeコマンドを使い、時間を測ると、処理終了まで、0.15秒かかっております。
（この時点では、ただのJavaアプリです。Nativeイメージ化しておりません。）

```
ubuntu@instance-4apr2023:/opt/code % time java SlotMachineX
~~~~~~~~~
Game over. You have run out of money.
java SlotMachineX  0.15s user 0.10s system 54% cpu 0.454 total
```


手順5. Nativeイメージを作成します。
このコマンドだけで、Nativeイメージを作成します。
"native-image --no-server --no-fallback SlotMachineX　"

```
ubuntu@instance-4apr2023:/opt/code$ ls
SlotMachineX$Player.class	SlotMachineX.class		SlotMachineX.java
ubuntu@instance-4apr2023:/opt/code$ native-image --no-server --no-fallback SlotMachineX　　　# Nativeイメージを作成する。GraalVMのインストールが必須。
'SlotMachineX$Player.class'   SlotMachineX.class   SlotMachineX.java   slotmachinex   slotmachinex.build_artifacts.txt
```
手順6.  Nativeイメージ、 "slotmachinex"が作成されているのを確認した上で、起動します。

```
ubuntu@instance-4apr2023:/opt/code$ time ./slotmachinex
~~~~~~~~~~~~~
Game over. You have run out of money.

real    0m0.002s
user    0m0.002s
sys     0m0.000s
ubuntu@instance-4apr2023:/opt/code$ 
```
0.002秒で処理が終わっております。７倍以上、速い！

手順6. Native ImageをDockerコンテナ化します。

分かりやすくする為に、下記の２つのファイルを、nativeimgと言うフォルダに移動します。
"slotmachinex", "slotmachinex.build_artifacts.txt"
また、dockerfileを作成します。
dockerfileの内容は、下記の通りです。

```
ubuntu@instance-4apr2023:/opt/code/nativeimg$ ls
dockerfile  slotmachinex  slotmachinex.build_artifacts.txt  temp

ubuntu@instance-4apr2023:/opt/code/nativeimg$ cat dockerfile
FROM ubuntu:latest
WORKDIR /app
COPY slotmachinex .
COPY slotmachinex.build_artifacts.txt .
RUN apt-get update && apt-get install -y time && \
    chmod +x slotmachinex
CMD ["time", "./slotmachinex"]
```

手順7. Dockerbuildをします。

```
ubuntu@instance-4apr2023:/opt/code/nativeimg$ sudo docker image build -t slotmachinex-alpine3:latest . 
```

手順7. Docker Imageが作成しているのを確認。
```
ubuntu@instance-4apr2023:/opt/code/nativeimg$ docker images
REPOSITORY             TAG       IMAGE ID       CREATED          SIZE
slotmachinex-alpine3   latest    e1e0b4bea3b8   23 minutes ago   134MB
~~~~~
以下省略。
```

手順8. Docker　をRunをします。

```
ubuntu@instance-4apr2023:/opt/code/nativeimg$ docker run -it slotmachinex-alpine3:latest
Current balance: $100
Reels: 7, Plum, 7
~~~~~~~~~
Current balance: $2
Reels: Bar, Plum, 7
Sorry, you lost $1.
Current balance: $1
Reels: Plum, Lemon, Lemon
Sorry, you lost $1.
Game over. You have run out of money.
0.00user 0.00system 0:00.00elapsed 60%CPU (0avgtext+0avgdata 6068maxresident)k
0inputs+0outputs (0major+331minor)pagefaults 0swaps
ubuntu@instance-4apr2023:/opt/code/nativeimg$
ubuntu@instance-4apr2023:/opt/code/nativeimg$
```

Nativeイメージ化したSlotmachineゲームは、超高速であることが確認できます。