SO_REUSEPORTはLinux Kernel 3.9からサポートされている機能で、複数のプロセス/Listenerから同じTCPポートをbind可能にして、Kernelが それぞれのプロセスに接続を分散してくれるという機能です。preforkなサーバはlistenしてからworkerをforkし、それぞれでacceptを行うという手順を踏みますが、SO_REUSEPORTを使えばその手順を踏まなくても複数プロセスから同じポートをListenして処理の並列性をあげたり、hot-depolyが実現できます。
Docker のHost networking機能とSO_REUSEPORTを使って、複数のコンテナから同じポートをbindできれば、コンテナのhot-deployができるんじゃないかと思ったので、試してみました。
SO_REUSEPORTについては以下のblogが参考になります。
DockerのHost networkingはdeeeetさんのblogがわかりやすい
Dockerなしで検証
まず、DockerなしでSO_REUSEPORTを試します。
サーバは Vagrant上のubuntu trusty
vagrant@vagrant-ubuntu-trusty-64:~$ uname -a
Linux vagrant-ubuntu-trusty-64 3.13.0-43-generic #72-Ubuntu SMP Mon Dec 8 19:35:06 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
検証はmemcahedを使います。簡単なので。。まず、memcachedにSO_REUSEPORT対応patchをあててbuildします。
patchはテキトウ
diff -ur memcached-1.4.22.orig/memcached.c memcached-1.4.22/memcached.c
--- memcached-1.4.22.orig/memcached.c 2015-01-01 16:50:52.000000000 +0900
+++ memcached-1.4.22/memcached.c 2015-01-06 22:48:07.000000000 +0900
@@ -4478,6 +4478,7 @@
#endif
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
+ setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&flags, sizeof(flags));
if (IS_UDP(transport)) {
maximize_sndbuf(sfd);
} else {
buildとmemcached起動
wget http://www.memcached.org/files/memcached-1.4.22.tar.gz
tar zxf memcached-1.4.22.tar.gz
cd memcached-1.4.22
cat /vagrant/reuseport.patch |patch -p1
./configure
make
./memcached -U 0 -p 11211 -l 127.0.0.1
localhostの11211をbindしました。UDPは無効にします
ここでもう一つ vagrant sshして同じポートでmemcachedを起動
./memcached -U 0 -p 11211 -l 127.0.0.1
同じポートなので、通常であればエラーになりますが、SO_REUSEPORTを有効にしているので普通に起動します。
この状態で、11211に接続し、1回setしたのち、接続をやりなおしながら複数回getを繰り返すと、片方のmemcachedには値が存在しないので、getが成功したり失敗したりを繰り返すはずです。
こんなperlのスクリプトを書いて
#!/usr/bin/env perl
use strict;
use warnings;
use 5.10.0;
use Cache::Memcached::Fast;
sub connect_memd {
Cache::Memcached::Fast->new({
servers => [qw/127.0.0.1:11211/]
});
}
connect_memd->flush_all for 1..10;
connect_memd->set("foo","bar");
for (1..10) {
my $r = connect_memd->get("foo") // "-";
say "$_, $r"
}
実行すると、
vagrant@vagrant-ubuntu-trusty-64:~$ perl /vagrant/test.pl
1, -
2, -
3, -
4, bar
5, -
6, bar
7, -
8, bar
9, bar
10, bar
11, bar
12, -
13, bar
14, -
15, -
16, -
17, bar
18, bar
19, -
20, bar
想定通りの動きとなりました。
Docker で SO_REUSEPORT
つぎはDockerでやってみます。
Dockerのバージョンは1.4.1
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ docker -v
Docker version 1.4.1, build 5bc2ff8
Dockerfileはこんな感じ、memcachedにpatchをあててbuild
FROM ubuntu:trusty
MAINTAINER Masahiro Nagano <kazeburo@gmail.com>
ENV DEBIAN_FRONTEND noninteractive
RUN locale-gen en_US.UTF-8 && dpkg-reconfigure locales
RUN apt-get update
RUN apt-get -y build-dep memcached
RUN apt-get -y install curl
RUN mkdir -p /opt
RUN curl -s http://www.memcached.org/files/memcached-1.4.22.tar.gz > /opt/memcached-1.4.22.tar.gz
RUN cd /opt && tar zxf memcached-1.4.22.tar.gz
COPY reuseport.patch /opt/memcached-1.4.22/reuseport.patch
WORKDIR /opt/memcached-1.4.22
RUN patch -p1 < reuseport.patch
RUN ./configure
RUN make
EXPOSE 11211
CMD ["/opt/memcached-1.4.22/memcached","-u","nobody","-U","0","-l","127.0.0.1","-p","11211"]
docker buildして起動
$ docker build -t memcached_reuseport .
$ docker run --net=host memcached_reuseport
別のターミナルから、もう一つコンテナを起動
$ docker run --net=host memcached_reuseport
おなじ11211を使いますが、普通に起動できました。
psコマンドで2つ起動していることを確認
15548 ? Ssl 1:10 /usr/bin/docker -d
329 ? Ss 0:00 \_ /bin/sh -c /opt/memcached-1.4.22/memcached -u nobody -U 0 -l 127.0.0.1 -p 11211
340 ? Sl 0:00 | \_ /opt/memcached-1.4.22/memcached -u nobody -U 0 -l 127.0.0.1 -p 11211
385 ? Ss 0:00 \_ /bin/sh -c /opt/memcached-1.4.22/memcached -u nobody -U 0 -l 127.0.0.1 -p 11211
397 ? Sl 0:00 \_ /opt/memcached-1.4.22/memcached -u nobody -U 0 -l 127.0.0.1 -p 11211
ssコマンドで同じポートをlistenしていることを確認
vagrant@vagrant-ubuntu-trusty-64:~$ sudo ss -ltp|grep 11211
LISTEN 0 128 127.0.0.1:11211 *:* users:(("memcached",397,26))
LISTEN 0 128 127.0.0.1:11211 *:* users:(("memcached",340,26))
ホストサーバにて、先ほどのperlスクリプトを実行します
vagrant@vagrant-ubuntu-trusty-64:/vagrant$ perl test.pl
1, bar
2, bar
3, bar
4, -
5, -
6, bar
7, bar
8, -
9, -
10, bar
11, -
12, bar
13, bar
14, -
15, -
16, -
17, bar
18, bar
19, bar
20, bar
同じような結果が得られました。Dockerのhost networking機能とSO_REUSEPORTは組み合わせて使えそうです。
ということで、次はSO_REUSEPORTを有効にしたアプリケーションサーバを起動してhot-deployが可能どうか検証してみようと思います。わっふるわっふる