Dockerコンテナで動くJVMアプリケーションに対して async-profiler を使ってみる
こんにちは。齋藤です。 今日は負荷試験の合間にブログを書いてみます。
前回やった async-profiler の記事 の続きで 今回はDocker コンテナ上で 動くJVMアプリケーションに対してprofilerを動かしてみます。 コンテナのOSはdebianです。
はじめに
今回はperformanceのメトリクスを取るために
--privileged
のパラメータを渡す必要があります。
Linux の perf eventを読みとる必要があるからです。
そのため、以下の形で特権モードで動かします。 なお、特権モードを有効にして本番稼働させるのは危険です。 十分に危険性を理解した上で行ってください。
docker run --privileged openjdk:8-jdk
また、Linux の perf eventにはコンテナホストの情報が含まれているため デフォルトでは無効化/制限されており、アプリケーションを起動する前にパラメータを弄る必要があります。 そのため、準備をしてから profilerを掛けてみます。
前提
前提を示しておきます。
- macOS Mojave version 10.14.3
- docker 18.09.2
- Docker Desktop 2.0.0.3 (31259)
その他の前提は以下の通り
$ docker version Client: Docker Engine - Community Version: 18.09.2 API version: 1.39 Go version: go1.10.8 Git commit: 6247962 Built: Sun Feb 10 04:12:39 2019 OS/Arch: darwin/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 18.09.2 API version: 1.39 (minimum version 1.12) Go version: go1.10.6 Git commit: 6247962 Built: Sun Feb 10 04:13:06 2019 OS/Arch: linux/amd64 Experimental: false $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE openjdk 8-jdk 5f8e49b0a018 4 weeks ago 624MB $ docker run openjdk:8-jdk cat /etc/os-release PRETTY_NAME="Debian GNU/Linux 9 (stretch)" NAME="Debian GNU/Linux" VERSION_ID="9" VERSION="9 (stretch)" ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" $ docker run openjdk:8-jdk java -version openjdk version "1.8.0_181" OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-2~deb9u1-b13) OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
準備
今回はコンテナイメージに手を入れずに実行したいため
以下のような entrypoint.sh
を初期化処理のために、コンテナホストに用意しておきます。
コンテナホストからgithubにアクセス出来る必要があります。
初期化処理の中で、perf eventが取れるようにしておきます。
#!bin/bash # linux perf eventが取れるようにしておく echo 1 > /proc/sys/kernel/perf_event_paranoid echo 0 > /proc/sys/kernel/kptr_restrict # 必要に応じてデバッグシンボルを入れておく。 # デバッグシンボルを入れておくと # JVMのメソッドのトレースも出来るようになります。 # 必要があれば入れておきましょう。 # apt update && apt install openjdk-8-dbg curl -s -L -o /tmp/async-profiler-1.5-linux-x64.tar.gz https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.5/async-profiler-1.5-linux-x64.tar.gz mkdir -p /tmp/profiler && tar xf /tmp/async-profiler-1.5-linux-x64.tar.gz -C /tmp/profiler exec $@
また、profileで使うJavaのアプリを用意しておきます。 今回は簡単なデモ用のアプリです。
$ cat Test.java import java.util.*; public class Test { public static void main(String[] args) throws Exception { while(true) { Thread.sleep(1000); Random r = new Random(); for(int i = 0; i < 1000; i++) { System.out.println(r.nextInt()); } } } }
dockerコンテナ上でprofileを取ってみる
今回はbashでインタラクティブに操作しながらprofileを取ります。 また、先程示した、entrypoint.shを使って起動させます。
$ docker run -it --privileged -v `pwd`/entrypoint.sh:/tmp/entrypoint.sh -v `pwd`/Test.java:/Test.java --entrypoint "/tmp/entrypoint.sh" openjdk:8-jdk bash
ここから先はdockerコンテナ内での操作です。
# デモ用のプログラムをコンパイル > javac Test.java > java Test > /dev/null & # JVMのPIDを調べる > jps 7 Jps 20 Test # 30秒間、profileを取るサンプル > /tmp/profiler/profiler.sh -d 30 -f `pwd`/profile.svg 60 > exit
svgの取得が終わったらコンテナを終了して コンテナからsvgを拾ってきてブラウザで表示してみましょう。
# コンテナからsvgを拾ってくる $ docker container cp <container-id>:/profile.svg .
ブラウザで拾ってきたSVGを表示すると、以下のようなFlame Graphが見れます。
以下の画像は、デバッグシンボルを入れた後に取ったメトリクスです。 黄色いバーが見えますが、JVMのメソッドが呼ばれている事がわかります。
まとめ
前回 はローカルのMacの端末でprofilerを取りましたが 今回の記事では、Dockerコンテナ上で動かしているアプリケーションに対して コンテナの中からprofilerを掛けることが出来ました。 JVMアプリケーションでボトルネックを調べたい時に試してみてください。
現在、自分達が作っているアプリケーションはECSなどで動いているので、他にも手順が必要なのですが 今回はここまでにしておきます。