マルチホストDockerネットワーキング（etcd+flannel）

マルチホストで動作するDockerネットワーキングツールについてのまとめ第二段。
（第一弾：[pipework＋GRE](http://qiita.com/nmatsui/items/fb7fc01dbba16b0ecb8e)）
（第三弾：[weave](http://qiita.com/nmatsui/items/a7caa5a3840b8c920124)）
今回は[coreos/flannel](https://github.com/coreos/flannel)を取り上げる。

# 検証環境
SoftLayerに以下のVirtual Serverを立ち上げて検証した。

||DC|hostname|private IP(eth0)|public IP(eth1)|
|:--|:--|:--|:--|:--|:--|
|ホスト1|Dallas 9|flannel01|10.142.51.197|X.X.X.X|
|ホスト2|Dallas 9|flannel02|10.142.51.198|Y.Y.Y.Y|
|ホスト3|Dallas 6|flannel03|10.106.91.131|Z.Z.Z.Z|

ホスト1とホスト2は同一VLAN上にあり、ホスト3は別VLAN上でホスト1,2とL3で接続されている。

各種バージョンは以下の通り。

| |version|
|:--|:--|
|distribution|Ubuntu 14.04.2 LTS|
|kernel|3.13.0-48-generic|
|docker|1.6.0|
|etcd|2.0.4+git|
|flannel|0.3.1+git|

# 準備
すべてのホストで以下の作業を実施する。

```text:必要パッケージのインストール
root@flannel01:~# apt-get update
root@flannel01:~# apt-get upgrade -y
root@flannel01:~# apt-get install build-essential linux-libc-dev bridge-utils git curl -y
```

```text:golangとdockerインストール
root@flannel01:~# wget -O - https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz | tar -C /usr/local -vxzf -
root@flannel01:~# echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile
root@flannel01:~# source /etc/profile
root@flannel01:~# wget -qO- https://get.docker.com/ | sh
```

```text:etcdのインストール
root@flannel01:~# git clone https://github.com/coreos/etcd.git /opt/coreos/etcd
root@flannel01:~# cd /opt/coreos/etcd
root@flannel01:/opt/coreos/etcd# ./build
root@flannel01:/opt/coreos/etcd# ln -s /opt/coreos/etcd/bin/etcd /usr/local/bin/etcd
root@flannel01:/opt/coreos/etcd# ln -s /opt/coreos/etcd/bin/etcdctl /usr/local/bin/etcdctl
```

```text:flannelのインストール
root@flannel01:~# git clone https://github.com/coreos/flannel.git /opt/coreos/flannel
root@flannel01:~# cd /opt/coreos/flannel 
root@flannel01:/opt/coreos/flannel# ./build
root@flannel01:/opt/coreos/flannel# ln -s /opt/coreos/flannel/bin/flanneld /usr/local/bin/flanneld
```

# etcdクラスタの構築

まずは以下の手順を参考に、全てのホストが参加するetcdクラスタを構築する。
https://github.com/coreos/etcd/blob/master/Documentation/clustering.md

etcdクラスタの起動はオプションが煩雑なので、bashスクリプトを作る。

```bash:start-etcd.sh
#!/bin/bash

if [ $# -ne 1 ]; then
  echo "usage: $0 nodename"
  exit 1
fi

declare -A NODES

NODES["flannel1"]="http://10.142.51.197:2380"
NODES["flannel2"]="http://10.142.51.198:2380"
NODES["flannel3"]="http://10.106.91.131:2380"

TOKEN="flannel-cluster"

if [[ "${NODES[$1]+_}" != "_" ]]; then
  echo "invalid nodename"
  exit 1
else
  NAME=$1
fi

CLUSTER=""

for i in ${!NODES[@]}; do
  if [ -n "$CLUSTER" ]; then
    CLUSTER=$CLUSTER","
  fi
  CLUSTER=$CLUSTER${i}=${NODES[$i]}
done

set -x

/usr/local/bin/etcd -name $NAME \
  -initial-advertise-peer-urls ${NODES[$NAME]} \
  -listen-peer-urls ${NODES[$NAME]} \
  -initial-cluster-token $TOKEN \
  -initial-cluster $CLUSTER \
  -initial-cluster-state new \
  > /var/log/etcd.log 2>&1 &

```

## ホスト１

```text:etcdの起動
root@flannel01:~# ./start-etcd.sh flannel1
root@flannel01:~# ps aux | grep etcd
root     12199  0.8  1.9  32436 19968 pts/0    Sl   01:13   0:03 /usr/local/bin/etcd -name flannel1 -initial-advertise-peer-urls http://10.142.51.197:2380 -listen-peer-urls http://10.142.51.197:2380 -initial-cluster-token flannel-cluster -initial-cluster flannel1=http://10.142.51.197:2380,flannel2=http://10.142.51.198:2380,flannel3=http://10.106.91.131:2380 -initial-cluster-state new
```

## ホスト２

```text:etcdの起動
root@flannel02:~# ./start-etcd.sh flannel2
root@flannel02:~# ps aux | grep etcd
root     10218  0.4  1.2  26988 12772 pts/0    Sl   01:17   0:00 /usr/local/bin/etcd -name flannel2 -initial-advertise-peer-urls http://10.142.51.198:2380 -listen-peer-urls http://10.142.51.198:2380 -initial-cluster-token flannel-cluster -initial-cluster flannel1=http://10.142.51.197:2380,flannel2=http://10.142.51.198:2380,flannel3=http://10.106.91.131:2380 -initial-cluster-state new
```

## ホスト３

```text:etcdの起動
root@flannel03:~# ./start-etcd.sh flannel3
root@flannel03:~# ps aux | grep etcd
root     10304  0.4  1.1  25900 11620 pts/0    Sl   01:18   0:00 /usr/local/bin/etcd -name flannel3 -initial-advertise-peer-urls http://10.106.91.131:2380 -listen-peer-urls http://10.106.91.131:2380 -initial-cluster-token flannel-cluster -initial-cluster flannel1=http://10.142.51.197:2380,flannel2=http://10.142.51.198:2380,flannel3=http://10.106.91.131:2380 -initial-cluster-state new
```

# flannel用の設定をetcdに投入

etcdにflannel用の設定を投入する。ホスト１で設定した情報はetcdクラスタで共有されるため、ホスト２やホスト３からも取得できる。

```text:etcdクラスタの状態確認
root@flannel01:~# etcdctl member list
12c356968a3f262f: name=flannel1 peerURLs=http://10.142.51.197:2380 clientURLs=http://localhost:2379,http://localhost:4001
7415c1f880800621: name=flannel3 peerURLs=http://10.106.91.131:2380 clientURLs=http://localhost:2379,http://localhost:4001
758e04f1ad0b9fef: name=flannel2 peerURLs=http://10.142.51.198:2380 clientURLs=http://localhost:2379,http://localhost:4001

root@flannel01:~# etcdctl cluster-health
cluster is healthy
member 12c356968a3f262f is healthy
member 7415c1f880800621 is healthy
member 758e04f1ad0b9fef is healthy
```

```text:flannelの設定投入
root@flannel01:~# etcdctl set /coreos.com/network/config '{"Network": "192.168.0.0/16", "SubnetLen": 24, "SubnetMin": "192.168.90.0", "SubnetMax": "192.168.99.0"}'
```

# flanneld起動

各ホストでflanneldを起動すると、etcdに設定した仮想ネットワーク**192.168.0.0/16**から指定した**/24**のサブネットが切りだされ、その設定が環境変数ファイル**/run/flannel/subnet.env**に書きだされる。

※SSHセッションが切断された場合、以下の起動手順だとflanneldは停止する。SSHセッションが切断される可能性が有る場合、nohupしたほうが良い。

## ホスト１

```text:flanneldの起動
root@flannel01:~# flanneld -iface=eth0 > /var/log/flannel.log 2>&1 &
root@flannel01:~# ps aux | grep flanneld
root     13531  0.0  0.3 260804  3944 pts/0    Sl   01:36   0:00 flanneld -iface=eth0

root@flannel01:~# cat /run/flannel/subnet.env 
FLANNEL_SUBNET=192.168.93.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=false
```

## ホスト２

```text:flanneldの起動
root@flannel02:~# flanneld -iface=eth0 > /var/log/flannel.log 2>&1 &
root@flannel02:~# ps aux | grep flanneld
root     11446  0.0  0.3 260804  3940 pts/0    Sl   01:38   0:00 flanneld -iface=eth0

root@flannel02:~# cat /run/flannel/subnet.env 
FLANNEL_SUBNET=192.168.97.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=false
```

## ホスト３

```text:flannelの起動
root@flannel03:~# flanneld -iface=eth0 > /var/log/flannel.log 2>&1 &
root@flannel03:~# ps aux | grep flanneld
root     11346  0.0  0.4 260804  4204 pts/0    Sl   01:38   0:00 flanneld -iface=eth0

root@flannel03:~# 
root@flannel03:~# cat /run/flannel/subnet.env 
FLANNEL_SUBNET=192.168.98.1/24
FLANNEL_MTU=1472
FLANNEL_IPMASQ=false
```

# docker0の設定変更

flanneldが出力した環境変数ファイルを用いて起動スクリプトを修正してDockerデーモンを再起動し、仮想ブリッジ**docker0**の設定を適切に変更する。

全てのホストで下記の手順を実行する。

```text:Dockerデーモン停止&docker0削除
root@flannel01:~# service docker stop
root@flannel01:~# ip link set dev docker0 down
root@flannel01:~# brctl delbr docker0
root@flannel01:~# iptables -t nat -F POSTROUTING
```

```text:Dockerの設定ファイル修正
root@flannel01:~# sed -i -e '/^FLANNEL.*/d' /etc/default/docker
root@flannel01:~# sed -i -e '/^DOCKER_OPTS="$DOCKER_OPTS --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}"$/d' /etc/default/docker
root@flannel01:~# cat /run/flannel/subnet.env >> /etc/default/docker
root@flannel01:~# echo 'DOCKER_OPTS="$DOCKER_OPTS --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}"' >>  /etc/default/docker
```

```text:Dockerデーモン起動
root@flannel01:~# service docker start
```

これにより、flannelが割り当てたサブネットを利用するようにdocker0の設定が変更される。

## ホスト１

```text:ホスト１のネットワーク
root@flannel01:~# ip addr show docker0
9: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
    inet 192.168.93.1/24 scope global docker0
       valid_lft forever preferred_lft forever
root@flannel01:~# ip addr show flannel0
8: flannel0: <POINTOPOINT,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500
    link/none 
    inet 192.168.93.0/16 scope global flannel0
       valid_lft forever preferred_lft forever
root@flannel01:~# ip route show
default via X.X.X.X dev eth1 
10.0.0.0/8 via 10.142.51.193 dev eth0 
10.142.51.192/26 dev eth0  proto kernel  scope link  src 10.142.51.197 
X.X.X.x/28 dev eth1  proto kernel  scope link  src X.X.X.X 
192.168.0.0/16 dev flannel0 
192.168.93.0/24 dev docker0  proto kernel  scope link  src 192.168.93.1 
```

## ホスト２

```text:ホスト２のネットワーク
root@flannel02:~# ip addr show docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
    inet 192.168.97.1/24 scope global docker0
       valid_lft forever preferred_lft forever
root@flannel02:~# ip addr show flannel0
6: flannel0: <POINTOPOINT,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500
    link/none 
    inet 192.168.97.0/16 scope global flannel0
       valid_lft forever preferred_lft forever
root@flannel02:~# ip route show
default via Y.Y.Y.Y dev eth1 
10.0.0.0/8 via 10.142.51.193 dev eth0 
10.142.51.192/26 dev eth0  proto kernel  scope link  src 10.142.51.198 
Y.Y.Y.y/28 dev eth1  proto kernel  scope link  src Y.Y.Y.Y
192.168.0.0/16 dev flannel0  proto kernel  scope link  src 192.168.97.0 
192.168.97.0/24 dev docker0  proto kernel  scope link  src 192.168.97.1 
````

## ホスト３

```text:ホスト３のネットワーク
root@flannel03:~# ip addr show docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
    inet 192.168.98.1/24 scope global docker0
       valid_lft forever preferred_lft forever
root@flannel03:~# ip addr show flannel0
6: flannel0: <POINTOPOINT,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN group default qlen 500
    link/none 
    inet 192.168.98.0/16 scope global flannel0
       valid_lft forever preferred_lft forever
root@flannel03:~# ip route show
default via Z.Z.Z.Z dev eth1 
10.0.0.0/8 via 10.106.91.129 dev eth0 
10.106.91.128/26 dev eth0  proto kernel  scope link  src 10.106.91.131 
Z.Z.Z.z/28 dev eth1  proto kernel  scope link  src Z.Z.Z.Z 
192.168.0.0/16 dev flannel0  proto kernel  scope link  src 192.168.98.0 
192.168.98.0/24 dev docker0  proto kernel  scope link  src 192.168.98.1 
```

ここまででdocker0のネットワークが適切に再設定され、**192.168.0.0/16**という仮想ネットワークが敷設される。

![docker_networking_3_01.png](https://qiita-image-store.s3.amazonaws.com/0/37648/1a10e726-e255-6b8a-3b02-59f92c7f261e.png)


# Dockerコンテナ起動

以下のようなコンテナを起動する。
falnnelはコンテナに割り振られるIPアドレスを指定することができないため、Dockerコンテナ起動後に`docker inspect`でIPアドレスを調べる。

|ホスト|コンテナ名|Dockerが割り当てたIPアドレス||
|:--|:--|:--|:--|
|ホスト1|httpd|192.168.93.2/24|apache2を起動|
|ホスト1|client1|192.168.93.3/24||
|ホスト2|client2|192.168.97.2/24||
|ホスト3|client3|192.168.98.2/24||

## ホスト１

```text:httpd起動
root@flannel01:~# HTTPD=$(docker run -d -i -t ubuntu:latest /bin/bash)
root@flannel01:~# docker inspect --format="{{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}" $HTTPD
192.168.93.2/24
```

```text:client1起動
root@flannel01:~# CLIENT1=$(docker run -d -i -t ubuntu:latest /bin/bash)
root@flannel01:~# docker inspect --format="{{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}" $CLIENT1
192.168.93.3/24
```

### httpdコンテナ

```text:httpdコンテナのネットワーク
root@flannel01:~# docker attach $HTTPD

root@7ba62b092066:/# ip addr show eth0
10: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:5d:02 brd ff:ff:ff:ff:ff:ff
    inet 192.168.93.2/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c0ff:fea8:5d02/64 scope link 
       valid_lft forever preferred_lft forever

root@7ba62b092066:/# ip route show
default via 192.168.93.1 dev eth0 
192.168.93.0/24 dev eth0  proto kernel  scope link  src 192.168.93.2
```

```text:apache2のインストール
root@7ba62b092066:/# apt-get install apache2 -y
root@7ba62b092066:/# apachectl start
```

### client1コンテナ

```text:client1コンテナのネットワーク
root@flannel01:~# docker attach $CLIENT1

root@0914ea0ca6ab:/# ip addr show eth0
12: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:5d:03 brd ff:ff:ff:ff:ff:ff
    inet 192.168.93.3/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c0ff:fea8:5d03/64 scope link 
       valid_lft forever preferred_lft forever

root@0914ea0ca6ab:/# ip route show
default via 192.168.93.1 dev eth0 
192.168.93.0/24 dev eth0  proto kernel  scope link  src 192.168.93.3 
```

```text:curlのインストール
root@0914ea0ca6ab:/# apt-get install curl -y
```

## ホスト２

```text:client2起動
root@flannel02:~# CLIENT2=$(docker run -d -i -t ubuntu:latest /bin/bash)
root@flannel02:~# docker inspect --format="{{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}" $CLIENT2
192.168.97.2/24
```

### client2コンテナ

```text:client2コンテナのネットワーク
root@flannel02:~# docker attach $CLIENT2

root@d875be3ae788:/# ip addr show eth0
8: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:61:02 brd ff:ff:ff:ff:ff:ff
    inet 192.168.97.2/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c0ff:fea8:6102/64 scope link 
       valid_lft forever preferred_lft forever

root@d875be3ae788:/# ip route show
default via 192.168.97.1 dev eth0 
192.168.97.0/24 dev eth0  proto kernel  scope link  src 192.168.97.2
```

```text:curlのインストール
root@d875be3ae788:/# apt-get install curl -y
```

## ホスト３

```text:client3起動
root@flannel03:~# CLIENT3=$(docker run -d -i -t ubuntu:latest /bin/bash)
root@flannel03:~# docker inspect --format="{{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}" $CLIENT3
192.168.98.2/24
```

### client3コンテナ

```text:client3コンテナのネットワーク
root@flannel03:~# docker attach $CLIENT3

root@6e553d2a2ad1:/# ip addr show eth0
9: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default 
    link/ether 02:42:c0:a8:62:02 brd ff:ff:ff:ff:ff:ff
    inet 192.168.98.2/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c0ff:fea8:6202/64 scope link 
       valid_lft forever preferred_lft forever

root@6e553d2a2ad1:/# ip route show
default via 192.168.98.1 dev eth0 
192.168.98.0/24 dev eth0  proto kernel  scope link  src 192.168.98.2 
```

```text:curlのインストール
root@6e553d2a2ad1:/# apt-get install curl -y
```

![docker_networking_3_02.png](https://qiita-image-store.s3.amazonaws.com/0/37648/f3724dc7-a9d9-37b2-f118-31394105296c.png)


# 疎通確認
ここまでで、複数ホストをまたがって敷設された192.168.0.0/16という仮想ネットワーク上に4つのコンテナが起動した。コンテナやホストから疎通確認を行う。

※パケットはflanneldがカプセル化しており、かつetcdがハートビートを頻繁に送り合っているため、tcpdumpを仕掛けてもどれがコンテナからのパケットなのか判別できない。。。

```
root@flannel01:~# tcpdump -v -i eth0 src host 10.142.51.198
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
04:44:10.100921 IP (tos 0x0, ttl 64, id 26599, offset 0, flags [DF], proto TCP (6), length 75)
    10.142.51.198.2380 > 10.142.51.197.36000: Flags [P.], cksum 0xa8e2 (correct), seq 2675:2698, ack 1, win 235, options [nop,nop,TS val 6265207 ecr 6596379], length 23
04:44:10.103224 IP (tos 0x0, ttl 64, id 51049, offset 0, flags [DF], proto TCP (6), length 52)
    10.142.51.198.46572 > 10.142.51.197.2380: Flags [.], cksum 0x5e92 (correct), ack 9388, win 257, options [nop,nop,TS val 6265207 ecr 6596504], length 0
04:44:10.103439 IP (tos 0x0, ttl 64, id 26600, offset 0, flags [DF], proto TCP (6), length 75)
    10.142.51.198.2380 > 10.142.51.197.36000: Flags [P.], cksum 0xa74f (correct), seq 2698:2721, ack 1, win 235, options [nop,nop,TS val 6265207 ecr 6596503], length 23
04:44:10.105420 IP (tos 0x0, ttl 64, id 51050, offset 0, flags [DF], proto TCP (6), length 52)
    10.142.51.198.46572 > 10.142.51.197.2380: Flags [.], cksum 0x5e54 (correct), ack 9449, win 257, options [nop,nop,TS val 6265208 ecr 6596504], length 0
...
```

## ホスト１ → httpdコンテナ

httpdコンテナはホスト1上の仮想ブリッジdocker0につながっているため、そのまま通信できる。

```text:ping(ホスト１→httpdコンテナ)
root@flannel01:~# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=64 time=0.038 ms
...
```

```text:HTTP(ホスト１→httpdコンテナ）
root@flannel01:~# curl 192.168.93.2

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
```

## client1コンテナ → httpdコンテナ

client1コンテナとhttpdコンテナは同じ仮想ブリッジdocker0につながっているため、そのまま通信できる。

```text:ping(client1コンテナ→httpdコンテナ）
root@0914ea0ca6ab:/# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=64 time=0.059 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=64 time=0.050 ms
...
```

```text:HTTP(client1コンテナ→httpdコンテナ）
root@0914ea0ca6ab:/# curl 192.168.93.2

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
```

## ホスト2 → httpdコンテナ

flanneldによってパケットがラップされ、同一VLANのホスト１上のhttpdコンテナと通信できる。

```text:ping(ホスト2→httpdコンテナ)
root@flannel02:~# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=61 time=0.616 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=61 time=0.317 ms
...
```

```text:HTTP(ホスト2→httpdコンテナ)
root@flannel02:~# curl 192.168.93.2

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
```

## client2コンテナ → httpdコンテナ

flanneldによってパケットがラップされ、同一VLANのホスト１上のhttpdコンテナと通信できる。

```text:ping(client2コンテナ→httpdコンテナ)
root@d875be3ae788:/# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=60 time=0.728 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=60 time=0.314 ms
...
```

```text:HTTP(client2コンテナ→httpdコンテナ)
root@d875be3ae788:/# curl 192.168.93.2

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
```

## ホスト3 → httpdコンテナ

flanneldによってパケットがラップされ、異なるVLANのホスト１上のhttpdコンテナと通信できる。

```text:ping(ホスト3→httpdコンテナ)
root@flannel03:~# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=61 time=1.82 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=61 time=1.52 ms
...
```

```text:HTTP(ホスト3→httpdコンテナ)
root@flannel03:~# curl 192.168.93.2

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
```

## clien3コンテナ → httpdコンテナ

flanneldによってパケットがラップされ、異なるVLANのホスト１上のhttpdコンテナと通信できる。

```text:ping(client3コンテナ→httpdコンテナ)
root@6e553d2a2ad1:/# ping 192.168.93.2
PING 192.168.93.2 (192.168.93.2) 56(84) bytes of data.
64 bytes from 192.168.93.2: icmp_seq=1 ttl=60 time=1.86 ms
64 bytes from 192.168.93.2: icmp_seq=2 ttl=60 time=1.62 ms
...
```

```text:HTTP(client3コンテナ→httpdコンテナ)
root@6e553d2a2ad1:/# curl 192.168.93.2

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...
```

# まとめ
etcdとflannelを用いることで、L2 & L3で接続された複数ホスト間に仮想ネットワークを敷設してDockerコンテナを所属させ、相互に通信させることができた。
ただしetcd+flannelはDockerコンテナのIPアドレスを明示的に指定する（あるいはDHCPを指定する）ことができないため、注意が必要である。