SwarmもConsulも使わないシンプルなDockerクラスタを作る

前回の[「Dockerのネットワークをbridgeで外と繋げる」](http://qiita.com/kjtanaka/items/c2fd862ca91a63d74166)では、マシン１台を例に説明しましたが、複数台でも同じように外へ繋ぐことができます。ただ、ゲートウェイの設定部分に少だけ工夫が必要なので、そこだけ注意が必要です。

では参考に、下記のような構成を例にして説明します。

| Host     | IP Address    |
|----------|---------------|
| MacBook  | 192.168.33.1  |
| host1(VirtualBox)  | 192.168.33.10  |
| host2(VirtualBox)  | 192.168.33.11  |
| container1(on host1)  | 192.168.33.21  |
| container2(on host1)  | 192.168.33.22  |
| container3(on host2)  | 192.168.33.23  |
| container4(on host2)  | 192.168.33.24  |

## `Vagrantfile`を準備する

```rb

Vagrant.configure(2) do |config|

  config.vm.define "host1" do |host1|
    host1.vm.box = "ubuntu/trusty64"
    host1.vm.hostname = "host1"
    host1.vm.network "private_network", ip: "192.168.33.10"
  end

  config.vm.define "host2" do |host2|
    host2.vm.box = "ubuntu/trusty64"
    host2.vm.hostname = "host2"
    host2.vm.network "private_network", ip: "192.168.33.11"
  end

  config.vm.provider "virtualbox" do |v|
    v.memory = "2048"
    # ネットワークアダプタに変更を加える
    v.customize ['modifyvm', :id, '--nictype1', 'Am79C973']
    v.customize ['modifyvm', :id, '--nicpromisc1', 'allow-all']
    v.customize ['modifyvm', :id, '--nictype2', 'Am79C973']
    v.customize ['modifyvm', :id, '--nicpromisc2', 'allow-all']
  end

  # bridge-utilsをインストールする
  config.vm.provision "shell", inline: <<-SHELL
    apt-get update
    apt-get -y install bridge-utils
  SHELL
end
```

## Dockerマシンを作る

```bash
# host1とhost2を立ち上げます
$ vagrant up

# host1をDockerマシンにします
$ docker-machine create \
      --driver "generic" \
      --generic-ip-address 192.168.33.10 \
      --generic-ssh-user vagrant \
      --generic-ssh-key .vagrant/machines/host1/virtualbox/private_key \
      --generic-ssh-port 22 \
      host1

# host2をDockerマシンにします
$ docker-machine create \
      --driver "generic" \
      --generic-ip-address 192.168.33.11 \
      --generic-ssh-user vagrant \
      --generic-ssh-key .vagrant/machines/host2/virtualbox/private_key \
      --generic-ssh-port 22 \
      host2
```

## host1にブリッジ`docker1`とネットワーク`shared_nw`を作る

```bash
# host1にログインします
$ vagrant ssh host1

# eth1のIPアドレスを削除します
$ sudo ip addr del 192.168.33.10/24 dev eth1

# "shared_nw"と"docker1"を作ってサブネットとゲートウェイを
# 設定します
$ sudo docker network create \
    --driver bridge \
    --subnet=192.168.33.0/24 \
    --gateway=192.168.33.10 \
    --opt "com.docker.network.bridge.name"="docker1" \
    shared_nw

# docker1をeth1に接続します
$ sudo brctl addif docker1 eth1
```

ここで理解しておきたい点は、`docker network create`の際に`docker1`に192.168.33.10がアサインされることです（`ifconfig docker1`で確認できます）。次のステップでは少しだけ工夫して`host2`のコンテナに対してもこのIPアドレスをゲートウェイに指定します。

## host2にブリッジ`docker1`とネットワーク`shared_nw`を作る

```bash
# host2にログインします
$ vagrant ssh host2

# eth1のIPアドレスを削除します
$ sudo ip addr del 192.168.33.11/24 dev eth1

# "shared_nw"と"docker1"を作ってサブネットとゲートウェイを
# 設定します
$ sudo docker network create \
    --driver bridge \
    --subnet=192.168.33.0/24 \
    --gateway=192.168.33.10 \
    --opt "com.docker.network.bridge.name"="docker1" \
    shared_nw

# docker1にアサインされてしまった192.168.33.10を削除して
# 192.168.33.11をアサインします
$ sudo ip addr del 192.168.33.10/24 dev docker1
$ sudo ip addr add 192.168.33.11/24 dev docker1

# docker1をeth1に接続します
$ sudo brctl addif docker1 eth1
```

## コンテナを作る

host2からログアウトしてMacBookに戻ります。それからhost1とhost2の`shared_nw`上に固定IPでコンテナを作ります。環境の切替を簡単化するために`$on_host1`と`$on_host2`に設定を入れておくと便利です。

```bash
$ on_host1=$(docker-machine config host1) 
$ on_host2=$(docker-machine config host2) 
$ docker $on_host1 run --name container1 --net shared_nw --ip 192.168.33.21 -dt ubuntu
$ docker $on_host1 run --name container2 --net shared_nw --ip 192.168.33.22 -dt ubuntu
$ docker $on_host2 run --name container3 --net shared_nw --ip 192.168.33.23 -dt ubuntu
$ docker $on_host2 run --name container4 --net shared_nw --ip 192.168.33.24 -dt ubuntu
```

設定がうまく行っていれば、ちゃんとpingが通ります。試しにcontainer1のシェルから下記のような手順で確認できます。

```bash
$ docker $on_host1 ps | grep container1
# 例:container1のIDがf90891c5448eだった場合
$ docker $on_host1 exec -it f90891c5448e bash

$ ping -c 3 192.168.33.1 # --> macbook
$ ping -c 3 192.168.33.10 # --> host1
$ ping -c 3 192.168.33.11 # --> host2
$ ping -c 3 192.168.33.22 # --> container2
$ ping -c 3 192.168.33.23 # --> container3
$ ping -c 3 192.168.33.24 # --> container4
$ ping -c 3 www.google.com
```

この構成でいけば、ネットワークの見通しがすっきりしているので、オンプレミスのDocker導入がスムーズになりそうですね。

## ネットワークの設定を恒久的にする

再起動後も設定が残るようにするために、ここでも少し工夫が必要になります。まずは`host1`と`host2`の`/etc/network/interfaces`の`eth1`の設定を下記のように変更します。

```
auto eth1
iface eth1 inet manual
   pre-up ifconfig $IFACE up
   post-down ifconfig $IFACE down
```

それから`/etc/rc.local`の`exit 0`の前に下記のコマンドを付け足します。

```bash:host1
brctl addif docker1 eth1
```

```bash:host2
ip addr del 192.168.33.10/24 dev docker1
ip addr add 192.168.33.11/24 dev docker1
brctl addif docker1 eth1
```

実際に再起動して確認してみてください。

この程度のハックであれば、そんなにメンテナンスの手間にはならないと思うのですが、もう少しいい方法を知っている（あるいは見つけた）方はコメントを下さい。よろしくお願いします。

## 後片付け

テストが終わって要らなくなったDockerマシンは下記の手順で削除できます。

```bash
$ docker $on_host1 stop $(docker ps -aq)
$ docker $on_host1 rm $(docker ps -aq) 
$ docker $on_host2 stop $(docker ps -aq) 
$ docker $on_host2 rm $(docker ps -aq) 
$ docker-machine rm host1 
$ docker-machine rm host2 
$ vagrant destroy 
```

# 理解を深める

Dockerのbridgeについて理解を深めたい方は下記リンクの"Building your own bridge"と"How Docker networks a container"を読むと勉強になります。

- [Docker - Network Configuration](https://docs.docker.com/v1.5/articles/networking/)