シングルインスタンスで Nginx を使った Spring Boot アプリのロードバランシング

便利だったので、忘れないように。

Spring Boot で Web アプリを開発すると、組み込まれたアプリケーションサーバーの効用により、
個別のポートでアプリケーション (と、一緒にアプリケーションサーバー) を起動することができます。
個別のポートでアプリケーションサーバーが起動するというのがミソで、
これを利用することで、1台のインスタンスでロードバランシングができるようになります。

ロードバランシングを、1台のインスタンスでする意義というのはいくつかあって、
以下の様なものがあると思っています。

- サーバーリソースを有効に使用するため
- 無停止デプロイをおこなうため

これらを実現するために、Nginx でアプリケーションサーバーを束ねて、
ロードバランシングしようというのが今回のお題です。

# 環境
- ConoHa byGMO 900円プラン (2016-04-19 時点)
  - メモリ 1GB
  - CPU 2コア
  - SSD 50GB
- Ubuntu 14.04 LTS
- Nginx 1.4.6
- Java 1.8.0
- Spring Boot 1.3.3

```
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"

$ nginx -V
nginx version: nginx/1.4.6 (Ubuntu)
built by gcc 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
TLS SNI support enabled

$ java -version
java version "1.8.0_77"
Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
Java HotSpot(TM) 64-Bit Server VM (build 25.77-b03, mixed mode)

$ cat pom.xml | grep '<parent>' -A 5
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
```

# Spring Boot アプリの作成
は、できている前提。
手元に Spring Boot アプリがまだ無い場合は以下から、
テンプレなどダウンロードしてきて、ささっと用意して下さい。
https://start.spring.io/

# Spring Boot アプリの起動
ここからが主題。
まず、アプリをパッケージングしておき、起動可能な状態にしておきましょう。

例として、Spring Initializr で `Artifact` が、"demo" であった場合を紹介します。
パッケージングは、Spring Initializr からプロジェクトを作成した場合、以下のように。

```
$ cd demo
$ ./mvnw clean package
$ ls target
classes            maven-archiver  demo-0.0.1-SNAPSHOT.jar
generated-sources  maven-status    demo-web-0.0.1-SNAPSHOT.jar.original
```

`demo-0.0.1-SNAPSHOT.jar` を使います。
今回は、同一アプリケーションをバランシングするので、3つ分起動しておきます。
ポートはそれぞれ、"8080"、"8081"、"8082" としておきます。

```
$ java -jar target/demo-0.0.1-SNAPSHOT.jar --server.port=8080 &
$ java -jar target/demo-0.0.1-SNAPSHOT.jar --server.port=8081 &
$ java -jar target/demo-0.0.1-SNAPSHOT.jar --server.port=8082 &
```

# Nginx でのロードバランシング設定
いよいよロードバランシングの設定をおこないます。
`/etc/nginx/sites-available/default` の設定は、
デフォルトのまま存在しているものとします。
新たに、`/etc/nginx/sites-available/demo` という設定ファイルを作成します。

```
$ vi /etc/nginx/sites-available/demo
```

`upstream` ディレクティブで、名前をつけて
バランシング先のアプリケーションサーバーを、列挙していきます。
今回は、"lb_demo" という名前をつけました。

次に `server` ディレクティブ内の、`location` ディレクティブで、
先ほどつけた名前を使用して、`proxy_pass` の設定をおこないます。

`proxy_pass http://lb_demo;` により、
"lb_demo" に設定されたアプリケーションサーバーに振り分けがおこなわれます。

```:/etc/nginx/sites-available/demo
upstream lb_demo {
	least_conn;
	server 127.0.0.1:8080;
	server 127.0.0.1:8081;
	server 127.0.0.1:8082 backup;
}

server {
	listen 80;
	listen [::]:80;

	root /usr/share/nginx/html;
	index index.html index.htm;

	# Make site accessible from http://localhost/
	server_name demo.local;

	proxy_redirect		off;
	proxy_set_header	Host				$host;
	proxy_set_header	X-Forwarded-For		$proxy_add_x_forwarded_for;
	proxy_set_header	X-Forwarded-Host	$host;
	proxy_set_header	X-Forwarded-Server	$host;
	proxy_set_header	X-Real-IP			$remote_addr;

	location / {
		proxy_pass http://lb_demo;
	}
}
```

それぞれの主要な設定値については、以下が分かりやすかったです。
http://og732.hatenadiary.com/entry/2013/05/08/225939

最後に設定を適用するため、`sites-enabled` にシンボリックリンクを作成して、
Nginx をリロードしておきましょう。

```
$ sudo ln -s /etc/nginx/sites-available/demo /etc/nginx/sites-enabled/demo
$ sudo service nginx reload
 * Reloading nginx configuration nginx                                   [ OK ]
```

と、こんな具合に設定を行うことで、
1台のインスタンスで、ロードバランシングの設定がおこなえます。

アプリの更新時など、やはりできればサービスは停止させたくないもの。
たとえインスタンスが1台しかなかったとしても、いろいろな工夫をこらしたい。