CLOVER🍀

That was when it all began.

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

nginxで、HTTPで動作するアプリケーションサーバーをバックエンドに、ロードバランスの設定を書いてみます。

nginxで、HTTPのロードバランシングを行うには、「ngx_http_upstream_module」というモジュールを使用するようです。
Module ngx_http_upstream_module

これを使って、設定していってみましょう。

バックエンドサーバーを書く

まずは、ロードバランサの背後にいるバックエンドのサーバーを用意します。今回は、簡単にSpring Boot CLIで用意することにしましょう。以下の3つのサーバー名で、サーバーを用意します。

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

サーバー内で利用するスクリプトは、こんな感じ。
server.groovy

@RestController
class HelloController {
    def logger = org.slf4j.LoggerFactory.getLogger(getClass())

    @GetMapping('hello')
    def hello(javax.servlet.http.HttpServletRequest request) {
        logger.info(java.time.LocalDateTime.now().toString() + ": access " + request.requestURI)
        "Hello " + InetAddress.localHost.hostName + "!!" + System.lineSeparator()
    }
}

アクセスしたら「Hello [ホスト名]!!」を返却します。

起動。

$ spring run server.groovy

ロードバランサを設定する

それでは、この3つのサーバーの前段にnginxをロードバランサとして設定します。

この環境では、nginxはUbuntu LinuxにオフィシャルのDebian package(mainline)でインストールしているものとします。

で、設定。nginxでロードバランシング(というかプロキシ)を行うポートは、ローカルで8080とします。
/etc/nginx/conf.d/balancer.conf

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

server {
    listen 8080;
    server_name localhost;
    location / {
        proxy_pass http://backends;
    }
}

upstreamの次に名前をつけてバックエンドのサーバーを指定し、serverディレクティブで転送先を設定すればよいみたいですね。
※転送先が単一のサーバーの場合は、upstreamを定義せずにproxy_passにそのまま転送先のホスト名やIPアドレスを書いてもよいみたいです

upstream

server

また、「/etc/nginx/conf.d」配下に置いた*.confファイルは、nginxに「http { }」配下として読み込まれます。なので、意味的にはこういうことになります。

http {
    upstream backends {
        server 172.17.0.2:8080;
        server 172.17.0.3:8080;
        server 172.17.0.4:8080;
    }

    server {
        listen 8080;
        server_name localhost;

        location / {
            proxy_pass http://backends;
        }
    }
}

これで、nginxを再起動して確認。デフォルトはラウンドロビンでの振り分けのようなので、curlで連続して確認するとこんな感じになります。

$ curl http://localhost:8080/hello
Hello server1!!

$ curl http://localhost:8080/hello
Hello server2!!

$ curl http://localhost:8080/hello
Hello server3!!

$ curl http://localhost:8080/hello
Hello server1!!

$ curl http://localhost:8080/hello
Hello server2!!

$ curl http://localhost:8080/hello
Hello server3!!

OKそうです。

その他、ヘルスチェックとかセッションスティッキーとかやってみたかったのですが、このあたりは商用の機能らしいです…。

health_check

sticky

sticky_cookie_insert

エラー時のフェイルオーバー

バックエンドサーバーがダウンした時など、nginxはリクエストを別のサーバーに振り分けてくれます。

たとえば、今回の例ではserver3を落としてクライアントからアクセスしても、何事もなかったように接続できます。もちろん、server3はレスポンスを返しませんが。

$ curl http://localhost:8080/hello
Hello server1!!
$ curl http://localhost:8080/hello
Hello server2!!
$ curl http://localhost:8080/hello
Hello server1!!
$ curl http://localhost:8080/hello
Hello server2!!

この時、nginxのエラーログにはこんな内容が出力されています。

2016/11/05 09:14:48 [error] 93#93: *44 connect() failed (111: Connection refused) while connecting to upstream, client: 172.17.0.1, server: localhost, request: "GET /hello HTTP/1.1", upstream: "http://172.17.0.4:8080/hello", host: "localhost:8080"
2016/11/05 09:14:48 [warn] 93#93: *44 upstream server temporarily disabled while connecting to upstream, client: 172.17.0.1, server: localhost, request: "GET /hello HTTP/1.1", upstream: "http://172.17.0.4:8080/hello", host: "localhost:8080"

server3を復帰させると、その後は振り分け先に追加されます。

$ curl http://localhost:8080/hello
Hello server1!!
$ curl http://localhost:8080/hello
Hello server2!!
$ curl http://localhost:8080/hello
Hello server3!!

どのような条件でエラーハンドリングするかは、proxy_next_upstreamディレクティブで行うようです。
proxy_next_upstream

簡単に使えるのは便利ですね〜。でも、細かいところは商用機能、と。