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

JRE しか入ってない Pod で Java の heap dump を取りたい / Get heap dump on JRE container

JRE しか入ってない Pod で Java の heap dump を取りたい / Get heap dump on JRE container

kota2and3kan

October 19, 2023
Tweet

More Decks by kota2and3kan

Other Decks in Technology

Transcript

  1. Who am I. Name: こたつ&&みかん Account: @kota2and3kan WorkAt: Scalar, Inc

    Job: [Technical Support, Infra Engineer] Like: DB: [PostgreSQL, CockroachDB, ScalarDB, ScalarDL] Bouldering: 5Q Dislike: Real Cockroach
  2. [前提] ちょっとだけ弊社のプロダクトを紹介 • ScalarDB ◦ 分散 Tx マネージャー。 ◦ 複数種類の

    DB を跨いだ Tx が実現できる。 ◦ 複数のマイクロサービスを跨いだ Tx も実現できる。 • ScalarDL ◦ DB に格納するデータの改ざん検知 (Byzantine Fault Detection) を可能にす るミドルウェア。 ◦ 内部で ScalarDB を利用しており、複数種類の DB に対応している。 • どちらもコンテナイメージを提供しており Kubernetes 上にデプロイできる。
  3. [前提] ちょっとだけ弊社のプロダクトを紹介 • どちらのプロダクトも VLDB (Database 分野のトップカンファレンスの 1つ) に論文が Accept

    されている。 ◦ ScalarDB ▪ https://www.vldb.org/pvldb/vol16/p3768-yamada.pdf ◦ ScalarDL ▪ https://www.vldb.org/pvldb/vol15/p1324-yamada.pdf
  4. 弊社製品の細かいことは 気にしなくてよいです              /) 
            ///) 
           /,.=゙''"/ 


       /     i f ,.r='"-‐'つ____   こまけぇこたぁいいんだよ!! 
   /      /   _,.-‐'~/⌒  ⌒\ 
     /   ,i   ,二ニ⊃( •). (•)\ 
    /    ノ    il゙フ::::::⌒(__人__)⌒::::: \ 
       ,イ「ト、  ,!,!|     |r┬-|     | 
      / iトヾヽ_/ィ"\      `ー'´     / 
 

  5. Pod Container 今日大事なこと -> Java で書かれている ScalarDL Ledger JRE Pod

    Container ScalarDB Cluster JRE Pod Container ScalarDL Auditor JRE
  6. 手元の検証環境で一旦検証 $ kubectl get pod NAME READY STATUS RESTARTS AGE

    scalardb-cluster-node-5bf4676577-gdtjs 1/1 Running 0 8m8s scalardb-cluster-node-5bf4676577-hp8jd 1/1 Running 0 8m8s scalardb-cluster-node-5bf4676577-l9fxp 1/1 Running 0 8m8s $ kubectl exec -it scalardb-cluster-node-5bf4676577-gdtjs -- bash $ jps bash: jps: command not found $ jcmd bash: jcmd: command not found
  7. • 正攻法は「shareProcessNamespace: true を設定して Pod を起動する」 「サイドカーとして JDK のコンテナを起動する」っぽい。 ◦

    https://speakerdeck.com/hhiroshell/jvm-on-kubernetes?slide=54 ◦ 既存の Pod に shareProcessNamespace を設定するには Pod の再起動が 必要。 ◦ 既存の Pod にサイドカーを追加するには Pod の再起動が必要。 ◦ 手元の環境で検証してみたところ、Java 11 では jcmd で heap dump を取得 できたが、Java 8 ではできなかった... (原因不明) いろいろ調べてみた結果 (1)
  8. • Kubernetes v1.26 以前の場合、「エフェメラルコンテナ (kubectl debug) で JDK のコンテナを起動する」という方法はできない模様。 ◦

    https://charleysdiary.hatenablog.com/entry/2019/12/19/033307 ◦ jps 等の利用には SYS_PTRACE の capability が必要だが、v1.26 ではエ フェメラルコンテナに SYS_PTRACE を設定する方法が無いため、この方法 はつかない模様。 いろいろ調べてみた結果 (2)
  9. • Kubernetes v1.27 以降の場合、「エフェメラルコンテナ (kubectl debug) で JDK のコンテナを起動する」という方法ができそう。 ◦

    v1.27 で kubectl debug に「--profile general」というオプションが追加されて いる。 ▪ https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.2 7.md ◦ この「--profile general」オプションを使うと、エフェメラルコンテナに SYS_PTRACE を設定できる。 ▪ https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/1441-kubectl-debu g#debugging-profiles ◦ ただし、このパターンでも「shareProcessNamespace: true」の設定は必要。 いろいろ調べてみた結果 (3)
  10. • Kubernetes v1.27 以降の場合、「エフェメラルコンテナ (kubectl debug) で JDK のコンテナを起動する」という方法ができそう。 ◦

    実際に v1.27 で検証したところ、Java 11 では jcmd で heap dump を 取得できたが、Java 8 ではできなかった... (原因不明) いろいろ調べてみた結果 (4)
  11. • 事象が発生している環境が Kubernetes v1.27 未満だったので、「エフェメ ラルコンテナ (kubectl debug --profile general)

    で JDK のコンテナを起動 する」という方法は使えない。 • 「サイドカーとして JDK のコンテナを起動する」方法を試してみたが、プロ ダクトが利用している Java 8 では何故かうまくいかなかった... (原因不明 / base image を Java 11 にしたらうまくいった) • そもそも Pod に「shareProcessNamespace: true」が設定されていなかっ たので、どちらの方法でも Pod の再作成は避けられない。 弊社プロダクトの場合は...
  12. • C 言語で書かれたツール。 • README 曰く、 ◦ The utility to

    send commands to a JVM process via Dynamic Attach mechanism. ◦ All-in-one jmap + jstack + jcmd + jinfo functionality in a single tiny program. ◦ No installed JDK required, works with just JRE. • この jattach をコンテナの中にコピーすれば heap dump が取れる (jcmd が実行できる) という情報があった。 jattach
  13. 検証 (jattach をダウンロード) $ curl -s -OL https://github.com/jattach/jattach/releases/download/v2.1/jattach $ chmod

    +x ./jattach $ ls -l ./jattach -rwxr-xr-x 1 ubuntu ubuntu 24850 Oct 6 19:19 ./jattach
  14. 検証 (Pod 内にコピーして --help を実行) $ kubectl cp ./jattach scalardb-cluster-node-5bf4676577-gdtjs:/tmp

    $ kubectl exec -it scalardb-cluster-node-5bf4676577-gdtjs -- bash $ /tmp/jattach --help jattach 2.1 built on Jul 25 2022 Copyright 2021 Andrei Pangin Usage: jattach <pid> <cmd> [args ...] Commands: load threaddump dumpheap setflag properties jcmd inspectheap datadump printflag agentProperties
  15. 検証 (PID を確認) $ ls -l /tmp/hsperfdata_scalardb/ total 32 -rw-------

    1 scalardb scalardb 32768 Oct 6 10:26 1 ※PID 1 問題については別途対応予定 https://text.superbrothers.dev/200328-how-to-avoid-pid-1-problem-in-kubernetes/
  16. 検証 (メモリの情報を取得) $ /tmp/jattach $(basename $(ls -1 /tmp/hsperfdata_scalardb/*)) jcmd "GC.heap_info"

    Connected to remote JVM JVM response code = 0 def new generation total 122368K, used 78283K [0x0000000369e00000, 0x00000003722c0000, 0x00000004dbea0000) eden space 108800K, 71% used [0x0000000369e00000, 0x000000036ea72ff0, 0x0000000370840000) from space 13568K, 0% used [0x0000000370840000, 0x0000000370840000, 0x0000000371580000) to space 13568K, 0% used [0x0000000371580000, 0x0000000371580000, 0x00000003722c0000) tenured generation total 271744K, used 25951K [0x00000004dbea0000, 0x00000004ec800000, 0x00000007c0000000) the space 271744K, 9% used [0x00000004dbea0000, 0x00000004dd7f7c48, 0x00000004dd7f7e00, 0x00000004ec800000) Metaspace used 40442K, capacity 41092K, committed 41472K, reserved 1085440K class space used 4642K, capacity 4828K, committed 4864K, reserved 1048576K
  17. 検証 (Heap Dump を取得) $ /tmp/jattach 1 jcmd "GC.heap_dump /tmp/heap_dump"

    Connected to remote JVM JVM response code = 0 File exists $ ls -l /tmp/heap_dump -rw------- 1 scalardb scalardb 22499468 Oct 6 10:31 /tmp/heap_dump
  18. 検証 (Heap Dump ローカルにダウンロード) $ kubectl cp scalardb-cluster-node-5bf4676577-gdtjs:/tmp/heap_dump ./download_heap_dump tar:

    Removing leading `/' from member names $ file ./download_heap_dump ./download_heap_dump: Java HPROF dump, created Fri Oct 6 10:31:15 2023
  19. 検証 (Heap Dump 解析) $ jhat ./download_heap_dump Reading from ./download_heap_dump...

    Dump file created Fri Oct 06 19:31:15 JST 2023 Snapshot read, resolving... Resolving 201222 objects... Chasing references, expect 40 dots........................................ Eliminating duplicate references........................................ Snapshot resolved. Started HTTP server on port 7000 Server is ready.
  20. おまけ (時間があれば) • jattach は C 言語で書かれているのに、動作要件が「JRE が入っているこ と」なのは何故なのか...? •

    そもそも C 言語でかかれたバイナリを他の環境 (ビルドした環境とは違う 環境) にコピーして使うことにちょっと違和感がある...
  21. シンプルな Hello World のバイナリと比較してみる。 $ gcc ./hello.c $ ./a.out Hello

    World $ ldd ./a.out linux-vdso.so.1 (0x00007ffd8397d000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff716fbb000) /lib64/ld-linux-x86-64.so.2 (0x00007ff7171f2000) おまけ (時間があれば) #include <stdio.h> int main() { printf("Hello World\n"); return 0; }
  22. おまけ (時間があれば) シンプルな Hello World と同じ共有ライブラリ、つまり libc にのみ依存している 感じだと思われる。 $

    ldd ./jattach linux-vdso.so.1 (0x00007ffeb55d7000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f14c1ba0000) /lib64/ld-linux-x86-64.so.2 (0x00007f14c1dd2000) $ ldd ./a.out linux-vdso.so.1 (0x00007ffd8397d000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff716fbb000) /lib64/ld-linux-x86-64.so.2 (0x00007ff7171f2000)
  23. • jattach は Hello World が動く環境であれば動きそうに見える (C の標準 Library である

    libc にしか依存してなさそう)。 • (推測)「JRE が入ってる環境」->「JVM が動く環境」->「libc が入ってる環境 (JVM は C 言語で書かれてるっぽいので)」という感じになるはずなので、 「JRE が入ってれば動く」という感じなのかもしれない。 • この辺りについては全然詳しくないので、C 言語のバイナリが動く条件等 について何かご存知の方がいれば教えていただけると嬉しいです。 おまけ (時間があれば)
  24. • Kubernetes 上の (JRE しか入ってない) Pod で jcmd 等のデバッグツー ルを使うための正攻法は「shareProcessNamespace:

    true を設定」かつ 「サイドカーとして JDK のコンテナを起動」だと思われる。 • Kubernetes v1.27 以降であれば「shareProcessNamespace: true を設 定」かつ「kubectl debug コマンドに『--profile general』オプションを付ける」 ことで、エフェメラルコンテナとして起動した JDK のコンテナから jcmd 等 のツールが実行できそう。 • 何故か Java 8 ではこれらの方法が使えなかったが、詳細不明... ◦ 何かご存じの方がいれば教えていただきたいです。 まとめ (1)
  25. • jattach を使うと JRE しか入ってない Pod の中で jcmd を実行できた。 •

    jattach は kubectl cp でバイナリを Pod 上にコピーするだけで使えるの で、Pod の再起動 (shareProcessNamespace の設定変更やサイドカー の追加) ができない場合でも heap dump が取れた。 • 普段 Java App on k8s をやってるみなさん、この辺どうしてますか? ◦ 他に良い方法がありそうな場合、教えていただけると嬉しいです。 まとめ (2)