原文はこちら。
The original article was written by Zichun (Chris) Chong (Software Developer at StreamWorx.AI Inc).
https://blog.openj9.org/2021/03/20/introducing-the-eclipse-openj9-jitserver-helm-chart/
(訳注)原文ではAdoptOpenJDKという表現が使われていますが、これは現在のEclipse Adoptiumです。
What is JITServer Technology?
JITServerとはEclipse OpenJ9 JVMテクノロジーであり、これを使うとJVMがJITコンパイルの作業を別プロセス(JITServer)に委譲できます。このJITServerはリモートマシンで動作する可能性があります。JITコンパイルをオフロードするというアプローチは、起動時間や立ち上がり時間の短縮、ピーク時のメモリ使用量の削減、メモリ不足の可能性の低減、JIT コンパイラのバグによる偽装クラッシュに対する耐性の向上など、数多くの利点をもたらします。さらに、リソースのプロビジョニング作業が非常にシンプルになります。それは、ユーザーがコンパイル時に発生するCPUやメモリの予期せぬ急増を考慮する必要がなく、Javaアプリケーションのニーズにのみ集中できるためです。
Starting with OpenJ9 release 0.18.1以後、JITServerはテクノロジープレビューとしてx86-64 Linuxで利用できますが、OpenJ9 release 0.23では、 IBM PowerおよびIBM Zで稼働する64ビットLinuxもサポートするようになりました。
JITServer in Cloud Environments
クラウドコンピューティングは、さまざまな業界でコンピュータサービスの利用方法を大きく変えています。調査によると、90%の企業がパブリッククラウド、プライベートクラウド、ハイブリッドクラウドのいずれかの環境にサービスを移行している、または移行したという結果が出ています。さらに専門家は、商用ワークロードの60%がクラウド環境で実行されていると予測しています。
25 Must-Know Cloud Computing Statistics in 2020
https://hostingtribunal.com/blog/cloud-computing-statistics/
69% of enterprises will have multi-cloud/hybrid IT environments by 2019, but greater choice brings excessive complexity
https://451research.com/images/Marketing/press_releases/Pre_Re-Invent_2018_press_release_final_11_22.pdf
JITServerテクノロジーは、クラウド環境に適しています。JITServerの利点は、アプリケーションとJITコンパイラーがCPUとメモリの奪い合いを強いられるような、(クラウドによく見られる)リソースに制約のある環境でより明らかになります。さらにJITServerは、クライアントJVMのピーク時のメモリ使用量を効果的に削減し、より小さなコンテナの使用を可能にすることで、アプリケーションの密度を高め、結果として運用コストを削減します。JITServerは、コンテナ化してクラウドネイティブ環境(Kubernetes、OpenShiftなど)に簡単にデプロイできるため、高密度なクラウド環境でJavaアプリケーションを実行することがさらに容易になります。
Eclipse OpenJ9 JITServer Helm Chart
JITServerをクラウド環境で試してみたいとお考えの方には朗報です。OpenJ9のJITServer helm chartが簡単かつわかりやすい方法を提供します。これを使うと、3つの簡単なステップでJITServerテクノロジーをデプロイし、有効にできます。これ以後で、JITServer helm chartを導入するための要件とその方法について説明します。
Requirements
- JITServerとクライアントJVMの両方で同じJDKリリースバージョンであること
- クラスター管理および運用の基本的な知識があること
- クラスター
- OpenShift Container Platform 3.11以後もしくはRedHat OpenShift 4.1以後
(訳注:明らかに誤記なので原文から変更しています。Kubernetesのバージョンでは1.11以後が対象)
- OpenShift Container Platform 3.11以後もしくはRedHat OpenShift 4.1以後
- クラスターのRBAC
- Kubernetes PodSecurityPolicies (PSP):
spec.runAsUser: RunAsAny
もしくは - RedHat OpenShift SecurityContextConstraints(SCC):
anyuid
- Kubernetes PodSecurityPolicies (PSP):
- Helm CLI version 3.0以後
Steps to deploying JITServer Technology
1. helmリポジトリからJITServer chartを探す
- OpenJ9 helm repo を helm CLI に追加します。
$ helm repo add openj9 https://raw.githubusercontent.com/eclipse/openj9-utils/master/helm-chart/
"openj9" has been added to your repositories
- Verify helmリポジトリを追加しOpenJ9 JITServer chartが利用になっていることを確認します。
$ helm repo ls
NAME URL
openj9 https://raw.githubusercontent.com/eclipse/openj9-utils/master/helm-chart/
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "openj9" chart repository
Update Complete. ⎈ Happy Helming!⎈
$ helm search repo openj9-jitserver
NAME CHART VERSION APP VERSION DESCRIPTION
openj9/openj9-jitserver-chart 0.24.0 0.24.0 Eclipse OpenJ9 JITServer Helm Chart.
2. JITServer chartをクラスターにデプロイ
- helm chartをインストールします。
$ helm install jitserver-release openj9/openj9-jitserver-chart
NAME: jitserver-release
LAST DEPLOYED: Fri Jan 29 20:11:19 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
Welcome to OpenJ9 JITServer Helm Chart. The JITServer technology has been
deployed successfully and ready for connection.
- helm chartのデプロイをテストします。
$ helm test jitserver-release
Pod jitserver-release-openj9-jitserver-chart-test-connection pending
Pod jitserver-release-openj9-jitserver-chart-test-connection succeeded
NAME: jitserver-release
LAST DEPLOYED: Fri Jan 29 20:11:19 2021
NAMESPACE: helm-chart-test
STATUS: deployed
REVISION: 1
TEST SUITE: jitserver-release-openj9-jitserver-chart-test-connection
Last Started: Fri Jan 29 20:41:14 2021
Last Completed: Fri Jan 29 20:41:26 2021
Phase: Succeeded
NOTES:
Welcome to OpenJ9 JITServer Helm Chart, the application has been
deployed successfully.
3. Javaアプリケーションに対してJITServerテクノロジーを有効化
- Find JITServerのサービスエンドポイント(サービス名、ポート)を探します。
$ export SERVICE_IP=$(kubectl get service --namespace default jitserver-release-openj9-jitserver-chart -o jsonpath="{..metadata.name}")
$ export SERVICE_PORT=$(kubectl get service --namespace default jitserver-release-openj9-jitserver-chart -o jsonpath="{.spec.ports[0].port}")
$ echo JITServer endpoint is $SERVICE_IP:$SERVICE_PORT
JITServer endpoint is jitserver-release-openj9-jitserver-chart:38400
- Javaアプリケーションコンテナーの環境変数JAVA_OPTIONSを以下の値にアップデートします(アプリケーションデプロイメント次第でアップデート方法は異なります)。
JAVA_OPTIONS = "-XX:+UseJITServer -XX:JITServerAddress=<SERVICE_IP> -XX:JITServerPort=<SERVICE_PORT>"
Configuring OpenJ9 JITServer Technology
helm chartによるデフォルトの設定は、クライアントJVMのサービスを提供できるJITServerインスタンスをデプロイするのであれば十分ですが、JITServerとクライアントJVMをさらに設定したい場合もあるでしょう。ここでは、JVMの環境変数(JAVA_OPTIONS
など)を使って渡すことができる、利用可能なJITServerのオプションをすべて列挙します。
JITServerのアドレスとポート番号を構成する
-XX:JITServerAddress=<文字列>
JITServerのサービス名もしくはIPアドレスを指定する(デフォルト値はlocalhost)。このオプションはクライアントJVMでのみ有効。-XX:JITServerPort=<整数値>
JITServerがリスニングし、クライアントJVMが接続するポート番号を指定する(デフォルト値は38400)。
JITコンパイルリクエストのタイムアウト値を構成する
-XX:JITServerTimeout=<整数値>
ソケット操作のタイムアウト値(ミリ秒)を指定する(デフォルト値は、JITServerでは30000ミリ秒、クライアントJVMでは2000ミリ秒)。クライアントJVM側のタイムアウト値は、ネットワークの遅延が大きい場合、適宜増加させる必要がある。
JITServerとクライアントJVM間のネットワーク通信をOpenSSLで暗号化するよう構成する
- バージョン1.0.xまたは1.1.xを使用して、OpenSSLの鍵と証明書を生成する。
openssl genrsa -out key.pem 2048
openssl req -new -x509 -sha256 -key key.pem -out cert.pem -days 365
-XX:JITServerSSLKey=key.pem -XX:JITServerSSLCert=cert.pem
JITServerで秘密鍵と証明書ファイルを指定する。-XX:JITServerSSLRootCerts=cert.pem
クライアントJVMで証明書ファイルを指定する。
JITServerデプロイ時に追加の設定を行うためには、helm chartの環境変数JAVA_OPTIONS
を更新してください。これは、--set
オプションを追加することで可能です。例えば、JITServerがポート9000をリッスンし、タイムアウト値として40000msを使用するように設定するには、以下のようなオプションを使用します。
$ helm install --set env[0].name="JAVA_OPTIONS" --set env[0].value="-XX:JITServerPort=9000 -XX:JITServerTimeout=40000 " openj9-release openj9/openj9-jitserver-chart
OpenJ9 JITServer Technology Deployment Considerations
通常クラウド環境は複雑で多くの可動部品を含んでいるため、JITServerのデプロイメントトポロジーはクラスターやインフラストラクチャによって異なる場合があります。このセクションでは、ほとんどのクラスターでJITServerテクノロジーの機能とパフォーマンスを最適化するための経験則を紹介します。
Compatible JDK Version
JITServerテクノロジーは、JITServerとクライアントJVM間のバージョン互換性を強制することに注意してください。JITServerの使用を前提にデプロイメントを構成する場合、バージョンの不一致があると、JITServerはクライアントからの接続を拒否し、パフォーマンスの低下やクライアントでのメモリ不足の原因となる可能性があります。
互換性を確保する最も簡単で推奨される方法は、Javaアプリケーション・イメージとJITServerイメージを、Adopt OpenJDK Docker Hubで入手可能な同じEclipse OpenJ9コンテナ・イメージをベースにすることです。これにより、JITServerとクライアントJVMの両方で同じJDKリリースバージョンが保証されます。
adoptopenjdk
Official Images for OpenJDK + HotSpot and OpenJDK + Eclipse OpenJ9 binaries built by AdoptOpenJDK.
https://hub.docker.com/_/adoptopenjdk
もう一つの方法は、手動でJDKのリリースバージョンを照合することです。Javaアプリケーションイメージ内で java -
version を実行し、JDKのリリースバージョンを確認します。以下の例では、OpenJ9 Java 11 のJDKが 0.24.0 のリリースバージョン、 Linux のamd64アーキテクチャで動作しています。Javaバージョン、リリースバージョン、アーキテクチャのキーワード検索で、Adopt OpenJDK Docker Hubから該当するイメージを探します。
$ java -version
openjdk version "11.0.10" 2021-01-19
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.10+9)
Eclipse OpenJ9 VM AdoptOpenJDK (build openj9-0.24.0, JRE 11 Linux amd64-64-Bit Compressed
References 20210120_910 (JIT enabled, AOT enabled)
OpenJ9 - 345e1b09e
OMR - 741e94ea8
JCL - 0a86953833 based on jdk-11.0.10+9)
最後に、helm chartインストール時に --set
オプションを使用して、helm chartのイメージリポジトリとタグをJavaアプリケーションまたはAdopt OpenJDKイメージにオーバーライドします。以下のように設定します。L
helm install –set image.repository="IMAGE_REPO" --set image.tag="IMAGE_TAG" jitserver-release openj9-jitserver-chart
Deployment Topology
同じJDKリリースバージョンを実行しており、JITServerインスタンスに十分なリソースが割り当てられている限り、1つのJITServerインスタンスに1つ以上のクライアントJVMを接続できます。ただし、1つのクライアントJVMが複数のJITServerインスタンスに接続することはできません。Kubernetes環境でこれを防ぐには、JITServerのサービスでservice.spec.sessionAffinity
をClientIP
に設定する必要があります。
通常Kubernetesクラスターは、クラスターのワークロードに基づいて自動的にPodをノードに割り当てます。しかし、ワークロードについて追加情報を持つ開発者であれば、ノードアフィニティの助けを借りて、さらにパフォーマンスを最適化するために、特定のノードにPodを手動で割り当てることができます。このテクニックは、クライアントJVMとJITServerのPodを同じノードに配置して、両者間のネットワークレイテンシーを最小化するために使用されることもあります。
Life Cycle Management
場合によっては、クライアントJVMをより新しいバージョンのJavaにアップグレードしたいこともあるでしょう。JITServerインスタンスは、それが提供するクライアントJVMと互換性がなければなりません。最も安全な方法は、クライアントJVMをアップグレードする前に、新しいバージョンのJITServer helm chartをもう一つデプロイし、その後古いJITServerインスタンスを削除することです。
可能な限り、クライアントJVM(アプリケーション)が終了する前にJITServerインスタンスを削除することは避けてください。クライアントJVMを実行しているコンテナのメモリ制限が、JITServerがすべてのJITコンパイルを処理するという前提で設定されている場合、アプリケーションがネイティブメモリの不足に陥り、終了してしまう可能性があります。すべてのアプリケーションがリタイアしたら、以下のようにJITServerインスタンスを安全に削除できます。
$ helm delete jitserver-release
release " jitserver-release " uninstalled
Container Resource Allocation
以下は、JITServerインスタンスに1つのアプリケーションが接続することを想定した場合の、JITServerのCPUおよびメモリリソースの推奨最小値です。
- 必要なCPU: 1 CPU
- 必要なメモリ: 512 MB
- ストレージ: なし
1つのJITServerインスタンスに複数のアプリケーションが同時に接続する場合は、上記の値を適宜かけ算してください。なお、複数のアプリケーションが時間をずらしてJITServerに接続する場合(アプリケーション間に多少の遅延がある場合)は、上記の要求値を下げることができます。
Demo: Enabling JITServer Technology on a Sample Java Application
このセクションでは、サンプルのJavaアプリケーションとしてOpen Liberty Application Serverを使い、この上でJITServerテクノロジーを有効にするプロセスをステップ・バイ・ステップで説明します。アプリケーション・イメージはOpen Liberty docker hubで公開されています。
open-liberty
Open Liberty multi-architecture images based on Ubuntu 18.04
https://hub.docker.com/_/open-liberty
このデモで使用されているアプリケーションイメージは、open-liberty:21.0.0.2-full-java8-openj9
で、adoptopenjdk:8u282-b08-jre-openj9-0.24.0
をベースイメージとして使用しており、これをJITServerとしてデプロイされていることに注意してください。
1. Javaアプリケーションのデプロイ前にJITServerインスタンスをデプロイ
まず、helm chartを使ってJITServerインスタンスとしてOpenJ9イメージをデプロイします。イメージリポジトリとタグを --set
でオーバーライドしてください。
$ helm install jitserver-release --set image.repository=adoptopenjdk --set
image.tag=8u282-b08-jre-openj9-0.24.0 openj9/openj9-jitserver-chart
NAME: jitserver-release
LAST DEPLOYED: Wed Feb 10 21:11:39 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
...
2. アプリケーションデプロイ時にJITServerテクノロジーを有効化
JITServerインスタンスをデプロイしたら、JITServerテクノロジーを有効にするJVMオプションを設定したJavaアプリケーションイメージをデプロイします。Open Liberty helm chartを使用してOpen Libertyのアプリケーションをデプロイします。
Open Liberty Helm Chart
https://github.com/IBM/charts/tree/master/stable/ibm-open-liberty
環境変数jvmArgs
は、JITServerのオプション -XX:+UseJITServer -XX:JITServerAddress=jitserver-release-openj9-jitserver-chart
をクライアントJVMに渡します。
$ helm install my-web-app --set image.repository=open-liberty --set image.tag=21.0.0.2-
full-java8-openj9 --set env.jvmArgs="-XX:+UseJITServer -XX:JITServerAddress=jitserver-
release-openj9-jitserver-chart" ibm-charts/ibm-open-liberty
NAME: my-web-app
LAST DEPLOYED: Wed Feb 10 21:15:43 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
...
3. verboseログやコンテナのリソース消費を観察し、JITServerテクノロジーが動作していることを確認
JITServerインスタンス上で生成されるverboseログは、JITServerが有効であることの指標になります。これは、JVMオプションに-Xjit:verbose={JITServer}
を追加し、JITServerのverboseログが/tmp/output.log
に表示されていることを確認することで実現できます。
$ kubectl exec jitserver-release-openj9-jitserver-chart-8695b7584f-k56lb cat
/tmp/output.log
JITServer is currently a technology preview. Its use is not yet supported.
...
JITServer is ready to accept incoming requests
#JITServer: Server received request for stream 00007FBAFB13C2F0
...
#JITServer: compThreadID=0 has successfully compiled
java/lang/Double.longBitsToDouble(J)D
Conclusion
OpenJ9 JITServerテクノロジーは、クラウドやリソースに制約のある環境でJavaアプリケーションを実行している方にはぜひ試していただきたい機能です。JITServerのhelm chartは、KubernetesやOpenShiftのクラスター上でのデプロイメントプロセスを自動化してくれます。helm chartにはデフォルトの設定が盛り込まれており、3つのシンプルなステップでJITServerをデプロイできます。OpenJ9 JVM上でJITServerテクノロジーを有効にすることで、コンテナのサイズを小さくし、スループット性能を向上させることができます。
JITServerテクノロジーについてさらに詳しく知りたい方は、OpenJ9 JITServer helm chartリポジトリや、JITServerに関する興味深いブログをご覧ください。
OpenJ9 JITServer helm chartリポジトリ
https://github.com/eclipse/openj9-utils/tree/master/helm-chart
JITServer関連のブログエントリ
https://blog.openj9.org/tag/jitserver/