CLOVER🍀

That was when it all began.

nginxのTCPロードバランシングを試す

先日、nginxのHTTPでのロードバランシングを試しました。

nginxのHTTPロードバランシングを試す - CLOVER

次は、もっと汎用的にTCPでのロードバランシングをしてみたいと思います。なお、今回はTCPのみで扱いますが、UDPでのロードバランシングも可能なようです。

ドキュメント

主要に利用するモジュール的には、
Module ngx_stream_core_module


Module ngx_stream_upstream_module

の2つとなります。

また、こちらのドキュメントも参考にしました。

NGINX Docs | NGINX Load Balancing – TCP and UDP Load Balancer

MySQLへの接続をロードバランシングしてみよう

今回のTCP接続の対象として、MySQLサーバーを持ち出してみます。

MySQLサーバーが、次のように3台起動しているものとします。

  • server1 (172.17.0.2)
  • server2 (172.17.0.3)
  • server3 (172.17.0.4)

別にMySQLレプリケーションなどがしたいわけではないので、単純にそれぞれ別個で構築します。

で、別々のサーバーにアクセスしていることがわかるように、以下のようにテーブルを作成してデータを登録します。

-- server1
mysql> CREATE TABLE server(name VARCHAR(10), PRIMARY KEY(name));
mysql> INSERT INTO server VALUES('server1');

-- server2
mysql> CREATE TABLE server(name VARCHAR(10), PRIMARY KEY(name));
mysql> INSERT INTO server VALUES('server2');

-- server3
mysql> CREATE TABLE server(name VARCHAR(10), PRIMARY KEY(name));
mysql> INSERT INTO server VALUES('server3');

MySQL側の準備は、これでおしまい。

nginxにTCPロードバランシングの設定をする

では、用意したMySQLに対してロードバランシングするnginxの設定を書いてみます。

HTTPサーバーとして使う時は、httpディレクティブの配下に設定を書いていきますが、今回はstreamディレクティブを使用します。

このnginxは、ローカルで起動しているものとし、MySQLのロードバランシングには3306ポートを使用します。
/etc/nginx/stream-balancer.conf

stream {
    upstream backends {
        server 172.17.0.2:3306;
        server 172.17.0.3:3306;
        server 172.17.0.4:3306;
    }

    server {
        listen 3306;
        proxy_pass backends;
    }
}

upstreamディレクティブで名前を付けつつ、serverでバックエンドを設定します。また、そのあとにserverディレクティブのproxy_passでupstreamで決めた名前(ここではbackends)を指定します。

listenは、このバランサのリッスンポートですね。

ちなみにUDPで使う場合は、以下のようにlistenのあとに「udp」って書くみたいです。

listen 53 udp;

今回の稼働環境は、Debian packageインストールしたnginxとなっています。ここは、「/etc/nginx/nginx.conf」にincludeを追加しましょう。
/etc/nginx/nginx.conf

include /etc/nginx/stream-balancer.conf;

これでnginxを再起動します。

確認

それでは、確認してみましょう。

確認用のプログラムは、以下のものを用意。
current-server.groovy

@Grab('mysql:mysql-connector-java:6.0.5')
@GrabConfig(systemClassLoader = true)
import groovy.sql.Sql

Sql.withInstance('jdbc:mysql://localhost:3306/practice?useUnicode=true&characterEncoding=utf-8&characterSetResults=utf-8&useServerPrepStmts=true&useLocalSessionState=true&elideSetAutoCommits=true&alwaysSendSetIsolation=false&useSSL=false', 'kazuhira', 'password') { sql ->
  sql.eachRow('SELECT name FROM server') { row -> println(row.name) }
}

実行。

$ groovy current-server.groovy
server1
$ groovy current-server.groovy
server2
$ groovy current-server.groovy
server3
$ groovy current-server.groovy
server1
$ groovy current-server.groovy
server2
$ groovy current-server.groovy
server3

デフォルトの振り分けアルゴリズムラウンドロビンなので、アクセスごとに次のサーバーに順次振り分けられます。Least Connection、Hashなどの設定も可能です。

あと、ヘルスチェックなども気になるところですが、こちらは商用機能です(HTTPロードバランシングの時も同じ)。
health_check

アクセスログを出力しよう

ところで、この設定だとロードバランシングはできていますが、ログが残りません。

気になるところなので、設定してみましょう。

設定自体は、こちらを参照。
Module ngx_stream_log_module

stream/upstreamのログフォーマットで使える埋め込み変数は、以下のドキュメントの「Embedded Variables」を参照。
Module ngx_stream_upstream_module

今回は、サンプルに加え接続先を出力する$upstream_addrを使用してみました。

stream {
    log_format stream-basic '$remote_addr [$time_local] '
                            '$protocol $status $bytes_sent $bytes_received '
                            'upstream->$upstream_addr $session_time';
    access_log /var/log/nginx/stream-access.log stream-basic;

    upstream backends {
        server 172.17.0.2:3306;
        server 172.17.0.3:3306;
        server 172.17.0.4:3306;
    }

    server {
        listen 3306;
        proxy_pass backends;
    }
}

再起動後、確認してみます。この設定だと、「/var/log/nginx/stream-access.log」にアクセスログが出力されます。

172.17.0.1 [06/Nov/2016:10:17:30 +0000] TCP 200 1295 1169 upstream->172.17.0.2:3306 0.239
172.17.0.1 [06/Nov/2016:10:17:32 +0000] TCP 200 1295 1169 upstream->172.17.0.3:3306 0.195
172.17.0.1 [06/Nov/2016:10:17:35 +0000] TCP 200 1295 1169 upstream->172.17.0.4:3306 0.228
172.17.0.1 [06/Nov/2016:10:17:44 +0000] TCP 200 1295 1169 upstream->172.17.0.2:3306 0.168
172.17.0.1 [06/Nov/2016:10:17:47 +0000] TCP 200 1295 1169 upstream->172.17.0.3:3306 0.188
172.17.0.1 [06/Nov/2016:10:17:49 +0000] TCP 200 1295 1169 upstream->172.17.0.4:3306 0.174

こんなところですかねー。