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アドレスを書いてもよいみたいです
また、「/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そうです。
その他、ヘルスチェックとかセッションスティッキーとかやってみたかったのですが、このあたりは商用の機能らしいです…。
エラー時のフェイルオーバー
バックエンドサーバーがダウンした時など、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
簡単に使えるのは便利ですね〜。でも、細かいところは商用機能、と。