Slide 1

Slide 1 text

KubernetesでJavaアプリケーションを 動かすためのチューニングガイド 〜 LT編 @hhiroshell 1

Slide 2

Slide 2 text

宣伝 • 感染対策をしっかりやって遊舎⼯房に⾏きましょう 2 遊舎⼯房さんの店舗はこちら→ ↓現在の@hhiroshellのキーボードたち #crkbd ⾃⼰紹介 @hhiroshell 早川 博 (はやかわ ひろし) • Cloud Nativeなインフラを開発 するエンジニア。 Yahoo Japan Corporation 所属 • エンジニアコミュニティ 「Cloud Native Developers JP」 オーガナイザー • Developers Summit 2018 Japan Container Days 12.18 CloudNative Days Tokyo 2019, 2020, 2021 • ⾃作キーボード沼 BMEK

Slide 3

Slide 3 text

⽬次 1. イントロダクション 2. Kubernetesのリソース管理機能 3. Kubernetesのリソース管理を踏まえてJavaのチューニングを考える 4. まとめ 3

Slide 4

Slide 4 text

⽬次 1. イントロダクション 2. Kubernetesのリソース管理機能 3. Kubernetesのリソース管理を踏まえてJavaのチューニングを考える 4. まとめ 4

Slide 5

Slide 5 text

CloudNative時代のJavaエンジニアの悩み • Kubernetesのリソース設定は難しい…。ぶっちゃけ雰囲気で設定して いる。RequestsとLimits…?なんで2種類あるん? • Java(JVM)のチューニングするの(昔から)難しくないですか。いま だによくわかりません。変な設定したら怒られそうだし。 • つまりKubernetes x Javaはとても難しい。 Ϥγʂ 5

Slide 6

Slide 6 text

両⽅わかったらとても重宝されると思うんだ • というわけでやっていきましょうー。 6

Slide 7

Slide 7 text

⽬次 1. イントロダクション 2. Kubernetesのリソース管理機能 3. Kubernetesのリソース管理を踏まえてJavaのチューニングを考える 4. まとめ 7

Slide 8

Slide 8 text

Kubernetesにおけるリソース設定 • RequestsとLimitsとがある • Requests – コンテナのために最低限確保されるリソース • CPU / Memory / hugepages / ephemeral-storage • Requestsに指定したリソースを実際に使えることが保証 される • Limits – コンテナが利⽤できる最⼤のリソース • CPU / Memory / hugepages / ephemeral-storage • Nodeに空きがあるときに使⽤できる最⼤のリソース量 apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx:latest resources: requests: cpu: 0.5 memory: 1G limits: cpu: 1 memory: 2G 実利⽤量 Requests Limits 8

Slide 9

Slide 9 text

RequestsとLimitsにまつわるいろいろな挙動 – 1/3 • Podが起動するとき – コンテナのRequestsの合計がNodeの全体を超えないように、配置先のNodeが選ばれる – Nodeに余裕があってもRequestsの値で判定される • 起動しさえすればコンテナはRequestsまでリソースが使える NodeのCPU or Memory全体 Requestsの合計がNodeを超えないのでデプロイOK! 9

Slide 10

Slide 10 text

RequestsとLimitsにまつわるいろいろな挙動 – 2/3 • リソースの実使⽤量が⼤きくなってNodeが混んできたとき – コンテナ同⼠でリソースの消費量がバランスされる – QoSの優先度に従ってPodのEvictが起きる(他のNodeに退避させられる) • Nodeに空きが無ければLimitsまでのリソースが使えない NodeのCPU or Memory全体 QoSのランクが低いPodから 優先的にEvictされる 10

Slide 11

Slide 11 text

【参考】QoS(Quality of Service) Class • PodのEvictされやすさはQoS Classによって決まる • Guaranteed: – 全てのコンテナの、CPUとMemoryのLimitsとRequestが設定されており、 Limits=Requestsとなっている場合にこれになる – Limitsのみ設定するとRequestsに同じ値が設定されるため、CPUと MemoryのLimitsのみを指定した場合もGuaranteedになる • Burstable: – Guaranteed、BestEffortに当てはまらない場合これになる • BestEffort: – 全てのコンテナでLimitsとRequestが設定されていない場合にこれになる Evict されづらい Evict され易い 11

Slide 12

Slide 12 text

RequestsとLimitsにまつわるいろいろな挙動 – 3/3 • Limitsを超えたリソースを使おうとしたとき – CPUの場合:スロットリング • CPUの利⽤量がLimitの値を超えない状態で動作を継続 – メモリの場合:OOMKill • コンテナがKillされ、結果としてPodが再起動される • Nodeに余裕があってもLimitsを超えるリソースは使えない NodeのCPU or Memory全体 Limitsを超えないように 制御される 12

Slide 13

Slide 13 text

⽬次 1. イントロダクション 2. Kubernetesのリソース管理機能 3. Kubernetesのリソース管理を踏まえてJavaのチューニングを考える 4. まとめ 13

Slide 14

Slide 14 text

Kubernetesのリソース管理とJavaアプリの関係 • Javaアプリケーションが消費するリソースは下図のリソースの実使 ⽤量に当たる(当たり前のことではありますが…) – ⼀部のJVMパラメータはLimitsの設定値から⾃動調整される – 実使⽤量がRequestsやLimitsに収まるように、JVMをチューニングしたり(特に メモリ)、レプリカ数を増やして1レプリカあたりの負荷を下げたりする Requests Limits Javaアプリケーションが 消費するリソース 14

Slide 15

Slide 15 text

