概要
これまでリモート経由でのメトリクスに対応していなかったmackerel-plugin-jvmがリモート経由でも対応されたと教えていただいたのでやってみました。
これが可能になれば、軽量さも重視されるコンテナにおいてmackerel-agentを入れることを回避できるようになりますね。
ECS上に構築したjavaアプリケーションと同じサービス内で起動した docker mackerel-agent から mackerel-plugin-jvmを使用してjavaアプリケーションのjvm関連のメトリクスの作成までを行います。
前提
以下のものは本記事では割愛します。
- ECSの使い方
- dockerの使い方
- docker-composeの使い方
- ecs-cliの使い方
- mackerelエージェントのインストール
- javaアプリケーションの詳細な中身
docker-composeの定義ファイルを作成する
jvm関連の値を取得するために必要な設定
javaアプリケーションを実行しているサーバー上にmackerel-agentをインストールする場合には意識することはなかったのですが、
他コンテナから取得させるには以下のものが必要となります。
- appコンテナ
- 1099ポート(デフォルト)のEXPOSE
- rmiregistry の起動
- jstatd の起動
- mackerel-agentコンテナ
- jdkのインストール(jps、jstat、jinfo)
- plugin-jvmの設定
- コンテナ間のlink
appコンテナ
1099ポートのEXPOSE
これはjava-appで定義しているDockerfileのEXPOSEに1099を追加するだけです。
FROM java:8-alpine
:
:
EXPOSE 9000 1099
※ アプリケーションの使用ポートを9000としています。
rmiregistry の起動
こちらはECS起動時に必要な内容となります。
以下のような内容をdocker-composeファイルのcommand
ブロックで定義してあげれば起動可能です。
eval 'rmiregistry -J-Djava.rmi.server.codebase=file://$${JAVA_HOME}/lib/tools.jar &'
jstatd の起動
まずはポリシーファイルを作成します。
このあたりは各社のポリシーに従って調整してください。
grant {
permission java.security.AllPermission;
};
※ こちらのpolicyファイルはDockerfile内でCOPYするか、docker-compose.ymlの command
ブロック内で出力させてあげてください。
jstatd
の起動も dokcer-composeファイルの command
ブロックに定義してやります。
eval 'jstatd -J-Djava.security.policy=/path/to/tools.policy -J-Djava.rmi.server.hostname=$${HOSTNAME} &'
1コンテナ1プロセスの考え方はそのうち…
mackerel-agentコンテナ
jdkのインストール
Dockerfileを作って管理するのが嫌だったので、こちらも docker-composeファイルの command
ブロックに記述してやります。
yum install -y java-1.8.0-openjdk-devel
plugin-jvmの設定
今回はDockerfileは作成しないので、 command
ブロックに以下のように追記します。
mkdir /etc/mackerel-agent/conf.d \
&& echo \"[plugin.metrics.jvm]\" > /etc/mackerel-agent/conf.d/jvm.conf \
&& echo 'command = \"mackerel-plugin-jvm -javaname=NettyServer -host=java-app\"' >> /etc/mackerel-agent/conf.d/jvm.conf \
&& echo 'display_name = \"java-app\"' >> /etc/mackerel-agent/mackerel-agent.conf
最終行でMackerelの管理画面上で見分けの付けやすいように名称を付与しています。
コンテナ間link
mackerel-agentのコンテナから名前解決でjava-appを探すために必要になります。
docker-composeファイルの links
ブロックに追記してやるだけです。
links:
- java-app
docker-composeの定義を書く
これらを踏まえてdocker-composeファイルを書いていきます。
version: '2'
services:
java-app:
mem_limit: 268435456
image: java-app:latest
user: quartette
environment:
- APP_ENV=dev
- JAVA_OPTS=-Xms128m -Xmx256m -Xmn64m -XX:MetaspaceSize=32m -XX:MaxMetaspaceSize=32m -Dfile.encoding=UTF-8 -Duser.country=JP -Duser.language=ja -Duser.timezone=Asia/Tokyo -Djava.awt.headless=true -XX:-OmitStackTraceInFastThrow
command:
/bin/sh -c
"eval 'rmiregistry -J-Djava.rmi.server.codebase=file://$${JAVA_HOME}/lib/tools.jar &' \
&& eval 'jstatd -J-Djava.security.policy=/path/to/tools.policy -J-Djava.rmi.server.hostname=$${HOSTNAME} &' \
&& /path/to/app.jar"
labels:
- Env=dev
logging:
driver: awslogs
options:
awslogs-group: "java-app"
awslogs-region: "ap-northeast-1"
awslogs-stream-prefix: "quartette"
mackerel-agent:
image: mackerel/mackerel-agent:latest
mem_limit: 134217728
command:
/bin/sh -c
"mkdir /etc/mackerel-agent/conf.d \
&& yum install -y java-1.8.0-openjdk-devel \
&& echo \"[plugin.metrics.jvm]\" > /etc/mackerel-agent/conf.d/jvm.conf \
&& echo 'command = \"mackerel-plugin-jvm -javaname=NettyServer -host=java-app\"' >> /etc/mackerel-agent/conf.d/jvm.conf \
&& echo 'display_name = \"java-app\"' >> /etc/mackerel-agent/mackerel-agent.conf \
&& /startup.sh
"
environment:
- apikey=[mackerel api key]
- auto_retirement=true
- enable_docker_plugin=true
- opts=-v
- include=/etc/mackerel-agent/conf.d/*.conf
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/lib/mackerel-agent/:/var/lib/mackerel-agent/
links:
- java-app
logging:
driver: awslogs
options:
awslogs-group: "mackerel-container-agent"
awslogs-region: "ap-northeast-1"
awslogs-stream-prefix: "quartette"
デプロイをして確認
ECSのサービスを更新する
私の環境では、 ecs-cli
を使ってデプロイをしているので以下のようにします。
ecs-cli compose -p java-app -f docker-compose.yml service up --container-name java-app --container-port 80 --roke ecsServiceRole --target-group-arn [targetGroupArn] --deployment-min-healthy-percent 50
Mackerel上で確認
上手く値が取得できていればこんな感じのグラフができているはずです。
グラフが表示されない場合
多くの場合、mackerel-agentからアプリケーションとの通信に失敗しているだけなのでコンテナにログインして確認をしましょう
mackerel-agent側からの確認
- plugin-jvm内で実行されているコマンドを直接実行する
mackerel-jvm-pluginではjps、jstat、jinfoあたりのコマンドが実行されています。
jpsとjstatの値が取れていればひとまずは問題ないと思います。
# jps java-app
5 RegistryImpl
6 Jstatd
7 NettyServer
# jstat -gc 7@java-app
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
30720.0 29184.0 1880.2 0.0 71168.0 44096.2 393216.0 283934.1 60032.0 56869.0 8320.0 7870.9 150 2.792 3 3.371 6.163
正常に設定できていればこんなような値が取得できるはずです。
- mackerel-jvm-pluginを直接実行する
jpsとjstatが取得できれば問題なくメトリクスが作成されるはずですが、plugin内でエラーになっている可能性もあるのでjvm-plugin も実行してみます。
正常に取得できるようであれば以下のような値が取得できます。
# mackerel-plugin-jvm -javaname=NettyServer -host=java-app
jvm.nettyserver.gc_events.YGC 0.000000 1512110013
jvm.nettyserver.gc_events.FGC 0.000000 1512110013
jvm.nettyserver.gc_time.YGCT 0.000000 1512110013
jvm.nettyserver.gc_time.FGCT 0.000000 1512110013
jvm.nettyserver.memorySpace.oldSpaceRate 72.208176 1512110013
jvm.nettyserver.memorySpace.newSpaceRate 11.839294 1512110013
jvm.nettyserver.gc_time_percentage.YGCT 0.000000 1512110013
jvm.nettyserver.gc_time_percentage.FGCT 0.000000 1512110013
jvm.nettyserver.new_space.NGCMX 134217728.000000 1512110013
jvm.nettyserver.new_space.NGC 134217728.000000 1512110013
jvm.nettyserver.new_space.EU 13965107.200000 1512110013
jvm.nettyserver.new_space.S0U 1925324.800000 1512110013
jvm.nettyserver.new_space.S1U 0.000000 1512110013
jvm.nettyserver.old_space.OGCMX 939524096.000000 1512110013
jvm.nettyserver.old_space.OGC 402653184.000000 1512110013
jvm.nettyserver.old_space.OU 290748518.400000 1512110013
jvm.nettyserver.metaspace.MCMX 1128267776.000000 1512110013
jvm.nettyserver.metaspace.MCMN 0.000000 1512110013
jvm.nettyserver.metaspace.MC 61472768.000000 1512110013
jvm.nettyserver.metaspace.MU 58233856.000000 1512110013
jvm.nettyserver.metaspace.CCSC 8519680.000000 1512110013
jvm.nettyserver.metaspace.CCSU 8059801.600000 1512110013
java-app側からの確認
- プロセスの確認
rmiregistryやjstadが起動しているかを確認します。
# ps
PID USER TIME COMMAND
1 quartette 0:00 /bin/sh -c eval 'rmiregistry -J-Djava.rmi.server.codebase=file://${JAVA_HOME}/lib/tools.jar &' && eval 'jstatd -J-Djava.security.policy
5 quartette 0:45 rmiregistry -J-Djava.rmi.server.codebase=file:///usr/lib/jvm/java-1.8-openjdk/lib/tools.jar
6 quartette 2:00 jstatd -J-Djava.security.policy=/home/tlab-app/tools.policy -J-Djava.rmi.server.hostname=cefd4fb34c63
7 quartette 7:06 /usr/lib/jvm/java-1.8-openjdk/bin/java -Xms128m -Xmx256m -Xmn32m -XX:MetaspaceSize=32m -XX:MaxMetaspaceSize=32m -Dfile.encoding=UTF-8
2461 quartette 0:00 /bin/bash
2466 quartette 0:00 ps
いくつか表示されていますが、PID 5、6の rmiregistry
と jstatd
が起動していれば問題ありません。
java-app側が問題ないはずなのにmackerel-agent側から値が取れない場合、EXPOSEでポートの指定が上手くいっていないか、コンテナ間リンクが上手くいっていない場合が多いです。
最後に
最近は運用をコンテナで行うことも増えてきて、コンテナ関連監視に悩んでいました。
コンテナ内にエージェントをインストールすることなく監視をしたい場合などにmackerelのコンテナベースのagentは便利だと思います。
DataDogやNewRelicなどでも同様のことはできるとは思いますが、Mackerelであれば日本語でのサポートも行ってくれるのでいいですね。