Intel MacでOpen LibertyのInstantOnを試す

# Liberty InstantOnとは

2022年9月29日に[Liberty InstantOn](https://openliberty.io/blog/2022/09/29/instant-on-beta.html)が発表されました。これはフル機能のJava EEアプリケーションサーバーを数百ミリ秒で起動する技術です。

コンテナ環境でServerless技術を利用する場合には，プロセスの起動時間が重要となります。リクエストがきてからコンテナ環境を起動するServerlessでは，コンテナのプロセスが起動する時間がレスポンスタイムに含まれるために，その短さはサービスの品質に直接影響します。

Javaのアプリケーションの起動時間を短縮する取り組みとしては，[GraalVM](https://www.oracle.com/jp/java/graalvm/what-is-graalvm/)の機能を使ってアプリケーションをネイティブの実行可能ファイルに変換する方法が試みられています。ただ，GraalVMのネイティブ化では，Javaの全ての機能を利用できるわけではないので，広く使われているJava EEのアプリケーションを実行可能ファイルに変換することはできません。

Liberty InstantOnはこれとは異なる方法で起動時間を短縮する機能です。Linuxの[CRIU（Checkpoint/Restore In Userspace）](https://criu.org/Main_Page)という技術を利用します。CRIUは，起動しているプロセスの情報をファイルに記録（Checkpoint）し，ファイルからプロセスを復帰（Restore）させるための一連の機能です。これを組み込んだ[OpenJ9](https://www.eclipse.org/openj9/) VMでOpen Liberty/WebSphere Libertyを起動し，アプリケーションの初期化が終わった時点でCheckpointを取得します。この状態のコンテナをCommitし，そこから再開することでごく短時間での起動を実現します。

**追記：** 2023年6月に公開されたバージョン23.0.0.6から，InstantOnが正式にサポートされました。

# Intel MacでInstantOnを試す


[InstantOnの発表ページ](https://openliberty.io/blog/2022/09/29/instant-on-beta.html)で紹介されているサンプル手順はRed Hat Enterprise Linux上での実行が前提となっていますが，同じ内容をmacOS Monterey（バージョン12.x）を搭載したIntel Macでも実行できましたので手順を公開します。

InstantOnのサンプル環境はコンテナイメージとしてIBM Cloud Registryから公開されています。コンテナ実行環境としては[Homebrew](https://brew.sh/index_ja)で導入したpodmanを使用します。ページの記述にしたがって普通に実行しただけだとエラーで失敗しますので，最終的に成功した手順を載せておきます。

## Podmanの準備

InstantOnではLinuxのカーネルのCAP_CHECKPOINT_RESTORE機能を使用するため，podmanをrootlessモードで起動していると正常に稼働しません。また，いちど仮想環境（podman machine）をrootlessモードで起動してしまっていると，rootfulモードで実行しても失敗してしまうので，専用の新しい仮想マシンを作成するようにします。

podmanの仮想環境を作成し，rootfulモードで起動します。デフォルトで起動した仮想マシンに接続するように構成します。

```terminal
$ podman machine init rootful
Extracting compressed file
Image resized.
Machine init complete
To start your machine run:

	podman machine start rootful

$ podman machine set --rootful rootful
$ podman machine start rootful
Starting machine "rootful"
Waiting for VM ...
Mounting volume... /Users/takakiyo:/Users/takakiyo
API forwarding listening on: /var/run/docker.sock
Docker API clients default to this address. You do not need to set DOCKER_HOST.

Machine "rootful" started successfully
$ podman system connection default rootful-root
```

使用したpodmanのバージョンは以下の通りです。

```terminal
$ podman version
Client:       Podman Engine
Version:      4.2.1
API Version:  4.2.1
Go Version:   go1.18.6
Built:        Wed Sep  7 04:16:02 2022
OS/Arch:      darwin/amd64

Server:       Podman Engine
Version:      4.2.1
API Version:  4.2.1
Go Version:   go1.18.5
Built:        Thu Sep  8 04:58:19 2022
OS/Arch:      linux/amd64
```

## テストアプリケーションのcloneとビルド

テストに使用するアプリケーションをGitHubからcloneし，ビルドします

```terminal
$ git clone https://github.com/openliberty/guide-getting-started.git
Cloning into 'guide-getting-started'...
remote: Enumerating objects: 2747, done.
remote: Counting objects: 100% (537/537), done.
remote: Compressing objects: 100% (275/275), done.
remote: Total 2747 (delta 172), reused 447 (delta 116), pack-reused 2210
Receiving objects: 100% (2747/2747), 859.87 KiB | 2.74 MiB/s, done.
Resolving deltas: 100% (1097/1097), done.
$ cd guide-getting-started/finish
$ mvn package
[INFO] Scanning for projects...
（中略）
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.240 s
[INFO] Finished at: 2022-10-16T21:09:55+09:00
[INFO] ------------------------------------------------------------------------
```

## コンテナイメージの作成

同ディレクトリにあるDockerfileの1行目を以下のように書き換えます。beta-instantonのタグを使用します。このタグがつけられたイメージが，InstantOnのためのサンプル環境です（いろいろ試したい方は，このイメージの中をみて，独自のテストをしてみてください）。

```Dockerfile
FROM icr.io/appcafe/open-liberty:beta-instanton
```

**追記：** 現在は正式版でInstantOnがサポートされていますので，以下のように[通常のイメージ](https://openliberty.io/docs/latest/container-images.html)で利用できます。

```Dockerfile
FROM icr.io/appcafe/open-liberty:kernel-slim-java21-openj9-ubi-minimal
```

コンテナイメージをビルドします。

```terminal
$ podman build -t getting-started .
STEP 1/7: FROM icr.io/appcafe/open-liberty:beta-instanton
Trying to pull icr.io/appcafe/open-liberty:beta-instanton...
Getting image source signatures
Copying blob sha256:3d49a5e804a4e5da1ee053e0dd6fda7c4011458bf43f658d45a7fe5d064d0a5f
Copying blob sha256:1b3417e31a5e0e64f861e121d4efed3152e75aaa85026cd784cd0070e063daa3
Copying blob sha256:809fe483e88523e7021d76b001a552856f216430023bdc0aeff8fce8df385535
Copying blob sha256:8fb7a38f830b2e199a440bcbbd5ec67371c73840f4931710a6c795af1c0b5104
Copying blob sha256:e640ac07d6e44b85e55fc2e927a285bb2fa2287eddffc02e67ba3a97c875cfa1
Copying blob sha256:3f06b1335a7d60be173e27acdfaf7ca4e33c00f55ffa57e56049d14920f48006
Copying blob sha256:9637e219b58194d63f6a4e1194b1da73d2a709ba41e9fe329c7d523a85b6f654
Copying blob sha256:640cbdd2130ddfd4ceb325aa1a171820b71f8c640dc3ccc8637fc79478e5dc1f
Copying blob sha256:26d176bdf7b3c68a0f0d599e98954b7ccf06a40ded5fc4fa4c905d5744c689f2
Copying blob sha256:581a9ce9fcb098057728da18b0be78b678f1a6e35fcb7e0b04bff822cbde4047
Copying blob sha256:fb00fa38b2cd77cb8d64dd42958bfa28b8c67009b3dc9e16bdf15474d006e79e
Copying blob sha256:b23a0409c41c442e8f1bf00d2649fa127f63f92e131d5ccf39a1d0ae82ec3cd6
Copying blob sha256:308dc9b7079f15af4e4f23cab1fd5082fadf4052d237959e3bdd4c8fa73a6346
Copying config sha256:7cd2ddfe28da75e669998130b99bb7fb48e255ba9d9f776804b78d630f2e0469
Writing manifest to image destination
Storing signatures
STEP 2/7: ARG VERSION=1.0
--> 3816b6a9a20
STEP 3/7: ARG REVISION=SNAPSHOT
--> b82ea29da9b
STEP 4/7: LABEL   org.opencontainers.image.authors="Your Name"   org.opencontainers.image.vendor="IBM"   org.opencontainers.image.url="local"   org.opencontainers.image.source="https://github.com/OpenLiberty/guide-getting-started"   org.opencontainers.image.version="$VERSION"   org.opencontainers.image.revision="$REVISION"   vendor="Open Liberty"   name="system"   version="$VERSION-$REVISION"   summary="The system microservice from the Getting Started guide"   description="This image contains the system microservice running with the Open Liberty runtime."
--> 1781202e3e0
STEP 5/7: COPY --chown=1001:0 src/main/liberty/config/ /config/
--> 3d515ebf80e
STEP 6/7: COPY --chown=1001:0 target/*.war /config/apps/
--> b56dbcc57b8
STEP 7/7: RUN configure.sh
COMMIT getting-started
--> 612b43d3e78
Successfully tagged localhost/getting-started:latest
612b43d3e785166c3d9c05c315944921333748dba432a5b53640ea240f77092c
```

このまま普通に実行すると，単にOpen Libertyが起動して，アプリケーションが実行できるようになります。

```terminal
$ podman run -it --name getting-started --rm -p 9080:9080 getting-started

WARNING: Unknown module: jdk.management.agent specified to --add-exports
WARNING: Unknown module: jdk.attach specified to --add-exports
Launching defaultServer (Open Liberty 22.0.0.11-beta/wlp-1.0.69.cl221020220912-1100) on Eclipse OpenJ9 VM, version 17.0.5-ea+2 (en_US)
CWWKE0953W: This version of Open Liberty is an unsupported early release version.
[AUDIT   ] CWWKE0001I: The server defaultServer has been launched.
[AUDIT   ] CWWKG0093A: Processing configuration drop-ins resource: /opt/ol/wlp/usr/servers/defaultServer/configDropins/defaults/checkpoint.xml
[AUDIT   ] CWWKG0093A: Processing configuration drop-ins resource: /opt/ol/wlp/usr/servers/defaultServer/configDropins/defaults/keystore.xml
[AUDIT   ] CWWKG0093A: Processing configuration drop-ins resource: /opt/ol/wlp/usr/servers/defaultServer/configDropins/defaults/open-default-port.xml
[AUDIT   ] CWWKZ0058I: Monitoring dropins for applications.
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://de537b960bc9:9080/ibm/api/
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://de537b960bc9:9080/health/
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://de537b960bc9:9080/metrics/
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://de537b960bc9:9080/dev/
[AUDIT   ] CWWKZ0001I: Application guide-getting-started started in 1.978 seconds.
[AUDIT   ] CWWKF0012I: The server installed the following features: [cdi-3.0, checkpoint-1.0, concurrent-2.0, distributedMap-1.0, jndi-1.0, json-1.0, jsonb-2.0, jsonp-2.0, monitor-1.0, mpConfig-3.0, mpHealth-4.0, mpMetrics-4.0, restfulWS-3.0, restfulWSClient-3.0, servlet-5.0, ssl-1.0, transportSecurity-1.0].
[AUDIT   ] CWWKF0011I: The defaultServer server is ready to run a smarter planet. The defaultServer server started in 6.851 seconds.
```

この状態でも，手元の環境では6〜7秒で起動します。別Windowのコマンドプロンプトからcurlコマンドなどで正常に稼働していることを確認します。

```terminal
$ curl http://localhost:9080/dev/system/properties   
```

コンテナを起動したWindowでCtrl+Cを押すと，Libertyが稼働しているコンテナが停止します。

```terminal
^C[AUDIT   ] CWWKE0085I: The server defaultServer is stopping because the JVM is exiting.
[AUDIT   ] CWWKE1100I: Waiting for up to 30 seconds for the server to quiesce.
[AUDIT   ] CWWKT0017I: Web application removed (default_host): https://de537b960bc9:9443/dev/
[AUDIT   ] CWWKT0017I: Web application removed (default_host): https://de537b960bc9:9443/health/
[AUDIT   ] CWWKT0017I: Web application removed (default_host): https://de537b960bc9:9443/metrics/
[AUDIT   ] CWWKT0017I: Web application removed (default_host): https://de537b960bc9:9443/ibm/api/
[AUDIT   ] CWWKZ0009I: The application guide-getting-started has stopped successfully.
[AUDIT   ] CWWKE0036I: The server defaultServer stopped after 2 minutes, 32.806 seconds.
```

## Checkpointの取得

それでは，アプリケーションの起動準備ができたタイミングでCheckpointを取得してみましょう。いろいろと権限が必要なため--privilegedオプションをつけて実行します。また環境変数WLP_CHECKPOINTにapplicationsを指定し，アプリケーションの初期化が終わった時点でOpenJ9のCheckpoint機能を呼び出します。

```terminal
$ podman run --name getting-started-checkpoint-container --privileged --env WLP_CHECKPOINT=applications getting-started
Performing checkpoint --at=applications

WARNING: Unknown module: jdk.management.agent specified to --add-exports
WARNING: Unknown module: jdk.attach specified to --add-exports
Launching defaultServer (Open Liberty 22.0.0.11-beta/wlp-1.0.69.cl221020220912-1100) on Eclipse OpenJ9 VM, version 17.0.5-ea+2 (en_US)
CWWKE0953W: This version of Open Liberty is an unsupported early release version.
[AUDIT   ] CWWKE0001I: The server defaultServer has been launched.
[AUDIT   ] CWWKG0093A: Processing configuration drop-ins resource: /opt/ol/wlp/usr/servers/defaultServer/configDropins/defaults/checkpoint.xml
[AUDIT   ] CWWKG0093A: Processing configuration drop-ins resource: /opt/ol/wlp/usr/servers/defaultServer/configDropins/defaults/keystore.xml
[AUDIT   ] CWWKG0093A: Processing configuration drop-ins resource: /opt/ol/wlp/usr/servers/defaultServer/configDropins/defaults/open-default-port.xml
[AUDIT   ] CWWKZ0058I: Monitoring dropins for applications.
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://940fd476eccc:9080/ibm/api/
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://940fd476eccc:9080/health/
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://940fd476eccc:9080/metrics/
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://940fd476eccc:9080/dev/
[AUDIT   ] CWWKZ0001I: Application guide-getting-started started in 1.340 seconds.
[AUDIT   ] CWWKC0451I: A server checkpoint was requested. When the checkpoint completes, the server stops.
/opt/ol/wlp/bin/server: line 946:   130 Killed                  "${JAVA_CMD}" "$@" >> "${CHECKPOINT_CONSOLE_LOG}" 2>&1 < /dev/null
```

**追記：** 環境変数WLP_CHECKPOINTに設定する値は，正式版ではafterAppStartに変わりました。以下のようにコマンドを実行してください。

```terminal
$ podman run --name getting-started-checkpoint-container --privileged --env WLP_CHECKPOINT=afterAppStart getting-started
```


今回は--rmをつけずにコンテナを起動したので，実行後のコンテナ環境がそのまま残っています。この環境にcheckpointでプロセスの情報が記録されたファイルが保存されています。

```terminal
$ podman ps -a
CONTAINER ID  IMAGE                             COMMAND               CREATED        STATUS                    PORTS       NAMES
940fd476eccc  localhost/getting-started:latest  /opt/ol/wlp/bin/s...  4 minutes ago  Exited (0) 4 minutes ago              getting-started-checkpoint-container
```

これをcommitして新しいイメージとして登録します。

```terminal
$ podman commit getting-started-checkpoint-container getting-started-instanton
a856d767b8c31718dfbc6e60f742675448086fb4421490b5bfde6d3392d2f879
$ podman images
REPOSITORY                           TAG             IMAGE ID      CREATED         SIZE
localhost/getting-started-instanton  latest          a856d767b8c3  7 seconds ago   990 MB
localhost/getting-started            latest          1049db82664e  31 minutes ago  890 MB
icr.io/appcafe/open-liberty          beta-instanton  7cd2ddfe28da  2 weeks ago     864 MB
```

## Restoreによる起動

さていよいよInstantOnの実行です。このイメージを使ってLibertyを起動すると，爆速でサーバーが起動します。

```terminal
% podman run -it --rm --privileged -p 9080:9080 getting-started-instanton

[AUDIT   ] CWWKZ0001I: Application guide-getting-started started in 0.066 seconds.
[AUDIT   ] CWWKC0452I: The Liberty server process resumed operation from a checkpoint in 0.131 seconds.
[AUDIT   ] CWWKF0012I: The server installed the following features: [cdi-3.0, checkpoint-1.0, concurrent-2.0, distributedMap-1.0, jndi-1.0, json-1.0, jsonb-2.0, jsonp-2.0, monitor-1.0, mpConfig-3.0, mpHealth-4.0, mpMetrics-4.0, restfulWS-3.0, restfulWSClient-3.0, servlet-5.0, ssl-1.0, transportSecurity-1.0].
[AUDIT   ] CWWKF0011I: The defaultServer server is ready to run a smarter planet. The defaultServer server started in 0.167 seconds.
```

手元の環境では0.1〜0.2秒でLibertyが起動しました。curlコマンドやブラウザなどでアクセスすると，普通にLibertyが起動していることがわかると思います。LibertyはCtrl+Cで止めてください。このイメージは，もちろん何度でも起動することができます。

あとしまつとしては，`podman machine stop rootful`で仮想環境を停止し，`podman system connection default podman-machine-default`などでデフォルトの接続を元に戻しておきましょう。