Limitsの設定から影響を受けるJVMの挙動 • Limitsの設定値を認識してJVMが⼀部の挙動を⾃動的に変える • CPU – Limitsの設定値がコンテナの --cpu-shares にマッピングされる。これをJVMが認 識して動作する – 結果として、Runtime.availableProcessors()の返り値や、ForkJoinプール、スレッ ドプールの割当量が変わる。これらに依存して挙動を変えているライブラリ やフレームワークも影響を受ける (e.g., core.async, ElasticSearch, Netty) • メモリ – JVMのヒープメモリが、Limitsに対してエルゴノミクスによって決定される – これに従ってGCアルゴリズム等も決まる 15

Slide 16

Slide 16 text

Javaアプリケーションが使うメモリの内訳 ① ヒープメモリ – ご存知ヒープメモリ。デフォルトではLimitsの値から動的に決定される(エルゴノミクス) ② スレッドスタック – 1スレッドごとに確保されるメモリ領域。デフォルトは1024K / Thread ③ コードキャッシュ – JITコンパイラによりコンパイルされたコードのキャッシュが保持される領域。デフォルトでは約240MB 消費メモリ合計 = ① ヒープメモリ + ② スレッドスタック + ③ コードキャッシュ + ④ メタスペース + ⑤ ダイレクトメモリ + ⑥ ネイティブメモリ 16

Slide 17

Slide 17 text

Javaアプリケーションが使うメモリの内訳 ④ メタスペース – クラスメタデータが配置される領域。デフォルトでは無制限 ⑤ ダイレクトメモリ – New I/Oダイレクトバッファで使う領域。デフォルトでは無制限。アプリの利⽤に合わせて適宜追加で確保 ⑥ ネイティブメモリ – ネイティブメソッドの実⾏に伴って消費される領域。上限値を設定することはできない 消費メモリ合計 = ① ヒープメモリ + ② スレッドスタック + ③ コードキャッシュ + ④ メタスペース + ⑤ ダイレクトメモリ + ⑥ ネイティブメモリ 17

Slide 18

Slide 18 text

ヒープメモリを調整しよう • デフォルトに任せるとヒープメモリの最⼤値がResource Limitsの20〜 30%となる(コンテナでない環境ならこれで良かったのかな?)。これだと多くの場合 Requests / Limitsに設定したメモリを使い切れない • -XX:MaxRAMPercentage=50.0 のように設定するとResource Limitsに対 する割合で指定できる Requests Limits ヒープメモリの上限に当たってこれ以上使えない 18

Slide 19

Slide 19 text

設定例 19 apiVersion: v1 kind: Pod metadata: name: openjdk8 spec: containers: - image: openjdk:11-jdk-slim name: openjdk # ...(snip)... resources: requests: memory: 256Mi cpu: 0.25 limits: memory: 1Gi cpu: 1 env: - name: JAVA_TOOL_OPTIONS value: “-XX:MaxRAMPercentage=50.0 -XX:MaxMetaspaceSize=128M -XX:MaxDirectMemorySize=10M"

Slide 20

Slide 20 text

チューニング時に意識すべきメモリ領域 • スレッドスタック、コードキャッシュは意外と消費が多いので、 ヒープメモリを除いた残りでこれが収まるように注意 – スレッドスタック: • Spring Bootでは最⼤コネクション数分のスレッド(デフォルト: 200)が⽣成される。 1024K * 200 = 205M に、他のスレッド分が加わる – コードキャッシュ: • デフォルトでは最⼤約240M • コンテナではそれほど⼤きいメモリを割り当てないことが多い。 Limitsが1GBならヒープは50%程度にしておくと安全 • 実際は測って決める 20

Slide 21

Slide 21 text

もっと本格的なチューニングは…。 • 実物で計測しながら各パラメータを調整して下さい…。m(_ _)m – > 推測するな、計測せよ c.f. https://qiita.com/e99h2121/items/e8f899756b21b0414835 • CloudFoundryのJava Buildpack Memory Calculatorも参考にできる – メモリパラメータの⾃動計算アルゴリズム公開されている 消費メモリ合計 = ① ヒープメモリ + ② スレッドスタック + ③ コードキャッシュ + ④ メタスペース + ⑤ ダイレクトメモリ + ⑥ ネイティブメモリ 21

Slide 22

Slide 22 text

⽬次 1. イントロダクション 2. Kubernetesのリソース管理機能 3. Kubernetesのリソース管理を踏まえてJavaのチューニングを考える 4. まとめ 22

Slide 23

Slide 23 text

まとめ • KubernetesのPodにはResource Limits / Requestsを設定できる – Requests: コンテナのために最低限確保されるリソース – Limits: コンテナが利⽤できる最⼤のリソース • JVMはResource Limitsの設定値を受けて⼀部パラメータを⾃動調整す る • ヒープメモリはデフォルトに任せると⼗分にメモリを使えないので、 最⼤値を引き上げるのがおすすめ • 実際のチューニングは実物で計測しながらやりましょう 23

Slide 24

Slide 24 text

Fin. 24

Slide 25

Slide 25 text

Appendix. 参考⽂献 25

Slide 26

Slide 26 text

【参考⽂献】 • Resource Management for Pods and Containers – https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ • Resource Requests and Limits Under the Hood: The Journey of a Pod Spec – https://speakerdeck.com/inductor/resource-requests-and-limits-under-the-hood-the- journey-of-a-pod-spec • Understanding CPU throttling in Kubernetes to improve application performance – https://speakerdeck.com/daikurosawa/understanding-cpu-throttling-in-kubernetes-to- improve-application-performance-number-k8sjp • Better Containerized JVMs in JDK10 – http://blog.gilliard.lol/2018/01/10/Java-in-containers-jdk10.html • CloudFoundry Java Buildpack Memory Calculator – https://github.com/cloudfoundry/java-buildpack-memory-calculator 26