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が可能どうか検証してみようと思います。わっふるわっふる

このブログ記事について

このページは、Masahiro Naganoが2015年1月 7日 10:37に書いたブログ記事です。

ひとつ前のブログ記事は「「Webエンジニアが知っておきたいインフラの基本」はぜひこの冬休みにWebエンジニア・ディレクタに読んでみて欲しい一冊」です。

次のブログ記事は「 Docker と SO_REUSEPORT を組み合わせてコンテナのHot Deployにチャレンジ」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 4.27-ja