Help us understand the problem. What is going on with this article?

Javaバージョン毎におけるkubernetes (GKE)上でのメモリ、CPU認識のデフォルト動作の違い

More than 1 year has passed since last update.

はじめに

Java 8においても、
Oracle Blogs 日本語のまとめ - [Java] Java SE support for Docker CPU and memory limits
の記事から、8u131以降はある程度 container.resource.limit.cpu、container.resource.limit.memoryを見て上手い事やってくれるのかな?
と思いきや、いまいちな挙動だったので、kubernetesで動かした際のjava8、java10、java11(ea)でのCPUの認識、及びメモリサイズの確保部分について調べてみました。

特に、GCの並列実行(スレッド数)や、各種並列スレッドのプール数等にも影響がある、 Runtime#availableProcessorsの動きがバージョンで変わるのか?という部分が
https://bugs.openjdk.java.net/browse/JDK-8140793
を見ても、2018/7/31時点でまだfixedVersionが出ていないのでどのバージョンでもまともに動かないのかどうかが気になった次第です。

結論

  • kubernetesでJavaアプリケーションを動作させる際、CPU、メモリを正しく認識させたいならjava10以降ならちゃんと認識してくれる。
  • java8でもメモリはXmx、Xmsなどで制御可能。
  • java8ではRuntime#availableProcessorsは正しく制限かける事はできないが、GCThread等は制御可能。

前提Javaアプリケーション

適当なSpring Boot アプリケーションを動かしてそのmetrics情報と、java -XX:+PrintGCDetails -XX:+PrintFlagsFinal -XX:+UnlockExperimentalVMOptions をcontainer上で動かした際の情報を確認します。

Spring Bootのアプリケーションは、
https://github.com/h-r-k-matsumoto/spring-boot-sample
をベースとして、それぞれ下記のように変更しています。また、cointainerに対するリソース割当は、下記のようになります。

020_deployments.yml
        resources:
          requests:
            cpu: 150m
            memory: 512Mi
          limits:
            cpu: 900m
            memory: 512Mi

deploymentsの全文は、
https://github.com/h-r-k-matsumoto/spring-boot-sample/blob/master/kubernetes/020_deployments.yml
です。

java8 (no cgroup option)

pom.xmlのfrom imageを下記に変更します。特にJavaオプションでは何も制御しません。デフォルトのままです。

pom.xml
  <image>openjdk:8u171-jre-alpine</image>

java8

pom.xmlのfrom imageを下記に変更します。

pom.xml
  <image>openjdk:8u171-jre-alpine</image>

更に、下記jvmFlagオプションを追加します。ポイントは、 UseCGroupMemoryLimitForHeap です。
後は・・・私がいつもdocker 環境で動かす際につけているものです。

pom.xml
  <jvmFlag>-XX:+UnlockExperimentalVMOptions</jvmFlag>
  <jvmFlag>-XX:+UseCGroupMemoryLimitForHeap</jvmFlag>
  <jvmFlag>-XX:ParallelGCThreads=1</jvmFlag>
  <jvmFlag>-XX:CICompilerCount=2</jvmFlag>
  <jvmFlag>-Djava.util.concurrent.ForkJoinPool.common.parallelism=1</jvmFlag>

java10

pom.xmlのfrom imageを下記に変更します。オプションは指定無しです。

pom.xml
  <image>openjdk:10-jre-slim</image>

java11 (ea)

pom.xmlのfrom imageを下記に変更します。オプションは指定無しです。

pom.xml
  <image>openjdk:11-jre-slim</image>

計測内容

Spring Boot Actuatorのmetrics 機能と、

PrintFlagsFinal
 kubectl exec {pod-name} -- java -XX:+PrintGCDetails -XX:+PrintFlagsFinal -XX:+UnlockExperimentalVMOptions

を実行して取得した情報を利用します。

Spring Boot Actuator - metrics

system.cpu.count

http://pod-ip:port/actuator/metrics/system.cpu.count
で取得します。Runtime#availableProcessors の実行結果です。
ソースはmicrometer - ProcessorMetrics.java

jvm.memory.max (heap)

http://pod-ip-port/actuator/metrics/jvm.memory.max?tag=area:heap
で取得します。ヒープ領域の最大サイズです。
ソースは、micrometer - JvmMemoryMetrics.javaMemoryUsage#getMax の結果です。

PrintFlagsFinal結果

MaxHeapSize

最大ヒープ管理領域のサイズです。jvm.memory.maxと同じになる・・・はず。

UseParallelGC

GCを並列実行するかどうかです。
参考:https://docs.oracle.com/javase/jp/8/docs/technotes/guides/vm/gctuning/collectors.html

ParallelGCThreads

パラレルGCの際のスレッド数です。

計測結果

実行するノードのspecは、vCPUx2、メモリ7.5GBです。

java version system.cpu.count jvm.memory.max(heap):MiB MaxHeapSize:MiB UseParallelGC ParallelGCThreads
java 11(ea) 1 123.75 128.00 false 0
java 10 1 123.75 128.00 false 0
java 8 2 120.00 128.00 true 2
java 8 (no cgroup option) 2 1,857.00 1,870.00 true 2

※java 8でparallelになってしまっているのは、-XX:+UseSerialGC のオプションでserialに変更可能。というか -XX:ParallelGCThreads=1 が効いていない…。

java11、java10は、特にオプション指定をしなかった場合は、containersに割り当てられたメモリサイズの 512Mi から正しく計算され、MaxRAMFraction=4となるため、512/4 = 128 となっていました。
また、CPUに関しても、900mとしましたが、1コア分として判定されています。
java11、java10は、XmxやらXmsは指定せずとも、container上で正しく動きそうです。

java8の場合は、メモリに関しては制御可能です。ただし、CPU数に関しては上手い事手出しができないっぽいので…  Runtime#availableProcessors から計算していてリソース確保している部分は、都度システムプロパティ等で設定した方がよさそうです。

jvm.memory.max(heap)と、MaxHeapSizeで多少の誤差が出てるのは・・・気にしないです!
もし「このようなオプションを指定した方が良い」、「ここ、間違ってるよ」とかあればご指摘お願いします。

参考

h-r-k-matsumoto
Java,GCP周りでサービス開発してます。 社内でのナレッジから社外含めてのナレッジに最近切り替えて活動してます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした