fbpx

[和訳] Java 10とDockerコンテナインテグレーションの改善 #docker

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。

本稿は IMPROVED DOCKER CONTAINER INTEGRATION WITH JAVA 10 (2018/4/3) の和訳です。

Apache SparkやKafkaなどのデータサービスや従来のエンタープライズアプリケーションなど、Java Virtual Machine(JVM)で実行する多くのアプリケーションはコンテナ内で実行可能です。最近まで、コンテナ内でJVMを実行すると、メモリとCPUサイジングの使用量に対して問題が発生し、パフォーマンスが低下していました。これは、Javaがコンテナ内で実行しているとJava自身が認識していなかったためです。 Java 10のリリースで、JVMはコンテナコントロールグループ(cgroup)が設定した制約を認識するようになりました。メモリとCPUの両方の制約を、コンテナ内のJavaアプリケーションに対して直接使うことができます:

  • コンテナに設定したメモリ制限を遵守する
  • コンテナで使用可能なCPUを設定する
  • コンテナのCPU制約を設定する

Java 10の新機能は、Docker for MacまたはWindowsとDocker Enterprise Edition (Docker EE) 環境の両方で実現しています。

コンテナのメモリ制限

Java 9まで、コンテナが使用するフラグによって設定したメモリまたはCPUの制限を、JVMは認識できませんでした。Java 10では、メモリ制限を自動的に認識し、適用します。

Javaは、2つのCPUと2GBのメモリを持つマシンをサーバクラスと定義し、デフォルトのヒープサイズを物理メモリの1/4と定義します。例えば、Docker EEをインストールしたマシンが2GBのメモリと4つのCPUを搭載しているとしましょう。Java 8とJava 10を実行するコンテナの違いを比較してみましょう。

Java 8:


docker container run -it -m512 --entrypoint bash openjdk:latest
 
$ docker-java-home/bin/java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
uintx MaxHeapSize := 524288000
{product}
openjdk version "1.8.0_162"

最大ヒープサイズは、コンテナ上で設定した512M制限ではなく、Docker EEで設定した512Mまたは2GBの1/4です。これと比較して、Java 10で同じコマンドを実行すると、コンテナで設定したメモリの制限値が予想した128Mにかなり近いことがわかります:


docker container run -it -m512M --entrypoint bash openjdk:10-jdk
 
$ docker-java-home/bin/java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
size_t MaxHeapSize = 134217728
{product} {ergonomic}
openjdk version "10" 2018-03-20

使用可能なCPUの設定

デフォルトでは、ホストマシンのCPUサイクルへの各コンテナのアクセスは無制限です。ホストマシンのCPUサイクルへのアクセスを制限するために、指定のコンテナに対してさまざまな制約を設定できます。Java 10はこれらの制限を認識します:


docker container run -it --cpus 2 openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2

Docker EEに割り当てたすべてのCPUサイクルは、同じ割合のCPUサイクルを取得します。その割り当ては、コンテナの CPUシェア ウエイトを実行中の他のすべてのコンテナのウエイトを変更することで、変更できます。その割り当ては、CPUに負担がかかるプロセスを実行する場合にのみ適用するようにします。あるコンテナ内のタスクがアイドル状態の場合、他のコンテナが残りのCPU時間を使用します。CPU時間の実際の量は、システム上で実行しているコンテナの数によって異なります。これらはJava 10で設定できます:


docker container run -it --cpu-shares 2048 openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 2

cpuset制約は、どのCPUがJava 10で実行を許可するかを設定しています。


docker run -it --cpuset-cpus="1,2,3" openjdk:10-jdk
jshell> Runtime.getRuntime().availableProcessors()
$1 ==> 3

メモリとCPUの割り当て

Java 10では、コンテナの設定を使用して、アプリケーションをデプロイするために必要なメモリとCPUの割り当てを見積もることができます。コンテナ内で実行している各プロセス用のメモリヒープとCPU要件が確定していて、JAVA_OPTSを設定しているとします。例えば、アプリケーションが10ノードに分散している場合、5つのノードそれぞれに1024のCPUシェアと512Mbのメモリが必要で、他の5つのノードに512のCPUシェアと256Mbが必要だとします。1つのCPUシェアの割合は1024で表すことに注意してください。

メモリの場合、アプリケーションは最小でも5Gbを割り当てる必要があります。

512Mb x 5 = 2.56 Gb

256Mb x 5 = 1.28 Gb

アプリケーションを効率的に実行するためには、8つのCPUが必要です。

1024 x 5 = 5 CPUs

512 x 5 = 3 CPUs

ベストプラクティスでは、JVMで実行する各プロセス用のメモリとCPU割り当てを決定するにあたり、アプリケーションのプロファイリングを行うことを推奨しています。しかしJava 10においては、Javaアプリケーションのメモリ不足エラーが発生しないようにコンテナのサイジングを行い、作業負荷の処理に十分なCPUを割り当てる場合は、この推測を取り除けます。

Java開発者向けのDockerソリューションの詳細については、次を参照してください:

新規CTA