Lambdaカクテル

ウェブアプリケーションエンジニアです.玉石混淆です.

Akka-remoteでNAT越え

Akka-remoteを利用して、NATを越えたメッセージングを実現するための方法。

Akka-remoteにおいてホスト名を指定するakka.remote.netty.tcp.hostnameには、マシンが認識する名前であるプライベートアドレスもしくはFQDNしか指定することができず、グローバルアドレスを指定することができない(註)。

そしてAkka-remoteはhostnameportが合致しなければメッセージに応答しない。 結果としてグローバルアドレスを指定した12.34.56.78宛の通信を無視してしまう。hostnameにWANアドレスを指定しても、インターフェースにbindすることができないので回線を開けず失敗し、こんなエラーを吐く。

Caused by: java.net.UnknownHostException: GLO.BAL.ADD.RESS: nodename nor servname provided, or not known

このためルータ越しの通信ができない。

これを解決する方法の目処が付いたので報告する。

註: グローバルアドレスがホスト名として指定されており、マシンがそれを正しく認識する場合はこの限りでない。

NATed環境下での通信路の確保

  • application.confもしくはreference.confでakka.remote.netty.tcp.use-passive-connections = onと設定する。

use-passive-connectionsonにすると、inbound時に張られた接続をoutbound時に再利用するようになる。これにより、Akka-remoteはhostnameの違いを無視し、hostnameにプライベートアドレスを指定した場合でもグローバルアドレスからのアクセスを受ける事が可能となる。

UPnPを利用した動的なポートマッピング

  • Scalaから利用できるuPnPライブラリを使う。

グローバルアドレスからの通信を受け付ける状態でも、ポートフォワードの為にルータ上の静的なNATテーブルを逐一編集するのは不便である。 この不便はuPnPによるポートマッピングで解決できる。これはLAN内部からNAT-Port Mapping Protocol(NAT-PMP)を利用することで実現できる。しかしこれを手動で行うのも手間だし不便だ。 そして便利なことに、Java/ScalaからuPnPにアクセスするライブラリが存在している。 これを利用してアプリケーションの起動時に動的にポートを解放させることで、簡単にAkka-remoteがリモートのノードと接続できるようになる。

Scalaから利用できるuPnPライブラリ

雑記

これを発見するまでに非常に長い時間がかかったし、これが全く思惑通りに動く保障もないので、心配なことしきりです。 分散処理って難しい。

あと早くIPv6普及しろ。

references