Liberty InstantOnとは
2022年9月29日にLiberty InstantOnのベータ版が発表されました。これはフル機能のクラウドネイティブJavaランタイムを数百ミリ秒で起動する技術です。Libertyは、MicroProfileとJakartaのWebProfileをサポートしています。
コンテナ環境でServerless技術を利用する場合には,プロセスの起動時間が重要となります。リクエストがきてからコ¥ンテナ環境を起動するServerlessでは,コンテナのプロセスが起動する時間がレスポンスタイムに含まれるために,その短さはサービスの品質に直接影響します。
Javaのアプリケーションの起動時間を短縮する取り組みとしては,GraalVMの機能を使ってアプリケーションをネイティブの実行可能ファイルに変換する方法が試みられています。ただ,GraalVMのネイティブ化では,Javaの全ての機能を利用できるわけではないので,広く使われているJava EEまたはJakartaEEのアプリケーションを実行可能ファイルに変換することはできません。
Liberty InstantOnはこれとは異なる方法で起動時間を短縮する機能です。LinuxのCRIU(Checkpoint/Restore In Userspace)という技術を利用します。CRIUは,起動しているプロセスの情報をファイルに記録(Checkpoint)し,ファイルからプロセスを復帰(Restore)させるための一連の機能です。これを組み込んだOpenJ9 VMでOpen Liberty/WebSphere Libertyを起動し,アプリケーションの初期化が終わった時点でCheckpointを取得します。この状態のコンテナをCommitし,そこから再開することでごく短時間での起動を実現します。
Intel MacでInstantOnを試す
Open Liberty の開始 で紹介されているサンプル手順はRed Hat Enterprise Linux上での実行が前提となっていますが,同じ内容をmacOS Monterey(バージョン12.x)を搭載したIntel Macでも実行できましたので手順を公開します。現在のところInstantOnフィーチャーはMI aach64アーキテクチャーではサポートされていません。
InstantOnのサンプル環境はコンテナイメージとしてIBM Cloud Registryから公開されています。コンテナ実行環境としてはHomebrewで導入したpodmanを使用します。ページの記述にしたがって普通に実行しただけだとエラーで失敗しますので,最終的に成功した手順を載せておきます。
Podmanの準備
InstantOnではLinuxのカーネルのCAP_CHECKPOINT_RESTORE機能を使用するため,podmanをrootlessモードで起動していると正常に稼働しません。また,いちど仮想環境(podman machine)をrootlessモードで起動してしまっていると,rootfulモードで実行しても失敗してしまうので,専用の新しい仮想マシンを作成するようにします。(podman machine list
でリストして podman machine rm
で古い仮想マシンを削除できます)
podmanの仮想環境を作成し,rootfulモードで起動します。デフォルトで起動した仮想マシンに接続するように構成します。
$ 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のバージョンは以下の通りです。
$ podman version
Client: Podman Engine
Version: 4.3.0
API Version: 4.3.0
Go Version: go1.18.7
Built: Tue Oct 18 13:55:57 2022
OS/Arch: darwin/amd64
Server: Podman Engine
Version: 4.3.0
API Version: 4.3.0
Go Version: go1.18.7
Built: Fri Oct 21 04:16:35 2022
OS/Arch: linux/amd64
テストアプリケーションのcloneとビルド
テストに使用するアプリケーションをGitHubからcloneし,ビルドします。
$ 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のためのサンプル環境です(いろいろ試したい方は,このイメージの中をみて,独自のテストをしてみてください)。
FROM icr.io/appcafe/open-liberty:beta
コンテナイメージをビルドします。
$ podman build -t getting-started .
STEP 1/7: FROM icr.io/appcafe/open-liberty:beta
Trying to pull icr.io/appcafe/open-liberty:beta...
Getting image source signatures
Copying blob sha256:3d49a5e804a4e5da1ee053e0dd6fda7c4011458bf43f658d45a7fe5d064d0a5f
(中略)
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が起動して,アプリケーションが実行できるようになります。
$ 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コマンドなどで正常に稼働していることを確認します。
$ curl http://localhost:9080/dev/system/properties
コンテナを起動したWindowでCtrl+Cを押すと,Libertyが稼働しているコンテナが停止します。
^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にafterAppStartを指定し,アプリケーションの初期化が終わった時点でOpenJ9のCheckpoint機能を呼び出します。
$ podman run --name getting-started-checkpoint-container --privileged --env WLP_CHECKPOINT=afterAppStart getting-started
Performing checkpoint --at=afterAppStart
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
このコマンドで、コンテナが立ち上がり、アプリケーションが起動されます。アプリケーションが立ち上がったところで、ランタイムはプロセスの”チェックポイント”を行います。プロセスの状態が保存されたところで、コンテナを終了します。 今回は—rmをつけずにコンテナを起動したので,実行後のコンテナ環境がそのまま残っています。この環境にcheckpointでプロセスの情報が記録されたファイルが保存されています
$ 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して`getting-started-instanton`という新しいイメージにしました。このイメージは、先ほどのアプリケーションが起動した状態のチェックポイント・プロセスを保持しています。このコンテナは、このプロセスの状態から起動します。
$ 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 7cd2ddfe28da 2 weeks ago 864 MB
Restoreによる起動
さていよいよInstantOnの実行です。このイメージを使ってLibertyを起動すると,爆速でサーバーが起動します。
% 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で止めてください。このイメージは,もちろん何度でも起動することができます。
--prividelged
をつけないでRestoreする場合
上記の例では、InstantOnの効果を簡単にご覧いただくためにコンテナを`--privileged`で起動しましたが、ベストプラクティスは必要最低限の権限でコンテナを使うことです。
InstantOnは、podmanの --security-opt
オプションを使って、下記の例のように必要な権限だけを指定して動かすことができます。必要な権限や、ファイルで権限を指定する方法についての詳細は、 クラウド・ネイティブ Java アプリケーション用の Liberty InstantOn 始動 を参照ください。
podman
コマンドを --security-opt
で起動した例
$ podman run \
--rm \
--cap-add=CHECKPOINT_RESTORE \
--cap-add=NET_ADMIN \
--cap-add=SYS_PTRACE \
--security-opt seccomp=unconfined \
--security-opt systempaths=unconfined \
--security-opt apparmor=unconfined \
-p 9080:9080 \
getting-started-instanton
[AUDIT ] CWWKZ0001I: Application guide-getting-started started in 0.101 seconds.
[AUDIT ] CWWKC0452I: The Liberty server process resumed operation from a checkpoint in 0.229 seconds.
[AUDIT ] CWWKF0012I: The server installed the following features: [cdi-3.0, checkpoint-1.0, concurrent-2.0, ... transportSecurity-1.0].
[AUDIT ] CWWKF0011I: The defaultServer server is ready to run a smarter planet. The defaultServer server started in 0.283 seconds
podman
コマンドを seccomp
を使って必要なシステムコールだけで動かした場合
$ podman run \
--rm \
--cap-add=CHECKPOINT_RESTORE \
--cap-add=NET_ADMIN \
--cap-add=SYS_PTRACE \
--security-opt seccomp=/Users/[email protected]/InstantOn/guide-getting-started/finish/criuRequiredSysCalls.json \
-v /proc/sys/kernel/ns_last_pid:/proc/sys/kernel/ns_last_pid \
-p 9080:9080 \
getting-started-instanton
[AUDIT ] CWWKZ0001I: Application guide-getting-started started in 0.101 seconds.
[AUDIT ] CWWKC0452I: The Liberty server process resumed operation from a checkpoint in 0.220 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.262 seconds.
比較的新しいOSのバージョンでは、podman
コマンドは ns_last_pid
をマウントする必要や seccomp
パラメーターを使う必要がなくなります。
podman run \
--rm \
--cap-add=CHECKPOINT_RESTORE \
--cap-add=NET_ADMIN \
--cap-add=SYS_PTRACE \
-p 9080:9080 \
getting-started-instanton
上記の CHECKPOINT_RESTORE command は macOS Monterey 12.6 のご覧のバージョンで動きました。
$ sw_vers
ProductName: macOS
ProductVersion: 12.6
BuildVersion: 21G115
あとしまつとしては,podman machine stop rootfulで仮想環境を停止し,podman system connection default podman-machine-defaultなどでデフォルトの接続を元に戻しておきましょう。