18080でJMX接続を待つ踏み台越しの複数のJavaApplicationサーバにローカルマシンから同時に接続する

## 何の話？

踏み台越しだと面倒なJMX接続によるJVM監視の、さらに面倒くさい場合の設定例です。

NginxのTCP Proxy機能を使って、同時に接続します。

## 構成の前提

* APサーバがn台（それぞれのホスト名は、app-srv-XX　XX:01~n）
* APサーバ上のJVMは Port:18080 でJMX接続を待つ
* APサーバへの踏み台サーバが1台
* APサーバの18080番ポートが踏み台サーバから見える
* 踏み台サーバからは、JVVM監視用のポートとしては18080番ポートしか開放されていない
* APサーバの18080番ポートは踏み台サーバ以外からは見えない
* JMX接続クライアントは、ローカルPC（Windows 10マシンを想定、別になんでもいい）で動かす
* ローカルマシンは踏み台を経由しないとAPサーバが見えない

APサーバが増えたりしたときに一々踏み台のポートの設定の依頼をしたくない。

## 解決したいこと

JVMをJMXで監視するためには、通常、以下の引数を与えてJXM接続を待ち受けるサーバ機能を起動します。
なお、Java7以降でないと使えない引数があります。

APサーバapp-srv-01の場合です。

```
-Djava.rmi.server.hostname=`hostname`              # hostname=app-srv-01
-Dcom.sun.management.jmxremote.port=18080
-Dcom.sun.management.jmxremote.rmi.port=18080
-Dcom.sun.management.jmxremote.ssl=false           # 任意
-Dcom.sun.management.jmxremote.authenticate=false　# 任意
```

ここで注意したいのは、<code>java.rmi.server.hostname</code>です。

この設定は、接続してくるJMXクライアントに送信され、JMX接続先として、このホスト名を使います。

つまり、JMXクライアントから見て、<code>app-srv-01:18080</code>でJMX接続が見えないと繋げないということです。

踏み台を経由している場合、app-srv-XXが見えないので繋がりません。

### 1台の場合は簡単

1台の場合は、この問題はすぐに解決できます。

sshのConfigに以下のポートフォワードを設定して、hostsにapp-srv-XXを追加すれば良いだけです。

```text:~/.ssh/config
Host humidai-srv.net
     user  hoge-user
     IdentityFile    ~/.ssh/id_rsa.hoge
     LocalForward   18080 ap-srv-01:18080
```

```hosts:hosts
### app-srv-XX jmx ###
127.0.0.1  app-srv-01
```

これでローカルマシンでも<code>ap-srv-ap01:18080</code>がJMXの接続先として見える様になります。

なお、ローカルマシンの環境により、app-srv-01が既に別のIPとして解決されてしまっていたりする場合は、hostsとjvm引数で設定しているホスト名の頭に<code>jmx.</code>などのPrefixを付けてください。

### 複数台ある時が面倒

1台のときの設定のままでは、localhostの18080番ポートが、ap-srv-ap01のポートフォワードに使われてしまうので、2台目以降では18080番ポートをJMX接続先のポートとして使えなくなってしまいます。

この問題をどうにかして、複数のリモートJVMを監視したいです。

## 解決方法

今回はこの問題を、NginxのTCP Proxy機能を用いて解決します。

### 1台目の設定を修正 & n台目までのポートフォワードおよびHosts設定を追加

Nginxの設定の前に、ローカルマシンの18080番ポートを塞がないようにポートフォワードを以下のように修正します（5台目以降は省略しています）。

```text:~/.ssh/config
Host humidai-srv.net
     user  hoge-user
     IdentityFile    ~/.ssh/id_rsa.hoge
     LocalForward   18081 ap-srv-01:18080
     LocalForward   18082 ap-srv-02:18080
     LocalForward   18083 ap-srv-03:18080
     LocalForward   18084 ap-srv-04:18080
                   :
                   :
```

Hostsファイルにも追記します。全部引けるIPが違うことに注意してください。理由は後述します。

```text:hosts
### app-srv-XX jmx ###
127.0.0.1  app-srv-01
127.0.0.2  app-srv-02
127.0.0.3  app-srv-03
127.0.0.4  app-srv-04
          :
          :
```

### Nginxの設定

#### Versionおよび必要Module

普通に最新のものをインストールすれば問題ないですが、以下くらいのもので動きます。

```
nginx version: nginx/1.11.9
--with-stream=dynamic # 静的にインストールしててももちろんOK
```

#### Ngix Stream設定

Nginxの設定に以下を追記します。

Streamディレクティブを書くところはトップレベルでないといけないので、nginx.confに直接書きます。

```nginx:nginx.conf
stream {
    upstream up.app-srv-01 { server localhost:18081; }
    upstream up.app-srv-02 { server localhost:18082; }
    upstream up.app-srv-03 { server localhost:18083; }
    upstream up.app-srv-04 { server localhost:18084; }
                :
                :
    
    map $server_addr $upstr{
       127.0.0.1 "up.app-srv-01";
       127.0.0.2 "up.app-srv-02";
       127.0.0.3 "up.app-srv-03";
       127.0.0.4 "up.app-srv-04";
                  :
                  :
       default   "up.app-srv-01";
    }

    server {
       listen 18080;
       proxy_pass  $upstr;
    }
}
```

TCPプロクシの設定ですが、httpプロトコルと違ってserver_nameがヘッダに含まれていないので、server_nameを使っての転送先を指定することが出来ません。

なので、Hostsで各々違うローカルマシンのIPを割り当てることで、割り当てるべきUpstreamを決定します。

## 接続確認

java VisualVMのようなツールで、以下のJMX接続を確認します。

```
app-srv-01:18080
app-srv-02:18080
app-srv-03:18080
app-srv-04:18080
   :
   :
```

では、今回はこれまで。

## 関連

NginxのSteamモジュールはくっそ便利で、色々使えます。

常にSMTPをlocalhost:25で受けられるようにする - http://qiita.com/aya_eiya/items/dbd2ffff1f07a7bfedd6