CoreOS + flannel で複数ホスト間コンテナ通信をする

CoreOS が提供する [flannel](https://github.com/coreos/flannel) を使って、複数の CoreOS マシンを跨いで Docker コンテナ同士通信できるようにする、というお話です。

## flannel

もともと [Kubernates](https://github.com/googlecloudplatform/kubernetes) には似たようなホスト間通信機能がついていたのですが、Google Compute Engine (GCE) 上でしか使えないという欠点がありました。これを取り出し、汎用的に使えるようにしたのが flannel です。

また、似た機能を持つものに [weave](https://github.com/zettio/weave) がありますが、weave は導入が少々面倒な上に相手ホストの IP を明示的に指定してあげないといけません。その点 flannel は CoreOS 上での導入が簡単で、相手の IP を知らなくてもよく透過的に利用できるという利点があります。ホストに割り振られる IP が不定である EC2 + VPC 構成でも使いやすいでしょう。

イメージ図はこんな感じです。

![flannel](https://raw.githubusercontent.com/coreos/flannel/master/packet-01.png)

(from https://github.com/coreos/flannel/blob/master/packet-01.png)

## 試してみる

以下で紹介する CoreOS VM を含めたサンプルリポジトリです。あわせてご利用ください:
[dtan4/coreos-flannel-sample](https://github.com/dtan4/coreos-flannel-sample)

### cloud-config

まず cloud-config はこんな感じです。flanneld.service というのを定義しています。

```yaml:user-data
#cloud-config

coreos:
  etcd:
    discovery: https://discovery.etcd.io/<token>
    addr: $public_ipv4:4001
    peer-addr: $public_ipv4:7001
  fleet:
    public-ip: $public_ipv4
  flannel:
    interface: $public_ipv4
  units:
    - name: etcd.service
      command: start
    - name: fleet.service
      command: start
    - name: flanneld.service
      drop-ins:
        - name: 50-network-config.conf
          content: |
            [Unit]
            Description=flanneld
            After=etcd.service
            Requires=etcd.service

            [Service]
            ExecStartPre=/usr/bin/etcdctl set /coreos.com/network/config '{ "Network": "10.1.0.0/16" }'
      command: start
    - name: docker-tcp.socket
      command: start
      enable: true
      # After(Requires)=flanneld.service を忘れないこと！
      content: |
        [Unit]
        Description=Docker Socket for the API
        After=flanneld.service
        Requires=flanneld.service

        [Socket]
        ListenStream=2375
        Service=docker.service
        BindIPv6Only=both

        [Install]
        WantedBy=sockets.target
```

注意すべきは、unit の起動順序を __etcd -> flannel -> docker__ とする必要があることです。flannel は etcd にマッピング情報を配置し、Docker は flannel が構築したネットワークの上で動作するためです。

* * *

余談ですが、flanneld.service は初回起動時に flannel の Docker イメージを pull してきます。docker.service が起動してないのに変ですね。
これは、flanneld.service の中で独自に Docker (early-docker.service) を起動することで実現しています。
詳しくは [Configuring flannel Networking](https://coreos.com/docs/cluster-management/setup/flannel-config/) の "Under the Hood" を読んでください。

### 起動

CoreOS クラスタを立ち上げます。サンプルリポジトリだと2台の CoreOS VM (core-01, core-02) が起動します。

```bash
$ vagrant up
```

立ち上がったらもうひとつターミナルを開き、それぞれのマシンにログインします。

```bash:core-01
$ vagrant ssh core-01
```

```bash:core-02
$ vagrant ssh core-02
```

両方の VM 上で bash コンテナを立ち上げ、コンテナに割り振られた IP アドレスを確認します（Ubuntu イメージを pull するので多少時間がかかります）。

```bash:core-01
core@core-01$ docker run -it --rm ubuntu /bin/bash
# 略
root@a46062460b1e:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
8: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
    link/ether 02:42:0a:01:0f:03 brd ff:ff:ff:ff:ff:ff
    inet 10.1.15.3/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe01:f03/64 scope link
       valid_lft forever preferred_lft forever
```

```bash:core-02
core@core-02$ docker run -it --rm ubuntu /bin/bash
# 略
root@686f6558b05c:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
8: eth0: <BROADCAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP group default
    link/ether 02:42:0a:01:51:03 brd ff:ff:ff:ff:ff:ff
    inet 10.1.81.3/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe01:5103/64 scope link
       valid_lft forever preferred_lft forever
```

core-01 上の bash コンテナには `10.1.15.3`、core-02 上の bash コンテナには `10.1.81.34` が割り当てられています。
では、それぞれのコンテナからもう一方のマシン上のコンテナに `ping` を打ってみます。

```bash:core-01
root@a46062460b1e:/# ping -c 5 10.1.81.3
PING 10.1.81.3 (10.1.81.3) 56(84) bytes of data.
64 bytes from 10.1.81.3: icmp_seq=1 ttl=60 time=0.794 ms
64 bytes from 10.1.81.3: icmp_seq=2 ttl=60 time=0.772 ms
64 bytes from 10.1.81.3: icmp_seq=3 ttl=60 time=1.37 ms
64 bytes from 10.1.81.3: icmp_seq=4 ttl=60 time=1.14 ms
64 bytes from 10.1.81.3: icmp_seq=5 ttl=60 time=1.15 ms

--- 10.1.81.3 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4003ms
rtt min/avg/max/mdev = 0.772/1.048/1.378/0.234 ms
```

```bash:core-02
root@686f6558b05c:/# ping -c 5 10.1.15.3
PING 10.1.15.3 (10.1.15.3) 56(84) bytes of data.
64 bytes from 10.1.15.3: icmp_seq=1 ttl=60 time=0.868 ms
64 bytes from 10.1.15.3: icmp_seq=2 ttl=60 time=1.38 ms
64 bytes from 10.1.15.3: icmp_seq=3 ttl=60 time=1.59 ms
64 bytes from 10.1.15.3: icmp_seq=4 ttl=60 time=2.06 ms
64 bytes from 10.1.15.3: icmp_seq=5 ttl=60 time=1.05 ms

--- 10.1.15.3 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4011ms
rtt min/avg/max/mdev = 0.868/1.391/2.065/0.423 ms
```

お互い、相手ホストのコンテナへの疎通確認ができました。めでたいですね :tada: 
以上が flannel を使った複数ホスト間コンテナ通信の例でした。

## 注意点

flannel は __マルチキャストパケットを通さない__ ので、例えば Elasticsearch の multicast discovery といったものは使えません。ユニキャストは通るのでなんとかして頑張りましょう。

## REF
- [coreos/flannel](https://github.com/coreos/flannel)
    - flannel 公式リポジトリ
- [Introducing flannel: An etcd backed overlay network for containers](https://coreos.com/blog/introducing-rudder/)
    - CoreOS に flannel が導入されたよ記事
- [Configuring flannel Networking](https://coreos.com/docs/cluster-management/setup/flannel-config/)
    - CoreOS 上で flannel を設定する方法
- [Dockerコンテナ接続パターン (2014年冬) | SOTA](http://deeeet.com/writing/2014/12/01/docker-link-pattern/)