docker bridge networkのIPアドレス指定での落とし穴（bip指定しても172.17.0.0/16から通信できなくなった）

## 要約

* /etc/docker/daemon.json で `bip` に IP アドレス範囲を指定しても、docker0 以外の追加ブリッジには作用しない。

* docker-compose などでブリッジが追加されると `172.17.0.1/16` が割り当てられてしまい、実存するサブネットから通信不能になる。

* `bip` の代わりに、`default-address-pools` に割り当て可能な IP アドレス範囲を指定する。

## 発生した問題

### 発生条件

* docker マシンが接続されている LAN 内に 172.17.0.0/16 などのクラスB(172.16.0.0/12)に含まれるサブネットが実存する場合

### システム構成

| 項目 | 説明 |
|----|----|
| OS | Debian 9.9 |
| Docker | docker-ce 18.09.6 |
| docker-compose | docker-compose 1.22 | 
| dockerマシンの接続サブネット | 172.16.0.0/16 |
| クライアントの接続サブネット | 172.17.0.0/16, 172.18.0.0/16, ... |

### 問題現象

* docker 既定のブリッジ `docker0` の IP アドレスが実存するサブネットと競合しないように、/etc/docker/deamon.json に `bip` を指定して、標準の `172.17.0.1/16` から `192.168.0.1/24` に変更してある。
* 先日まで 172.17.0.0/16 に接続するクライアントからも docker 上のアプリを利用できていた。
* docker-compose でアプリ追加を試していたところ、172.17.0.0/16 に接続するクライアントから docker 上のアプリが利用できなくなった。
* 172.17.0.0/16 からの ping や traceroute もエラーとなり、docker マシンへ通信できない状態に陥ってしまった。
* 172.18.0.0/16 に接続するクライアントからは通信できているが、時折 docker マシンへの ssh 接続が切断されることがあった。

## 落とし穴

