この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。齋藤です。 今日は負荷試験の合間にブログを書いてみます。
前回やった 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などで動いているので、他にも手順が必要なのですが 今回はここまでにしておきます。