Share
Embed
コンテナ環境でのJavaチューニング
Javaをコンテナ環境で使う際に、知らないうちに発生してしまうCPUスロットリングやOOM-killerなど、発生メカニズムやJavaVMのチューニング対処について。
Kenji Kazumura
June 05, 2023
Transcript
57 slides
-
© 2023 Fujitsu Limited 2023/06/04 数村憲治 コンテナ環境での Javaチューニング JJUG CCC
Spring 2023 @kkzr -
© 2023 Fujitsu Limited 自己紹介 Jakarta EE 仕様策定委員 MicroProfile ステコミ委員
JCP Executive Committee メンバー Eclipse Foundation ボードディレクター EclipseCon、JakartaOne、JJUGなどで登壇 2 -
本資料での注意事項 © 2023 Fujitsu Limited 本資料は、OpenJDK実装をベースに記載しています。 単に、「JDK」、「Java」と記載している場合、 OpenJDKの実装を意味していることがあります。 OpenJDK 17は、17.0.7
OpenJDK 8は、8u372 の各バージョンを使用しています。 マイナーバージョンが違う場合は、 本資料の記載動作と違う場合があります。 3 -
コンテナと、Javaエルゴノミクス © 2023 Fujitsu Limited コンテナ コンテナに割当てられた メモリ・CPUリソース 整合性 Java
Javaが使えると思っている メモリ・CPUリソース コンテナ起動のたびに 変わる可能性あり 他のコンテナに影響される 可能性あり エルゴノミクス または 手動で設定 5 -
Javaのエルゴノミクス © 2023 Fujitsu Limited GC種別 Javaヒープサイズ GCスレッド数 並行処理数 ・・・
Javaでのエルゴノミクスとは、各種パラメタを JVMが自身が動作する環境をベースに自動的に設定すること エルゴミクス対象 例 7 -
GC種別 (OpenJDK 8) © 2023 Fujitsu Limited 利用可能 GC エルゴノミクス
対象 条件 Serial ✔ Parallel ✔ CMS N/A G1 N/A CPU数 < 2 || メモリ < 1792MB CPU数 ≧ 2 && メモリ ≧ 1792MB 8 -
GC種別 (OpenJDK 17) © 2023 Fujitsu Limited 利用可能 GC エルゴノミクス
対象 条件 Serial ✔ Parallel N/A G1 ✔ Z N/A N/A CPU数 < 2 || メモリ < 1792MB CPU数 ≧ 2 && メモリ ≧ 1792MB Shenandoah 9 -
コンテナ環境でのコア数 © 2023 Fujitsu Limited 40% 30% ▪コンテナ環境 ▪非コンテナ環境 20%
10% 使 用 率 コア数 https://newrelic.com/resources/report/2023-state-of-the-java-ecosystem 3 5 6 7 10 11 12 15 20 24 36 48 10 -
Javaヒープサイズ © 2023 Fujitsu Limited 初期値:物理メモリの1/64 最大値:物理メモリの1/4 -XX:MaxRAMPercentage -Xmx エルゴノミクスによる設定
-XX:InitialRAMPercentage -Xms 気にいらなければ、自分で設定 Javaヒープサイズの指定方法 (JDK 10 以降) 11 -
コンテナ環境でのJavaヒープサイズ © 2023 Fujitsu Limited https://newrelic.com/resources/report/2023-state-of-the-java-ecosystem 使 用 率 JVMヒープサイズ設定
30% 20% 10% ▪コンテナ環境 ▪非コンテナ環境 MB GB GB GB GB GB GB GB GB GB 12 -
論理CPU数の影響 © 2023 Fujitsu Limited エルゴノミクスによる設定 (OpenJDK 17) GC スレッド数
・複雑 ・おおよそ、8+(n-8)*(5/8) コンパイラ スレッド数 ・かなり複雑 ・おおよそ、 int(log2 n) * (int)(log2 int(log2 n)) * 3 /2 ForkJoinPool 並行度数 ・おおよそ、n - 1 GC種別 前掲 n = 論理CPU数 13 -
DockerでのCPUリソース割当て © 2023 Fujitsu Limited docker run --cpu-period=100000 --cpu-quota=200000 CPU
Share CPU Quota docker run --cpu-shares=2048 コンテナに割り当てるCPU数を相対的に指定する デフォルト値は1024 period間に消費可能なCPU時間を指定する CPUスロットリングが発生しやすい CPU Sets 使用するCPUセットを指定する 一般的には使わない 例 例 15 -
CPU1 CPU2 CPU Shares (ビジー) © 2023 Fujitsu Limited A:B:C=2:1:1でCPU時間が配分される
コンテナC share:1024 コンテナB share:1024 それぞれのコンテナがビジー状態であれば、 コンテナA share:2048 16 -
CPU2 アイドル CPU1 CPU Shares (アイドルあり) © 2023 Fujitsu Limited
コンテナB share:1024 コンテナA、Cがアイドル状態になると・・・ コンテナBがCPU時間を総どり コンテナC share:1024 コンテナA share:2048 17 -
Javaプロセスが認識するCPU数(cgroup v2) © 2023 Fujitsu Limited 1024*n ± 512 がおおよその境
docker run java docker run --cpu-shares=1024 java docker run --cpu-shares=1536 java docker run --cpu-shares=2048 java docker run --cpu-shares=2560 java ---> ホストCPU数 ---> 1CPU ---> 2CPU ---> 2CPU ---> 3CPU JDK 8、JDK 17 with -XX:+UseContainerCpuShares の場合 18 -
Javaプロセスが認識するCPU数(cgroup v1) © 2023 Fujitsu Limited 1024がマジックナンバー docker run java
docker run --cpu-shares=1023 java docker run --cpu-shares=1024 java docker run --cpu-shares=1025 java docker run --cpu-shares=2049 java ---> ホストCPU数 ---> 1CPU ---> ホストCPU数 ---> 2CPU ---> 3CPU JDK 8、JDK 17 with -XX:+UseContainerCpuShares の場合 19 -
Javaプロセスが認識するCPU数 © 2023 Fujitsu Limited デフォルトでは、CPU Sharesは参照しない docker run java
docker run --cpu-shares=1023 java docker run --cpu-shares=1024 java docker run --cpu-shares=1025 java docker run --cpu-shares=2049 java ---> ホストCPU数 --->ホストCPU数 ---> ホストCPU数 --->ホストCPU数 --->ホストCPU数 JDK 17 (デフォルト -XX:-UseContainerCpuShares) の場合 20 -
Java における CPU Share 注意事項 © 2023 Fujitsu Limited 実際にコンテナに割当てられるCPU時間は、
他のコンテナの状態に依存する => 非決定的 Javaプロセスからは、他のコンテナの状態はわからない => 変動するCPUを前提にプログラムするのは難しい 21 -
Java と CPU Share ミスマッチ (1) © 2023 Fujitsu Limited
ホストマシンのCPU数が3の場合 コンテナA コンテナB --cpu-shares= 1024 2048 コンテナに割当てられる CPU数(CPU時間) 1 2 Javaが認識するCPU数 3 2(JDK8) or 3(JDK17) 例 ミスマッチ ミスマッチ cgroup v1 の例 22 -
Java と CPU Share ミスマッチ (2) © 2023 Fujitsu Limited
ホストマシンのCPU数が3の場合 コンテナA コンテナB --cpu-shares= 1500 3000 コンテナに割当てられる CPU数(CPU時間) 1 2 Javaが認識するCPU数 1 3 例 ミスマッチ cgroup v2 の例 23 -
Docker CPU Quota © 2023 Fujitsu Limited quota : period間にコンテナが消費できるCPU時間(マイクロセカンド)
runサブコマンドに、「--cpu-quota=」を指定 quotaを使い切るとコンテナにCPUは割り当てられない CPUスロットリング period : デフォルトは100マイクロセカンド runサブコマンドに、「--cpu-period=」を指定 24 -
Java における CPU Quota © 2023 Fujitsu Limited デフォルトでは、CPU Quota
が CPU Shares より優先 JDK17のデフォルトでは、CPU Shares を参照しない -XX:[+/-]PreferContainerQuotaForCPUCountフラグで、優先変更可能。 ただし、「Prefer」であって、quotaを全く使用しないわけではない。 Javaフラグ CPU数 -XX:+PreferContainerQuotaForCPUCount quota / period -XX:-PreferContainerQuotaForCPUCount min ( quota/period, shares/1024 ) -XX:ActiveProcessorCount=n n Javaの計算が気にいらなければ、ActiveProcessorCountを使用。 25 -
CPU Javaプロセスが認識するCPU数 (JDK 8) © 2023 Fujitsu Limited docker run
--cpu-quota=300000 java CPU docker run --cpu-quota=300000 --cpu-shares=2048 java CPU docker run --cpu-quota=300000 --cpu-shares=2048 java -XX:-PreferContainerQuotaForCPUCount CPU docker run --cpu-quota=100000 --cpu-shares=2048 java -XX:-PreferContainerQuotaForCPUCount CPU docker run --cpu-quota=300000 --cpu-shares=2048 java -XX:ActiveProcessorCount=4 26 -
CPU Javaプロセスが認識するCPU数 (JDK 17) © 2023 Fujitsu Limited docker run
--cpu-quota=300000 java CPU docker run --cpu-quota=300000 --cpu-shares=2048 java CPU docker run --cpu-quota=300000 --cpu-shares=2048 java -XX:+UseContainerCpuShares -XX:-PreferContainerQuotaForCPUCount CPU docker run --cpu-quota=100000 --cpu-shares=2048 java -XX:+UseContainerCpuShares -XX:-PreferContainerQuotaForCPUCount CPU docker run --cpu-quota=300000 --cpu-shares=2048 java -XX:ActiveProcessorCount=4 27 -
CPUスロットリング © 2023 Fujitsu Limited スレッド1 スレッド2 時間 0μ 1000μ
2000μ quota=1000、period=1000 マイクロセカンドの場合 スレッド3 500μ消費 500μ消費 処理開始 たとえ、他のコンテナがアイドル 状態でも、CPUは割り当てられない 処理開始 処理開始 Javaはマルチスレッド 28 -
CPUスロットリングの検出方法 © 2023 Fujitsu Limited ⚫ nr_periods: フルになったperiodの回数 ⚫ nr_throttled:
スロットリング回数 ⚫ throttled_time/throttled_usec: スロットリング 時間(ナノ秒/マイクロ秒) コンテナ内の /sys/fs/cgroup/cpu/cpu.stat (cgroup v1) /sys/fs/cgroup/cpu.stat (cgroup v2) 29 -
CPU Quotaを設定すると、 CPUスロットリングの可能性あり CPU Shareだけだと、 実際のCPU割当が決定的にならない Javaでの注意事項 - CPUリソース ©
2023 Fujitsu Limited ⚫ ベストエフォートでの対応 ⚫ 問題発生時は、ActiveProcessorCountや、各種パラメタの 手動設定も検討 ⚫ Javaでは発生しやすい マルチスレッドで、知らない所でCPUを使っている ⚫ 性能調査が難しくなる可能性あり 30 -
Dockerでのメモリ割当て © 2023 Fujitsu Limited docker run --memory=2048mb docker run
--memory-swap=2048mb コンテナへの物理メモリ割当て コンテナへの物理メモリ+スワップ割当て スワッピングをさけるめに、両者を同一にする 例 例 31 -
Javaでの注意事項 - メモリリソース © 2023 Fujitsu Limited -Xmxではなく、-XX:MaxRAMPercentageの方が安全 docker inspectで、OOM
killer発生を確認 コンテナ割当メモリより、大きなヒープサイズ指定が可能 OutOfMemoryErrorが出ない場合あり 32 -
KubernetesでのCPUリソース割当て © 2023 Fujitsu Limited ⚫ Requests :スケジューリングに使用 ⚫ Limits:CPU割当上限に使用
RequestsとLimitsの2種類の設定 RequestsとLimitsの両方を、 コンテナごとに設定可能 34 -
スケジューリング © 2023 Fujitsu Limited CPU1 ノード1 ノード2 CPU4 CPU2
CPU3 CPU1 CPU4 CPU2 CPU3 35 -
スケジューリング © 2023 Fujitsu Limited Pod1 Request 3CPU デプロイ CPU1
ノード1 ノード2 CPU4 CPU2 CPU3 CPU1 CPU4 CPU2 CPU3 36 -
スケジューリング © 2023 Fujitsu Limited Pod1 Request 3CPU デプロイ CPU1
ノード1 ノード2 CPU4 CPU2 CPU3 CPU1 CPU4 CPU2 CPU3 Pod1 Pod1 Pod1 37 -
スケジューリング © 2023 Fujitsu Limited Pod1 Request 3CPU デプロイ Pod2
Request 3CPU デプロイ CPU1 ノード1 ノード2 CPU4 CPU2 CPU3 CPU1 CPU4 CPU2 CPU3 Pod1 Pod1 Pod1 38 -
スケジューリング © 2023 Fujitsu Limited Pod1 Request 3CPU デプロイ Pod2
Request 3CPU デプロイ CPU1 ノード1 ノード2 CPU4 CPU2 CPU3 CPU1 CPU4 CPU2 CPU3 Pod1 Pod1 Pod1 Pod2 Pod2 Pod2 39 -
スケジューリング © 2023 Fujitsu Limited CPU1 ノード1 ノード2 Pod1 Request
3CPU CPU4 CPU2 CPU3 CPU1 CPU4 CPU2 CPU3 デプロイ Pod1 Pod1 Pod1 Pod2 Request 3CPU デプロイ Pod2 Pod2 Pod2 Pod3 Request 3CPU ペンディング 40 -
KubernetesでのCPU指定 © 2023 Fujitsu Limited CPU数単位、または、ミリコア単位で指定 (「2.5」と「2500m」は同値 ) 指定例 ノードに
0.5CPU分の空きがあれば、 デプロイされる。 0.5CPUの空きがなければ、 ペンディング。 spec: containers: - name: myapp image: myimage resource: requests: cpu: “500m” limits: cpu: “1000m” 41 -
Requestsの実装 © 2023 Fujitsu Limited JDK 17では、デフォルトでは、プロセッサ数の計算に使用しない。 JDK 8では、Limitsが指定されていなければ、プロセッサ数の計算 に使用される。
CPU Sharesなので、 実際のCPU割当はRequest以上になることが可能 Kubernetesの実装 1CPU(=1000m)は、1024 CPU Sharesに換算 Javaの実装 42 -
Limitsの実装 © 2023 Fujitsu Limited 変換されたquotaが、プロセッサ数の計算に使用される。 ただし、利用可能なプロセッサ数を超えない。 periodは、100ミリ秒 quotaは、Limits値 X
100 (ミリ秒) に変換 quotaを使い切ると、PodにCPUは割当てられない (CPUスロットリング) Kubernetesの実装 Javaの実装 43 -
KubernetesでのCPUリソース設定 © 2023 Fujitsu Limited 指定しないと、CPUリソースが非決定的 指定すると、CPUスロットリングになる可能性あり 指定するか・しないかは、 ノード内で他に何が動いているかによりけり 指定すると、CPUリソース割り当ては流動的になる
指定しないと、ノード内でのCPU割り当ては保証されない Requestsの指定 Limitsの指定 ---> 性能調査が難しくなるかも 44 -
Javaでの注意事項 – Requests & Limits © 2023 Fujitsu Limited Requests
Limits Javaのデフォルト Limitsを用いたCPU数を計算 それだけのCPUが割り当てられるとは限らない K8S設定 Javaで-PreferContainerQuotaForCPUCount設定 Requestsを用いたCPU数を計算 それ以上のCPUが割り当てられるかもしれない RequestsとLimitsが違う場合 RequestsとLimitsが同じ場合 Requests Limits Limitsを用いたCPU数を計算 決定的にCPUが割り当てられる CPUスロットリングになる可能性あり ノード全体で有効利用できない可能性あり CPU リ ソ ス ー CPU リ ソ ス ー 45 -
メモリRequests © 2023 Fujitsu Limited Pod1 Request 4GB Pod2 Request
4GB デプロイ デプロイ 2GB 2GB ノード(8GB) 47 -
メモリRequests © 2023 Fujitsu Limited ノード(8GB) Pod1 Request 4GB Pod2
Request 4GB デプロイ デプロイ 6GB 2GB 48 -
メモリRequests © 2023 Fujitsu Limited ノード(8GB) Pod1 Request 4GB Pod2
Request 4GB デプロイ デプロイ ここで、Pod2がさらに1GB(合計3GB)使おうとすると。。。 6GB 2GB 49 -
メモリRequests © 2023 Fujitsu Limited ノード(8GB) Pod1 Request 4GB Pod2
Request 4GB デプロイ デプロイ 2GB OOM-killed 50 -
メモリRequests © 2023 Fujitsu Limited ノード(8GB) Pod1 Request 4GB Pod2
Request 4GB デプロイ リスタート 3GB 51 -
サマリ © 2023 Fujitsu Limited メモリ不足は、コンテナダウン(OOM-kill)ですぐわかる コンテナとJavaの整合性 CPUスロットリングは、性能に影響するが検出が難しい ⚫ Share/Quotaの動作原理をふまえたJava設定
⚫ Javaは生来的に、マルチスレッド ActiveProcessor、コンパイラスレッド、GCスレッドチューニング ⚫ OOM-killされないようにRequests/Limitsを設定する ⚫ JavaヒープサイズはMaxRAMPercentageなどでOOM-kill防止 ⚫ 使用するJavaの版で動作確認する(マイナーアップでの非互換あり) 55