* 公式ドキュメントに記載されている次の方法は、Docker 標準の既定のブリッジ `docker0` の IPアドレス範囲にのみ作用する。

    * [Configure the default bridge network - docker docs(latest)](https://docs.docker.com/network/bridge/#configure-the-default-bridge-network)
    * [Customize the docker0 bridge - docker docs(v17.09)](https://docs.docker.com/v17.09/engine/userguide/networking/default_network/custom-docker0/)

    ```
    /etc/docker/daemon.json

    {
        "bip":"192.168.0.1/24"
    }
    ```

* docker-compose や docker network create コマンドにより `docker0` 以外に新たなブリッジが追加されると、172.17.0.1/16 が割り当てられてしまう。

    ```
    docker-compose.yml

        networks:
            backend_bridge:
                driver: ...
        ...
    ```

* そうなると、実存する 172.17.0.0/16 サブネットと競合するため、 172.17.0.0/16 に接続するクライアントからの通信が誤ってルーティングされてしまい、通信不能に陥る。

* docker-compose.yml に `networks:` が記述されていなくても、複数コンテナが定義されていれば、コンテナ間の通信用に新たな default ブリッジが追加される。

* `networks:` に定義するブリッジごとに IP アドレス範囲を指定することはできるが、別の docker-compose 対象と競合しないように管理するのは面倒で、ミスも誘発しやすい。

## 対応策

* 参考になった記事

    > [Changing default subnet for docker custom networks - stackoverflow.com](https://stackoverflow.com/questions/44003663/changing-default-subnet-for-docker-custom-networks)

    > [Configuring Docker to not use the 172.17.0.0 range - serverfault.com](https://serverfault.com/questions/916941/configuring-docker-to-not-use-the-172-17-0-0-range/936255#936255)

* /etc/docker/daemon.json の記述例

    * `default-address-pools` に docker 内のブリッジに割り当て可能な IP アドレス範囲と各ブリッジのサブネットマスク相当の値を指定する。

    * `bip` を省略した場合、この `default-address-pools` の中から IP アドレスが割り振られる。

    ```
    {
        "default-address-pools":[  
            {  
                "base":"192.168.0.0/16",
                "size":24
            }
        ]
    }
    ```

## 動作確認

### 対策前

* /etc/docker/daemon.json

    ```
    {
        "bip":"192.168.0.1/16"
    }
    ```

* 動作結果

    ```
    user@host:~$ docker-compose up -d
    user@host:~$ ip -4 a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        inet 127.0.0.1/8 scope host lo
        valid_lft forever preferred_lft forever
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
        inet 172.16.2.194/24 brd 172.16.2.255 scope global noprefixroute eth0
        valid_lft forever preferred_lft forever
    6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
        inet 192.168.0.1/16 brd 192.168.255.255 scope global docker0
        valid_lft forever preferred_lft forever
    17: br-b7e055bbfd68: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        inet 172.17.0.1/16 brd 172.17.255.255 scope global br-b7e055bbfd68
        valid_lft forever preferred_lft forever
    ```

    > 既定のブリッジ: `docker0` には `192.168.0.1/16` が割り当てられている。しかし、その下のブリッジ: `br-b7e055bbfd68` には `172.17.0.1/16` が割り当てられてしまっている。

    > `docker-compose up` や `docker network create` のコマンドで新たにブリッジが追加されるたびに、その次の IP アドレス範囲(`172.18.0.1/16`)が割り当てられる。

#### もう一つの落とし穴

* `docker-compose down; docker-compose up -d` を繰り返していると、ブリッジに割り当てられる IP アドレス範囲が実存するサブネットと競合しなくなり、問題現象が発生しなくなるため、この問題が顕在化しない恐れがある。

* その後 docker マシンを再起動しても、既存ブリッジでは IP アドレス範囲が維持されるため問題なく稼働する。だが、その後に `docker-compose down; docker-compose up -d` などを実行して新たにブリッジが追加されると、 `172.17.0.1/16` が再び割り当てられて問題が顕在化する。

### 対策後

* /etc/docker/daemon.json

    ```
    {
        "default-address-pools":[
            {
            "base":"192.168.0.0/16",
            "size":24
            }
        ]
    }
    ```

* 動作結果

    ```
    user@host:~$ sudo service docker restart
    user@host:~$ docker-compose down; docker-compose up -d
    user@host:~$ ip -4 a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        inet 127.0.0.1/8 scope host lo
        valid_lft forever preferred_lft forever
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
        inet 172.16.2.194/24 brd 172.16.2.255 scope global noprefixroute eth0
        valid_lft forever preferred_lft forever
    102: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
        inet 192.168.0.1/24 brd 192.168.0.255 scope global docker0
        valid_lft forever preferred_lft forever
    103: br-45f6e0728ffd: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        inet 192.168.1.1/24 brd 192.168.1.255 scope global br-45f6e0728ffd
        valid_lft forever preferred_lft forever
    ```

    > `docker0` とその下のブリッジ `br-45f6e0728ffd` に `192.168.0.0/16` の IP アドレス範囲の中から2つのサブネットが割り当てられている。

## 参照情報

* [Custom IP Range for new networks - Docker Community Forums](https://forums.docker.com/t/custom-ip-range-for-new-networks/60839)

<!--
**作業ログ**

* before

```
user@host:~$ ip -4 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 172.16.2.178/24 brd 172.16.2.255 scope global eth0
       valid_lft forever preferred_lft forever
3: br-097d51f21375: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    inet 192.168.2.1/24 brd 192.168.2.255 scope global br-097d51f21375
       valid_lft forever preferred_lft forever
4: br-27f67e358005: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    inet 192.168.160.1/20 brd 192.168.175.255 scope global br-27f67e358005
       valid_lft forever preferred_lft forever
5: br-2ba7ea3a1c2b: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    inet 192.168.1.1/24 brd 192.168.1.255 scope global br-2ba7ea3a1c2b
       valid_lft forever preferred_lft forever
6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    inet 192.168.0.1/24 brd 192.168.0.255 scope global docker0
       valid_lft forever preferred_lft forever

user@host:~$ ip -4 r
default via 172.16.2.254 dev eth0 proto static metric 100
172.16.2.0/24 dev eth0 proto kernel scope link src 172.16.2.178 metric 100
192.168.0.0/24 dev docker0 proto kernel scope link src 192.168.0.1 linkdown
192.168.1.0/24 dev br-2ba7ea3a1c2b proto kernel scope link src 192.168.1.1
192.168.2.0/24 dev br-097d51f21375 proto kernel scope link src 192.168.2.1
192.168.160.0/20 dev br-27f67e358005 proto kernel scope link src 192.168.160.1
```

* after

```
user@host:~$ ip -4 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 172.16.2.178/24 brd 172.16.2.255 scope global eth0
       valid_lft forever preferred_lft forever
3: br-2ba7ea3a1c2b: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    inet 192.168.1.1/24 brd 192.168.1.255 scope global br-2ba7ea3a1c2b
       valid_lft forever preferred_lft forever
5: br-6d13ba6430c5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    inet 192.168.2.1/24 brd 192.168.2.255 scope global br-6d13ba6430c5
       valid_lft forever preferred_lft forever
6: br-bbdeebe3d693: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    inet 192.168.3.1/24 brd 192.168.3.255 scope global br-bbdeebe3d693
       valid_lft forever preferred_lft forever
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    inet 192.168.0.1/24 brd 192.168.0.255 scope global docker0
       valid_lft forever preferred_lft forever

user@host:~$ ip -4 r
default via 172.16.2.254 dev eth0 proto static metric 100
172.16.2.0/24 dev eth0 proto kernel scope link src 172.16.2.178 metric 100
192.168.0.0/24 dev docker0 proto kernel scope link src 192.168.0.1 linkdown
192.168.1.0/24 dev br-2ba7ea3a1c2b proto kernel scope link src 192.168.1.1
192.168.2.0/24 dev br-6d13ba6430c5 proto kernel scope link src 192.168.2.1
192.168.3.0/24 dev br-bbdeebe3d693 proto kernel scope link src 192.168.3.1
```
-->