ブレインズテクノロジーの加藤です。
今回は、Dockerコンテナ上で起動するjavaアプリケーションの性能情報の取得方法についてまとめます。
例としてElasticsearchを使用しましたが、javaで起動しているアプリケーションであれば何にでも応用が効くはずです。
経緯
あるお客様の環境で、Dockerコンテナ上にElasticsearchを起動してサービスを提供しているのですが、GC系のERRORが頻発するようになりました。
原因の特定と解決策の検討のため、jstatによる性能情報を取得しようとしたのですが、ホストから見たElasticsearchのプロセスIDが異なるからか、ホスト側からは取得ができず。
仕方なくDockerコンテナに入って取得しようとしたのですが、今度はDockerコンテナにjdkが入っていないため、インストールするところから始めました。
以下はその手順です。
Dockerコンテナにログインする
Elasticsearchが起動していること、およびプロセス名を確認します。
今回のプロセス名は"brave_jepsen"のようです。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3596bfd346d4 elasticsearch:latest "/docker-entrypoint.…" Less than a second ago Up 2 seconds 9200/tcp, 9300/tcp brave_jepsen
docker exec コマンドにより、elasticsearchのコンテナにログインします。
$ docker exec -it brave_jepsen bash root@3596bfd346d4:/usr/share/elasticsearch#
ログインできました。
Dockerコンテナにjdkをインストールする
試しにDockerコンテナの中で、jstatコマンドを実行してみます。
$ jstat
bash: jstat: command not found
入ってないですね。仕方ないのでインストールします。
インストールにはapt-getコマンドを使用します。
まずはapt-getを最新化しましょう。
$ apt-get update Ign:1 http://deb.debian.org/debian stretch InRelease Get:2 http://deb.debian.org/debian stretch-updates InRelease [91.0 kB] ・・・(略) Fetched 10.3 MB in 3s (3173 kB/s) Reading package lists... Done
ちなみに環境によっては、apt-getを実行しても失敗することがあります。
その際は、環境変数「http_proxy」にプロキシサーバを設定すれば通るかもしれません。
export http_proxy=${プロキシサーバのホスト}:${プロキシサーバのポート}
続いて、apt-fileをインストールします。
$ apt-get install -y apt-file Reading package lists... Done Building dependency tree Reading state information... Done ・・・(略) Setting up apt-file (3.1.4) ... The system-wide cache is empty. You may want to run 'apt-file update' as root to update the cache.
apt-fileをupdateしろと言っているので、仰せのままに実行します。
$ apt-file update Ign:1 http://deb.debian.org/debian stretch InRelease Get:2 http://security.debian.org stretch/updates InRelease [63.0 kB] ・・・(略) Building dependency tree Reading state information... Done 3 packages can be upgraded. Run 'apt list --upgradable' to see them.
apt-fileのsearchコマンドにより、jstatが含まれるパッケージを見つけます。
$ apt-file search jstat libmpj-java: /usr/bin/mpjstatus openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/bin/jstat openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/bin/jstatd openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/man/ja_JP.UTF-8/man1/jstat.1.gz openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/man/ja_JP.UTF-8/man1/jstatd.1.gz openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/man/man1/jstat.1.gz openjdk-8-jdk-headless: /usr/lib/jvm/java-8-openjdk-amd64/man/man1/jstatd.1.gz
「openjdk-8-jdk-headless」に含まれるようです。
なので対象のパッケージをインストールします。
$ apt-get install -y openjdk-8-jdk-head Reading package lists... Done Building dependency tree ・・・(略) update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/bin/wsgen to provide /usr/bin/wsgen (wsgen) in auto mode update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/bin/jcmd to provide /usr/bin/jcmd (jcmd) in auto mode
jpsコマンドにより、Elasticsearchのプロセスが表示されれば成功です。
elasticsearchの場合、プロセスIDは1になるようです。
$ jps -v 1 Elasticsearch -Xms2g -Xmx2g -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+AlwaysPreTouch -Xss1m -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djna.nosys=true -Djdk.io.permissionsUseCanonicalPath=true -Dio.netty.noUnsafe=true -Dio.netty.noKeySetOptimization=true -Dio.netty.recycler.maxCapacityPerThread=0 -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j.skipJansi=true -XX:+HeapDumpOnOutOfMemoryError -Des.path.home=/usr/share/elasticsearch 802 Jps -Dapplication.home=/usr/lib/jvm/java-8-openjdk-amd64 -Xms8m
Javaの性能情報取得
jstatコマンドにより性能情報を取得します。
使用可能なオプションの種類や、出力項目の詳細は、下記のオラクルのページに載っています。
今回は gccauseオプションを使用したいと思います。
コマンドの意味としては、"-h1000"で1000行ごとにヘッダーを表示。
途中の1でelasticsearchのプロセスIDを指定。
1000は、1秒ごとに1000回繰り返すことでその間の性能情報を取得することができます。
$ jstat -gccause -h1000 1 1000 > /usr/share/elasticsearch/logs/gccause.txt $ cat /usr/share/elasticsearch/logs/gccause.txt S0 S1 E O M CCS YGC YGCT FGC FGCT GCT LGCC GCC 100.00 0.00 98.21 0.53 93.29 83.32 2 0.115 2 0.037 0.152 CMS Final Remark No GC 100.00 0.00 98.21 0.53 93.29 83.32 2 0.115 2 0.037 0.152 CMS Final Remark No GC 100.00 0.00 98.21 0.53 93.29 83.32 2 0.115 2 0.037 0.152 CMS Final Remark No GC 100.00 0.00 98.21 0.53 93.29 83.32 2 0.115 2 0.037 0.152 CMS Final Remark No GC 100.00 0.00 98.21 0.53 93.29 83.32 2 0.115 2 0.037 0.152 CMS Final Remark No GC 100.00 0.00 98.21 0.53 93.29 83.32 2 0.115 2 0.037 0.152 CMS Final Remark No GC 100.00 0.00 98.21 0.53 93.29 83.32 2 0.115 2 0.037 0.152 CMS Final Remark No GC
GCログはDockerコンテナ内に出力されるので、volume mappingでホスト側と共有しておくと良いです。
ちなみにお客様の環境ではdocker-composeを使っています。
docker-compose.ymlの設定は以下のような感じです。
(抜粋) volumes: - /home/logs/elasticsearch:/usr/share/elasticsearch/logs
この設定をしておけば、ホスト側の「/home/logs/elasticsearch」ディレクトリにgccause.txtが出力されているはずです。
gccauseだと実行時刻が出力されないため、ホスト側で下記のようなコマンドを実行するといい感じです。
$ tail -f /home/logs/elasticsearch/gccause.txt | awk '{print strftime("%Y/%m/%d %H:%M:%S"), $0; fflush()}' > /home/logs/elasticsearch/gccause_new.txt (出力結果) 2018/02/23 15:54:50 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT LGCC GCC 2018/02/23 15:54:50 99.35 0.00 81.41 5.55 98.06 94.74 40 2.183 2 0.108 2.292 Allocation Failure No GC 2018/02/23 15:54:50 99.35 0.00 81.43 5.55 98.06 94.74 40 2.183 2 0.108 2.292 Allocation Failure No GC 2018/02/23 15:54:50 99.35 0.00 81.45 5.55 98.06 94.74 40 2.183 2 0.108 2.292 Allocation Failure No GC 2018/02/23 15:54:50 99.35 0.00 81.46 5.55 98.06 94.74 40 2.183 2 0.108 2.292 Allocation Failure No GC 2018/02/23 15:54:50 99.35 0.00 81.48 5.55 98.06 94.74 40 2.183 2 0.108 2.292 Allocation Failure No GC
終わりに
というわけで、無事にDockerコンテナで起動するElasticsearchの性能情報を取得できました。
冒頭でも書きましたが、javaアプリケーションであれば、Elasticsearch以外でもこの方法で取得できるはずです。
この記事が誰かの役に立てればいいと思うー。そだねー。
ブレインズテクノロジーでは「共に成長できる仲間」を募集中です
採用ページはこちら