twemproxy + Redis 環境を Docker で構築

twemproxy(別名 nutcracker) + Redis の環境を Docker で構築してみます。 (twemproxy は memcached・Redis 用の軽量なプロキシです)

使用した設定ファイル等は http://github.com/fits/try_samples/tree/master/blog/20151124/

Redis の Docker イメージを取得

Redis の Docker イメージは docker pull で取得する事にします。

$ docker pull redis

twemproxy の Docker イメージを構築

twemproxy の Docker イメージは、下記 Dockerfile を使って centos のイメージをベースに twemproxy のソースをビルドして作成する事にします。

Dockerfile
FROM centos

RUN yum -y update
RUN yum -y install make automake libtool git

RUN git clone https://github.com/twitter/twemproxy.git
RUN cd twemproxy && autoreconf -fvi && ./configure && make && make install
RUN rm -fr twemproxy

RUN yum clean all

上記 Dockerfile で docker build を実行すれば Docker イメージを作成できます。

Docker イメージの作成
$ docker build --no-cache -t sample/twemproxy:0.1 .

・・・
Successfully built ・・・

twemproxy のビルドで warning は出ましたが、Docker イメージの作成に成功しました。

twemproxy + Redis × 2 の実行

twemproxy と Redis 2台を個別の Docker コンテナで実行してみます。

twemproxy の設定ファイルで接続する Redis サーバーを <ホスト名 or IPアドレス>:<ポート番号>:<重み> で指定する必要があり、これが課題となります。

twemproxy 設定ファイル例
sample:
  ・・・
  redis: true
  servers:
   - 127.0.0.1:6380:1
   - 127.0.0.1:6381:1
   ・・・

今回は、起動スクリプトを使って twemproxy と Redis の Docker コンテナをまとめて起動するようにしてみました。

該当コンテナを docker start (コンテナを再起動) してみて、失敗した場合は docker run を行うようにしています。 (docker start し易いように --name=<コンテナ名> で名前を付けています)

また、コンテナ間の接続を容易にするため、docker run 時に -h <ホスト名> でホスト名も付けました。 (コンテナ名をそのままホスト名に使っています)

/vagrant/tmp/start_twemproxy (twemproxy + Redis コンテナの起動スクリプト
#!/bin/sh

INDENT="    "
CONF_DIR=/vagrant/tmp/conf
CONTAINER_CONF_DIR=/conf
TWEMPROXY_IMAGE=sample/twemproxy:0.1

CONTAINER_REDIS_LIST="redis1 redis2"
CONTAINER_TWEMPROXY=twemproxy1

REDIS_SERVERS=""

# Redis のコンテナを個々に起動
for s in $CONTAINER_REDIS_LIST
do
  if test -z `docker start $s`; then
    docker run --name=$s -h $s -d redis
  else
    echo start $s
  fi

  REDIS_SERVERS="$REDIS_SERVERS$INDENT- $s:6379:1\n"
done

# twemproxy の設定ファイルを生成
sed -e "s/#{SERVERS}/$REDIS_SERVERS/g" `dirname $0`/nutcracker.yml.tpl > $CONF_DIR/nutcracker.yml

# twemproxy のコンテナ起動
if test -z `docker start $CONTAINER_TWEMPROXY`; then
  docker run --name=$CONTAINER_TWEMPROXY -h $CONTAINER_TWEMPROXY -d -p 6379:6379 -v $CONF_DIR:$CONTAINER_CONF_DIR $TWEMPROXY_IMAGE nutcracker -c $CONTAINER_CONF_DIR/nutcracker.yml
else
  echo start $CONTAINER_TWEMPROXY
fi

twemproxy は nutcracker -c <設定ファイル> で起動しています。

また、twemproxy 設定ファイル(nutcracker.yml)は Redis サーバーの構成に合わせて下記テンプレートの #{SERVERS}sed で置き換えて生成するようにしています。

/vagrant/tmp/nutcracker.yml.tpl (twemproxy 設定ファイルのテンプレート)
sample:
  listen: 0.0.0.0:6379
  hash: fnv1a_64
  distribution: ketama
  timeout: 500
  redis: true
  servers:
#{SERVERS}

実行

それでは実行してみます。
初回起動時は docker start に失敗し docker run を実施する事になります。

twemproxy1, redis1, redis2 コンテナの実行例
$ /vagrant/tmp/start_twemproxy

Error response from daemon: no such id: redis1
Error: failed to start containers: [redis1]
bdc12db5・・・
Error response from daemon: no such id: redis2
Error: failed to start containers: [redis2]
fd4546fc・・・
Error response from daemon: no such id: twemproxy1
Error: failed to start containers: [twemproxy1]
87bb567e・・・

redis-cli で twemproxy1 へ接続する Docker コンテナを起動し (Redis の Docker イメージを使っています) 、動作確認してみます。

動作確認
$ docker run --rm -it redis redis-cli -h twemproxy1

twemproxy1:6379> set a 123
OK
twemproxy1:6379> get a
"123"

twemproxy は正常に動作しているようです。

redis1・2 へ直接接続するコンテナをそれぞれ実行し確認すると、値は redis2 の方に設定されていました。

$ docker run --rm -it redis redis-cli -h redis1
redis1:6379> keys *
(empty list or set)
redis1:6379> exit

$ docker run --rm -it redis redis-cli -h redis2
redis2:6379> keys *
1) "a"
redis2:6379> exit

備考 - Vagrant 利用時

Vagrant で起動したゲスト OS 上で Docker を実行している場合、Vagrantfile へ以下のような設定を追加すれば provision で各コンテナを起動しホスト OS から twemproxy へ接続できるようになります。

Vagrantfile
・・・
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
    ・・・
    # twemproxy 用のポートフォワード設定
    config.vm.network "forwarded_port", guest: 6379, host: 6379

    # twemproxy + redis の起動スクリプト実行
    config.vm.provision :shell, :inline => "/vagrant/tmp/start_twemproxy"
end
provision 実施例
> vagrant up --provision

・・・
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: start redis1
==> default: start redis2
==> default: start twemproxy1
ホスト OS からの接続例
> redis-cli
127.0.0.1:6379> get a
"123"