NGINX に追加された UDP ロードバランシングを試してみた

つい先日、 NGINX 公式ブログでアナウンスされた UDP ロードバランシング機能が気になっていたので、まだ正式リリース前ではありますが試してみました。

構成

UDP で代表的なプロトコルといえば DNS なので、今回は

graph LR Client --- NGINX NGINX --- dns1[Dnsmasq1
example.com A 127.0.0.10] NGINX --- dns2[Dnsmasq2
example.com A 127.0.0.20]

という構成を Docker Compose で作り、 NGINX に対して dig コマンドで example.com の名前解決クエリを送った時に、 127.0.0.10127.0.0.20 が交互に返ってくるかどうかを見ることにしました。この構成を作るに当たって必要なファイルは Github リポジトリとして公開しています。

準備

NGINX

まず、 UDP ロードバランシング機能が含まれている NGINX を用意する必要がありますが、現時点では開発リポジトリ上にのみ存在する状態のため( 1.9.13 で正式リリース予定)、 Mercurial 経由で

hg clone http://hg.nginx.org/nginx

としてソースコードを取得し、

cd nginx
./auto/configure --with-stream
make
make install

のように --with-stream オプションをつけてコンパイルする必要があります。今回はこれらの一連の NGINX のビルド処理を、 NGINX の Docker 公式リポジトリ の alpine タグの Dockerfile を元に手を加えた Dockerfile を作り、自動化しています。

NGINX が用意できたところで設定ですが、 UDP のみのロードバランシングの場合非常にシンプルなものになります

worker_processes  1;

events {
    worker_connections  1024;
}

stream {
    upstream dns_udp_upstreams {
        server ${DNS1_PORT_53_UDP_ADDR}:53;
        server ${DNS2_PORT_53_UDP_ADDR}:53;
    }

    server {
        listen 53 udp;
        proxy_pass dns_udp_upstreams;
        proxy_timeout 1s;
        proxy_responses 1;
    }
}

upstream の設定は通常と同じで、ポイントは server 部ですが、新たに listen ディレクティブに udp が指定できるようになっています。

また、 proxy_responses ディレクティブが追加され、それぞれのクライアント要求において、いくつアップストリームサーバから UDP パケットを受けとったかを数え、 proxy_responses と同じだけパケットを受けとった場合には接続を閉じる、という処理を定義できます。デフォルトは無制限で、その場合、 proxy_timeout の時間が経過するまで UDP パケットの到着を待ち続ける形となるようです。

Dnsmasq

実際の DNS リクエストに応答する Dnsmasq は andyshinn/dnsmasq の Docker イメージを使い、二つのサーバの起動オプションにシンプルに --host-record を追加し、

dnsmasq -k --host-record=example.com,127.0.0.10
dnsmasq -k --host-record=example.com,127.0.0.20

という形でそれぞれ立ち上がるよう設定することとしました。

Docker Compose

上記の NGINX, Dnsmasq の一連の処理を docker-compose.yml にまとめ、 docker-compose up で環境全体を構築できるようにしています。

動作確認

docker-compose up を実行して環境を構築後、 Docker Machine を使っている場合はリクエスト先の IP アドレスを docker-machine ip your-machine で調べておき、下記のようにコマンドを実行すると、交互に結果が返ってくることが確認できるかと思います。

$ dig @192.168.99.100 example.com +short
127.0.0.10
$ dig @192.168.99.100 example.com +short
127.0.0.20
$ dig @192.168.99.100 example.com +short
127.0.0.10

まとめ

非常にシンプルな設定で UDP ロードバランサが構築できることを確認できました。

これまで UDP ロードバランサを構築しようという場合には、ユーザーモードで動作するロードバランサ、リバースプロキシソフトウェアの選択肢がほぼ存在せず、たとえば Linux においては LVS を用いてのロードバランシングが一般的でした。しかしながら LVS はカーネルモードでの動作を必要とし、昨今隆盛を極めているコンテナ内では使用できないなど、 UDP をベースにしたプロトコルを持つサービスを構築する場合には、物理サーバまたは仮想マシンに頼らざるを得ない状況でした。

今回 NGINX による UDP ロードバランシング機能の提供により、他の TCP, HTTP などのプロトコルと同様に、 UDP もより自由度の高い形でロードバランサを構築することが可能となったことから、今後のサーバ構築時の選択肢を大きく変えていくのではないか、と期待しています