DockerのContainer linkとUnix Domain Socket、どっちのほうが速い？

概要
----

Dockerのコンテナ間通信の方法として

1. Container Link
2. Volume経由のUnix Domain Socket

といった方法がありますが、どっちの方が速いのか調べてみました

TL;DR
-----

Unix Domain Socketの方が速い

※当環境だと 8%程度

検証環境
--------

* Sinatra+Unicornが動くコンテナを作成、TCP:8080とUnix Domain SocketにてListen
* NginxなコンテナからTCP:8080とUnix Domain SocketをUpstream指定

上記環境に対し、ApacheBenchで `-c 10 -n 10000` を3回実行、平均で比較

```
NEC LaViE G Type Z
Intel(R) Core(TM) i7-4500U CPU @ 1.80GHz
MemTotal: 3966364 kB
Disk: Model=SAMSUNG MZMTD256HAGM-000L1
Ubuntu 14.04
ruby 2.2.2p95
```

※詳細は後ほど

結果
----

```
$ ab -n 10000 -c 100 http://172.17.0.104/tcp
Requests per second:    2382.71 [#/sec] (mean)
Time per request:       41.969 [ms] (mean)

Requests per second:    2348.47 [#/sec] (mean)
Time per request:       42.581 [ms] (mean)

Requests per second:    2349.65 [#/sec] (mean)
Time per request:       42.560 [ms] (mean)
```

```
$ ab -n 10000 -c 100 http://172.17.0.104/uds
Requests per second:    2574.83 [#/sec] (mean)
Time per request:       38.837 [ms] (mean)

Requests per second:    2538.66 [#/sec] (mean)
Time per request:       39.391 [ms] (mean)

Requests per second:    2554.32 [#/sec] (mean)
Time per request:       39.149 [ms] (mean)
```

**8%程度、Unix Domain Socketの方が速い**

詳細
----

下記を全て行うと `http://NGINX_CONTAINER/tcp` と `http://NGINX_CONTAINER/uds` というURLができあがり、それぞれTCP経由、Unix Domain Socket経由でSinatra+Unicornへアクセスします

### Sinatra+Unicorn Dockerコンテナ(HTTPd) ###

```:Dockerfile
FROM ubuntu:latest
MAINTAINER Kohei MATSUSHITA <ma2shita+git@ma2shita.jp>

RUN apt-get update
RUN apt-get install -y software-properties-common
RUN apt-add-repository -y ppa:brightbox/ruby-ng
RUN apt-get update
RUN apt-get install -y --no-install-recommends build-essential vim curl git ssh-client supervisor unzip rsync lsof ruby2.2 ruby2.2-dev libpq-dev
RUN apt-get clean
RUN gem install bundle rake --no-rdoc --no-ri

COPY . /tmp/sinatra/
WORKDIR /tmp/sinatra/app/
RUN bundle install

EXPOSE "8080"
CMD ["bundle", "exec", "unicorn", "-c", "./unicorn.rb"]
```

```app/Gemfile
source "https://rubygems.org"
gem "sinatra"
gem "unicorn"
```

```:config.ru
require "rubygems"
require "sinatra/base"
class HelloApp < Sinatra::Base
  get "/" do
    "Ok!!"
  end

  get "/uds" do
    "hello"
  end

  get "/tcp" do
    "hello"
  end
end
run HelloApp
```

```:app/unicorn.rb 
worker_processes 4
listen "0.0.0.0:8080"
Dir.mkdir "/dev/shm/unicorn/" rescue nil
listen "/dev/shm/unicorn/io.sock"
stderr_path "/dev/null"
stdout_path "/dev/null"
```

#### docker build & run ####

上記を用意した上で下記を実行

```
$ docker build --force-rm --tag sinatra .`
$ docker run --name sinatra --rm -v ${PWD}/unicorn.io:/dev/shm/unicorn sinatra
```

※`${PWD}/unicorn.io/` に `io.sock` というUnix Domain Socketができる(待ち受けている)


### Nginx ###

NginxはofficialのDocker imageを使用するため、Dockerfileは作りません

```:nginx_conf.d/default.conf
upstream uds_backend {
	server unix:/dev/shm/unicorn/io.sock;
}

upstream tcp_backend {
	server  172.17.0.103:8080;
}

server {
	access_log /dev/null;
	listen 80;
	server_name _;
	location /uds {
		proxy_pass http://uds_backend;
	}
	location /tcp {
		proxy_pass http://tcp_backend;
	}
}
```

※`tcp_backend`の server アドレスは、先に起動した sinatra コンテナのIPアドレスを書きます。`docker inspect httpd` で調べてください

#### docker run ####

```
$ docker run --name rproxy --link httpd:httpd -v ${PWD}/unicorn.io:/dev/shm/unicorn -v ${PWD}/nginx_conf.d:/etc/nginx/conf.d -p 80:80 --rm nginx
```

※`${PWD}/unicorn.io` は sinatra コンテナで指定したディレクトリと同じものを指定する必要があります (`io.sock`を共有)


あとがき
--------

* Volumeを使う場合、同一ホストである事が必須となってしまうので、そういうアプリケーションには良いかもしれない (nginxのように環境変数を読ませるのにTIPSが必要な状況下なら、なおさらgoodかもしれない)

こういうことができる人、本当に募集してます


いそがしい