GraalVMとGraalPyでPythonのネイティブイメージを作成してみた

# この記事の目的
GraalVMとGraalPyを利用して、Pythonのネイティブイメージを作成し、クライアント環境にデプロイする手順をご紹介します。
# サーバ側の手順
## GraalVM構築
参考
https://www.graalvm.org/latest/docs/getting-started/linux/
### 環境確認
```shell
$ uname -m
aarch64
```
上記のため、下記をダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387966/d88c370a-f4ea-1ce8-0653-5bfe1ab01b92.png)
ダウンロードしたファイルを解凍します。
graalvm-jdk-21_linux-aarch64_bin.tar.gz
```shell
$ tar -xzf graalvm-jdk-21_linux-aarch64_bin.tar.gz
$ ls
graalvm-jdk-21_linux-aarch64_bin.tar.gz graalvm-jdk-21.0.4+8.1
```
下記のように```.bash_profile```を編集し、GraalVMの環境変数を追加します。
```shell
$ vi .bash_profile 
```
```shell
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

export JAVA_HOME=/home/opc/graalvm-jdk-21.0.4+8.1
export PATH=$JAVA_HOME/bin:$PATH
```
```shell
$ source .bash_profile 
```
インストールを確認します。
```shell
$ java -version
java version "21.0.4" 2024-07-16 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 21.0.4+8.1 (build 21.0.4+8-LTS-jvmci-23.1-b41)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 21.0.4+8.1 (build 21.0.4+8-LTS-jvmci-23.1-b41, mixed mode, sharing)
```
Native Imageの前提依存関係をインストールします。Linuxバージョンによって違うコマンドを実行する必要となるが、今回にはOracle Linuxです。
参考
https://www.graalvm.org/latest/reference-manual/native-image/
```shell
$ sudo yum install gcc glibc-devel zlib-devel
```
## GraalPy構築
GraalPy 24.1.0をダウンロードします。
参考
https://github.com/oracle/graalpython/releases
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387966/76537ee0-8f9c-a1cf-1475-4ef0e0292bc4.png)
ダウンロードしたファイルを解凍します。
graalpy-24.1.0-linux-aarch64.tar.gz
```shell
$ tar -xzf graalpy-24.1.0-linux-aarch64.tar.gz
$ ls
graalpy-24.1.0-linux-aarch64.tar.gz graalpy-24.1.0-linux-aarch64
```
下記のように```.bash_profile```を編集し、GraalPyの環境変数を追加します。
```shell
$ vi .bash_profile 
```
```shell
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

export JAVA_HOME=/home/opc/graalvm-jdk-21.0.4+8.1
export PATH=$JAVA_HOME/bin:$PATH
export PATH=/home/opc/graalpy-24.1.0-linux-aarch64/bin:$PATH
```
```shell
$ source .bash_profile 
```
仮想環境を構築します。
```shell
$ mkdir sample_project
$ cd sample_project
[sample_project]$ graalpy -m venv sample_venv
[sample_project]$ 
[sample_project]$ ls
sample_venv
[sample_project]$ cd sample_venv/
[sample_venv]$ ls
bin  include  lib  pyvenv.cfg
```
Pythonスクリプトを実行します。
```shell
[sample_project]$ cd sample_venv/
[sample_venv]$ mkdir src
[sample_venv]$ cd src
[src]$ vi helloworld.py
[src]$ cat helloworld.py 
print("Hello Native World!")

[src]$ chmod 700 helloworld.py
[src]$ ll
total 4
-rwx------. 1 opc opc 30 Oct 22 03:20 helloworld.py
[src]$ graalpy helloworld.py 
Hello Native World!
```
## ネイティブイメージ作成
環境構築を完成さ、Pythonスクリプトも準備できました。これからクライアントで実行するネイティブイメージを作成します。
参考：
https://www.graalvm.org/latest/reference-manual/python/standalone-applications/
```shell
[sample_project]$ pwd
/home/opc/sample_project
[sample_project]$ graalpy -m standalone native --module ./sample_venv/src/helloworld.py --output ./sample_venv/src/HelloWorld_binary --venv sample_venv
Produced artifacts:
 /home/opc/sample_project/sample_venv/src/HelloWorld_binary (executable)
========================================================================================================================
Finished generating 'HelloWorld_binary' in 40m 30s.
[sample_project]$ ls /home/opc/sample_project/sample_venv/src/
HelloWorld_binary  helloworld.py 
```
クライアントにネイティブイメージを移動します。
```shell
[sample_project]$ cd ~
[~]$ scp -i ssh-key-2024-09-25.key /home/opc/sample_project/sample_venv/src/HelloWorld_binary opc@168.138.204.35:/home/opc
HelloWorld_binary                  100%  347MB 124.0MB/s   00:02
[~]$ 
```
# クライアント側の手順
ネイティブイメージはバイナリ実行ファイルなので、クライント側の環境設定が必要ではなく、ネイティブイメージが直接実行できます。
```shell
[~]$ ls
HelloWorld_binary
[~]$ ./HelloWorld_binary 
Hello Native World!
[~]$ 
```
# まとめ
GraalVMがJAVAでネイティブイメージを作成した例が多いですが、今回Pythonを利用したネイティブイメージの作成方法を検証しました。PythonとGraalVMに興味がある方はご参考にしてください。