Edited at

nginx の reuseport (SO_REUSEPORT) を試してみる。 docker-compose で

More than 3 years have passed since last update.


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 できるようにするもの。

http://man7.org/linux/man-pages/man7/socket.7.html


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 で終了。


docker-compose.yml

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"



conf.d/default.conf

server {

server_name localhost;
listen 80 reuseport;
location / {
return 200 'OK';
}
}


test.sh

#!/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