事象
Datadog にJVM のメトリクスをレポートするためにアプリケーション起動時に JMX 監視を有効にするようにしたら3回に1回くらいの確率でデプロイが失敗するようになった。
デプロイガチャ😂
状況整理
エラーメッセージ
Error: JMX connector server communication error: service:jmx:rmi://ip-xxx-xxx-xxx-xxx:7199
7199 はJMX用に割り当てたポート。
JMXの設定
デプロイはシェルスクリプトで Java プロセスを kill してから再起動している。
JMXの設定は再起動時に Java オプションから。
# !/bin/bash
set -ue
service_restart() {
ssh "$DEPLOY_HOST" 'pkill java; test $? -le 1'
echo "java process stopped"
START_JAVA_COMMAND=$(cat << EOS
nohup /path/to/app_start_script \
-Dconfig.file=/path/to/stage/conf/application.conf \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=7199 \
-Dcom.sun.management.jmxremote.rmi.port=7199 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
> /var/log/nohup.out \
2>&1 &
EOS
)
ssh "$DEPLOY_HOST" "$START_JAVA_COMMAND"
echo "java process started"
}
service_restart
app_start_script
は単に Java オプションを受け取ってアプリケーションを起動するスクリプト。
調査
調べてみたところ jmxremote が有効になっているとアプリケーションの再起動に失敗するという事例は結構あるみたいだった。
原因
どうやら Java プロセスを kill した後もしばらくの間、JMXに指定ポートを掴まれてしまっていることが原因みたいだった。
具体的には、再起動の時点でJMXに割り当てたいポートが LISTEN もしくは TIME_WAIT の状態なってしまっていた。
対策
ということなので netstat で JMXのプロセスを監視して解放されたことを確認してから Java アプリケーションを再起動させるようにシェルを改修した。
# !/bin/bash
set -ue
# JMXのポートが空くまで待機する関数
wait_until_port_opened() {
while :
do
if ! ssh "$DEPLOY_HOST" "netstat -an | grep 7199" > /dev/null
then
break
fi
sleep 1
done
}
service_restart() {
ssh "$DEPLOY_HOST" 'pkill java; test $? -le 1'
# JMXのポートが空くまで待機
wait_until_port_opened
echo "java process stopped"
START_JAVA_COMMAND=$(cat << EOS
nohup /path/to/app_start_script \
-Dconfig.file=/path/to/stage/conf/application.conf \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=7199 \
-Dcom.sun.management.jmxremote.rmi.port=7199 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
> /var/log/nohup.out \
2>&1 &
EOS
)
ssh "$DEPLOY_HOST" "$START_JAVA_COMMAND"
echo "java process started"
}
service_restart
他の解決策
いろいろ調べてる時に教えてもらったことだけれども、ライブラリを利用してJMXの設定にポートを利用しないというアプローチもあるようだった。
こちらはまたの機会に。