Upgrade to Pro — share decks privately, control downloads, hide ads and more …

コンテナ環境でのJavaチューニング

 コンテナ環境でのJavaチューニング

Javaをコンテナ環境で使う際に、知らないうちに発生してしまうCPUスロットリングやOOM-killerなど、発生メカニズムやJavaVMのチューニング対処について。

Kenji Kazumura

June 05, 2023
Tweet

More Decks by Kenji Kazumura

Other Decks in Programming

Transcript

  1. © 2023 Fujitsu Limited
    2023/06/04
    数村憲治
    コンテナ環境での
    Javaチューニング
    JJUG CCC Spring 2023
    @kkzr

    View Slide

  2. © 2023 Fujitsu Limited
    自己紹介
    Jakarta EE 仕様策定委員
    MicroProfile ステコミ委員
    JCP Executive Committee メンバー
    Eclipse Foundation ボードディレクター
    EclipseCon、JakartaOne、JJUGなどで登壇
    2

    View Slide

  3. 本資料での注意事項
    © 2023 Fujitsu Limited
    本資料は、OpenJDK実装をベースに記載しています。
    単に、「JDK」、「Java」と記載している場合、
    OpenJDKの実装を意味していることがあります。
    OpenJDK 17は、17.0.7
    OpenJDK 8は、8u372
    の各バージョンを使用しています。
    マイナーバージョンが違う場合は、
    本資料の記載動作と違う場合があります。
    3

    View Slide

  4. アジェンダ
    © 2023 Fujitsu Limited
    Java エルゴノミクス
    Dockerでのリソース制御
    サマリ
    Kubernetesでのリソース制御
    4

    View Slide

  5. コンテナと、Javaエルゴノミクス
    © 2023 Fujitsu Limited
    コンテナ
    コンテナに割当てられた
    メモリ・CPUリソース
    整合性
    Java
    Javaが使えると思っている
    メモリ・CPUリソース
    コンテナ起動のたびに
    変わる可能性あり
    他のコンテナに影響される
    可能性あり
    エルゴノミクス
    または
    手動で設定
    5

    View Slide

  6. アジェンダ
    © 2023 Fujitsu Limited
    Java エルゴノミクス
    Dockerでのリソース制御
    サマリ
    Kubernetesでのリソース制御
    6

    View Slide

  7. Javaのエルゴノミクス
    © 2023 Fujitsu Limited
    GC種別
    Javaヒープサイズ
    GCスレッド数
    並行処理数
    ・・・
    Javaでのエルゴノミクスとは、各種パラメタを
    JVMが自身が動作する環境をベースに自動的に設定すること
    エルゴミクス対象

    7

    View Slide

  8. GC種別 (OpenJDK 8)
    © 2023 Fujitsu Limited
    利用可能
    GC
    エルゴノミクス
    対象
    条件
    Serial ✔
    Parallel ✔
    CMS N/A
    G1 N/A
    CPU数 < 2 || メモリ < 1792MB
    CPU数 ≧ 2 && メモリ ≧ 1792MB
    8

    View Slide

  9. 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

    View Slide

  10. コンテナ環境でのコア数
    © 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

    View Slide

  11. Javaヒープサイズ
    © 2023 Fujitsu Limited
    初期値:物理メモリの1/64
    最大値:物理メモリの1/4
    -XX:MaxRAMPercentage
    -Xmx
    エルゴノミクスによる設定
    -XX:InitialRAMPercentage
    -Xms
    気にいらなければ、自分で設定
    Javaヒープサイズの指定方法 (JDK 10 以降)
    11

    View Slide

  12. コンテナ環境での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

    View Slide

  13. 論理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

    View Slide

  14. アジェンダ
    © 2023 Fujitsu Limited
    Java エルゴノミクス
    Dockerでのリソース制御
    サマリ
    Kubernetesでのリソース制御
    14

    View Slide

  15. 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

    View Slide

  16. CPU1 CPU2
    CPU Shares (ビジー)
    © 2023 Fujitsu Limited
    A:B:C=2:1:1でCPU時間が配分される
    コンテナC
    share:1024
    コンテナB
    share:1024
    それぞれのコンテナがビジー状態であれば、
    コンテナA
    share:2048
    16

    View Slide

  17. CPU2
    アイドル
    CPU1
    CPU Shares (アイドルあり)
    © 2023 Fujitsu Limited
    コンテナB
    share:1024
    コンテナA、Cがアイドル状態になると・・・
    コンテナBがCPU時間を総どり
    コンテナC
    share:1024
    コンテナA
    share:2048
    17

    View Slide

  18. 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

    View Slide

  19. 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

    View Slide

  20. 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

    View Slide

  21. Java における CPU Share 注意事項
    © 2023 Fujitsu Limited
    実際にコンテナに割当てられるCPU時間は、
    他のコンテナの状態に依存する
    => 非決定的
    Javaプロセスからは、他のコンテナの状態はわからない
    => 変動するCPUを前提にプログラムするのは難しい
    21

    View Slide

  22. 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

    View Slide

  23. 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

    View Slide

  24. Docker CPU Quota
    © 2023 Fujitsu Limited
    quota : period間にコンテナが消費できるCPU時間(マイクロセカンド)
    runサブコマンドに、「--cpu-quota=」を指定
    quotaを使い切るとコンテナにCPUは割り当てられない
    CPUスロットリング
    period : デフォルトは100マイクロセカンド
    runサブコマンドに、「--cpu-period=」を指定
    24

    View Slide

  25. 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

    View Slide

  26. 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

    View Slide

  27. 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

    View Slide

  28. CPUスロットリング
    © 2023 Fujitsu Limited
    スレッド1
    スレッド2
    時間
    0μ 1000μ 2000μ
    quota=1000、period=1000 マイクロセカンドの場合
    スレッド3
    500μ消費
    500μ消費
    処理開始
    たとえ、他のコンテナがアイドル
    状態でも、CPUは割り当てられない
    処理開始
    処理開始
    Javaはマルチスレッド
    28

    View Slide

  29. 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

    View Slide

  30. CPU Quotaを設定すると、
    CPUスロットリングの可能性あり
    CPU Shareだけだと、
    実際のCPU割当が決定的にならない
    Javaでの注意事項 - CPUリソース
    © 2023 Fujitsu Limited
    ⚫ ベストエフォートでの対応
    ⚫ 問題発生時は、ActiveProcessorCountや、各種パラメタの
    手動設定も検討
    ⚫ Javaでは発生しやすい
    マルチスレッドで、知らない所でCPUを使っている
    ⚫ 性能調査が難しくなる可能性あり
    30

    View Slide

  31. Dockerでのメモリ割当て
    © 2023 Fujitsu Limited
    docker run --memory=2048mb
    docker run --memory-swap=2048mb
    コンテナへの物理メモリ割当て
    コンテナへの物理メモリ+スワップ割当て
    スワッピングをさけるめに、両者を同一にする


    31

    View Slide

  32. Javaでの注意事項 - メモリリソース
    © 2023 Fujitsu Limited
    -Xmxではなく、-XX:MaxRAMPercentageの方が安全
    docker inspectで、OOM killer発生を確認
    コンテナ割当メモリより、大きなヒープサイズ指定が可能
    OutOfMemoryErrorが出ない場合あり
    32

    View Slide

  33. アジェンダ
    © 2023 Fujitsu Limited
    Java エルゴノミクス
    Dockerでのリソース制御
    サマリ
    Kubernetesでのリソース制御
    33

    View Slide

  34. KubernetesでのCPUリソース割当て
    © 2023 Fujitsu Limited
    ⚫ Requests :スケジューリングに使用
    ⚫ Limits:CPU割当上限に使用
    RequestsとLimitsの2種類の設定
    RequestsとLimitsの両方を、
    コンテナごとに設定可能
    34

    View Slide

  35. スケジューリング
    © 2023 Fujitsu Limited
    CPU1
    ノード1 ノード2
    CPU4
    CPU2
    CPU3
    CPU1
    CPU4
    CPU2
    CPU3
    35

    View Slide

  36. スケジューリング
    © 2023 Fujitsu Limited
    Pod1
    Request
    3CPU
    デプロイ
    CPU1
    ノード1 ノード2
    CPU4
    CPU2
    CPU3
    CPU1
    CPU4
    CPU2
    CPU3
    36

    View Slide

  37. スケジューリング
    © 2023 Fujitsu Limited
    Pod1
    Request
    3CPU
    デプロイ
    CPU1
    ノード1 ノード2
    CPU4
    CPU2
    CPU3
    CPU1
    CPU4
    CPU2
    CPU3
    Pod1
    Pod1
    Pod1
    37

    View Slide

  38. スケジューリング
    © 2023 Fujitsu Limited
    Pod1
    Request
    3CPU
    デプロイ
    Pod2
    Request
    3CPU
    デプロイ
    CPU1
    ノード1 ノード2
    CPU4
    CPU2
    CPU3
    CPU1
    CPU4
    CPU2
    CPU3
    Pod1
    Pod1
    Pod1
    38

    View Slide

  39. スケジューリング
    © 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

    View Slide

  40. スケジューリング
    © 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

    View Slide

  41. 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

    View Slide

  42. Requestsの実装
    © 2023 Fujitsu Limited
    JDK 17では、デフォルトでは、プロセッサ数の計算に使用しない。
    JDK 8では、Limitsが指定されていなければ、プロセッサ数の計算
    に使用される。
    CPU Sharesなので、
    実際のCPU割当はRequest以上になることが可能
    Kubernetesの実装
    1CPU(=1000m)は、1024 CPU Sharesに換算
    Javaの実装
    42

    View Slide

  43. Limitsの実装
    © 2023 Fujitsu Limited
    変換されたquotaが、プロセッサ数の計算に使用される。
    ただし、利用可能なプロセッサ数を超えない。
    periodは、100ミリ秒
    quotaは、Limits値 X 100 (ミリ秒) に変換
    quotaを使い切ると、PodにCPUは割当てられない (CPUスロットリング)
    Kubernetesの実装
    Javaの実装
    43

    View Slide

  44. KubernetesでのCPUリソース設定
    © 2023 Fujitsu Limited
    指定しないと、CPUリソースが非決定的
    指定すると、CPUスロットリングになる可能性あり
    指定するか・しないかは、
    ノード内で他に何が動いているかによりけり
    指定すると、CPUリソース割り当ては流動的になる
    指定しないと、ノード内でのCPU割り当ては保証されない
    Requestsの指定
    Limitsの指定
    ---> 性能調査が難しくなるかも
    44

    View Slide

  45. 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

    View Slide

  46. KubernetesでのメモリRequests
    © 2023 Fujitsu Limited
    スケジューリングに使用、
    ノードに空きがなければPending
    Requests以上のメモリを使用可能
    ノードでメモリがなくなったとき、
    Requestsを超えたPodは、終了させられる(evicted)
    46

    View Slide

  47. メモリRequests
    © 2023 Fujitsu Limited
    Pod1
    Request
    4GB
    Pod2
    Request
    4GB
    デプロイ
    デプロイ
    2GB
    2GB
    ノード(8GB)
    47

    View Slide

  48. メモリRequests
    © 2023 Fujitsu Limited
    ノード(8GB)
    Pod1
    Request
    4GB
    Pod2
    Request
    4GB
    デプロイ
    デプロイ
    6GB
    2GB
    48

    View Slide

  49. メモリRequests
    © 2023 Fujitsu Limited
    ノード(8GB)
    Pod1
    Request
    4GB
    Pod2
    Request
    4GB
    デプロイ
    デプロイ
    ここで、Pod2がさらに1GB(合計3GB)使おうとすると。。。
    6GB
    2GB
    49

    View Slide

  50. メモリRequests
    © 2023 Fujitsu Limited
    ノード(8GB)
    Pod1
    Request
    4GB
    Pod2
    Request
    4GB
    デプロイ
    デプロイ
    2GB
    OOM-killed
    50

    View Slide

  51. メモリRequests
    © 2023 Fujitsu Limited
    ノード(8GB)
    Pod1
    Request
    4GB
    Pod2
    Request
    4GB
    デプロイ
    リスタート
    3GB
    51

    View Slide

  52. KubernetesでのメモリLimits
    © 2023 Fujitsu Limited
    メモリ割当上限に使用。
    Limits以上のメモリを使用すると、killされる。
    52

    View Slide

  53. Kubernetesでのメモリリソース設定
    © 2023 Fujitsu Limited
    上限を適切に設定する
    指定しいないと、ホスト上のメモリをベースに
    Javaのエルゴノミクスが作動
    Limitsと同じ値を設定する
    途中で移動・Pendingになることを防ぐ
    Requestsの指定
    Limitsの指定
    53

    View Slide

  54. アジェンダ
    © 2023 Fujitsu Limited
    Java エルゴノミクス
    Dockerでのリソース制御
    サマリ
    Kubernetesでのリソース制御
    54

    View Slide

  55. サマリ
    © 2023 Fujitsu Limited
    メモリ不足は、コンテナダウン(OOM-kill)ですぐわかる
    コンテナとJavaの整合性
    CPUスロットリングは、性能に影響するが検出が難しい
    ⚫ Share/Quotaの動作原理をふまえたJava設定
    ⚫ Javaは生来的に、マルチスレッド
    ActiveProcessor、コンパイラスレッド、GCスレッドチューニング
    ⚫ OOM-killされないようにRequests/Limitsを設定する
    ⚫ JavaヒープサイズはMaxRAMPercentageなどでOOM-kill防止
    ⚫ 使用するJavaの版で動作確認する(マイナーアップでの非互換あり)
    55

    View Slide

  56. © 2023 Fujitsu Limited
    Question?
    56

    View Slide

  57. Thank you
    © 2023 Fujitsu Limited

    View Slide