nginx の reuseport オプションとは
nginx 1.9.1 からの新機能で、 SO_REUSEPORT というソケットのオプションを有効化するもの。
https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
SO_REUSEPORT とは
Linux Kernel 3.9 以降の機能で、 1 つのポートを複数のプロセスで bind できるようにするもの。
SO_REUSEPORT (since Linux 3.9)
Permits multiple AF_INET or AF_INET6 sockets to be bound to an
identical socket address. This option must be set on each
socket (including the first socket) prior to calling bind(2)
on the socket. To prevent port hijacking, all of the
processes binding to the same address must have the same
effective UID. This option can be employed with both TCP and
UDP sockets.
(以下略)
つまり、同じポートを使う別のプロセスをにょきにょき建てられる。
なので、高速化とダウンタイムのないデプロイには便利。
Docker で試してみた。
メジャーなディストリビューションで 3.9 以上がデフォルトになってるのものは少ない。
ところで docker-machine の VirtualBox ドライバーが提供する boot2docker のカーネルは 2015 年 10 月 13 日現在、 Linux v4.1.10 (AUFS v4.1-20151012) 。
https://github.com/boot2docker/boot2docker/releases
これは行ける!と思ったので、自分の使ってる OS X 上で、 Docker on boot2docker な構成で試してみた。
せっかくなので docker-compose (元 fig) で楽をすることにした。
普通の nginx の使い方なら HUP シグナルを送れば reload (graceful restart) するので充分だが、
特に Docker の場合は「コンテナごと入れ替えたい」というケースが頻繁にありうるので、このケースとよりマッチしそうだ。
コンテナのクラスタごと Green-Blue-Deploy する場合とか。
用意したファイル。
以下の 3 つのみ。
- docker-compose.yml
- nginx の default.conf (reuseport を ON にしたもの。 HTTP の応答は単に "OK" とだけ返答する )
- test.sh (一括で実行するシェルスクリプト)
3 つの nginx プロセスをホストのネットワークを利用する設定で立ち上げる。
reuseport オプションが指定されているので、 3 つとも 80 番のポートで待ち受け、
早い者勝ちでコネクションを確立して普通の HTTP の応答をする。
Ctrl-C で終了。
nginx_1:
image: nginx
net: host
volumes:
- ./conf.d:/etc/nginx/conf.d
ports:
- "80:80"
nginx_2:
image: nginx
net: host
volumes:
- ./conf.d:/etc/nginx/conf.d
ports:
- "80:80"
nginx_3:
image: nginx
net: host
volumes:
- ./conf.d:/etc/nginx/conf.d
ports:
- "80:80"
server {
server_name localhost;
listen 80 reuseport;
location / {
return 200 'OK';
}
}
#!/bin/sh
MACHINE="$1"
eval "$(docker-machine env "$MACHINE")"
TARGET_HOST=$(docker-machine ip "$MACHINE")
docker-compose up -d
while sleep 1; do
curl -fsSL "$TARGET_HOST"
done > /dev/null &
SUB="$!"
docker-compose logs
kill "$SUB"
docker-compose stop
docker-compose rm -f
実行例
$ ./test.sh default
Creating nginx2_nginx_2_1...
Creating nginx2_nginx_3_1...
Creating nginx2_nginx_1_1...
Attaching to nginx2_nginx_1_1, nginx2_nginx_3_1, nginx2_nginx_2_1
nginx_3_1 | 192.168.99.1 - - [13/Oct/2015:12:30:54 +0000] "GET / HTTP/1.1" 200 2 "-" "curl/7.43.0" "-"
nginx_2_1 | 192.168.99.1 - - [13/Oct/2015:12:30:55 +0000] "GET / HTTP/1.1" 200 2 "-" "curl/7.43.0" "-"
nginx_3_1 | 192.168.99.1 - - [13/Oct/2015:12:30:56 +0000] "GET / HTTP/1.1" 200 2 "-" "curl/7.43.0" "-"
nginx_2_1 | 192.168.99.1 - - [13/Oct/2015:12:30:57 +0000] "GET / HTTP/1.1" 200 2 "-" "curl/7.43.0" "-"
nginx_1_1 | 192.168.99.1 - - [13/Oct/2015:12:30:58 +0000] "GET / HTTP/1.1" 200 2 "-" "curl/7.43.0" "-"
nginx_2_1 | 192.168.99.1 - - [13/Oct/2015:12:30:59 +0000] "GET / HTTP/1.1" 200 2 "-" "curl/7.43.0" "-"
nginx_1_1 | 192.168.99.1 - - [13/Oct/2015:12:31:00 +0000] "GET / HTTP/1.1" 200 2 "-" "curl/7.43.0" "-"
おー、 nginx1_1, nginx2_1, nginx3_1 が入れ替わりながら応答してる。イイカンジだ。
実際の使用例では、
- 新しいクラスタをデプロイ
- 旧いクラスタの nginx コンテナに QUIT シグナルを送る (graceful shutdown)
の順で行えば良いと思う。
余談
なお、 HAProxy は SO_REUSEPORT を自動で有効化してくれるらしい
https://github.com/haproxy/haproxy/blob/3cd0ae963e958d5d5fb838e120f1b0e9361a92f8/src/proto_tcp.c#L792-L798
http://diary.sorah.jp/2015/02/09/20150209