Docker で コンテナに DNS サーバーを指定したときの IPの衝突によって名前解決できなくなる現象の確認

# 結論
- dockerの `dns` オプションを指定した際は、docker networkとの衝突に注意が必要
    - コンテナ内から名前解決できなくなる
- 以下の2つは等価ではない
    - docker の `dns` オプションを指定しない（デフォルトを使用する）
    - ホスト側DNSサーバーIPアドレス(例えば `192.168.0.1`) を dockerの `dns` オプションに指定する

# 条件
- Windows 10 Home
    - ProではないのでHyper-Vが使えない
    -  → Docker for Windows が使えない
- VirtualBox
- Docker Machine

## docker --version
```
Docker version 18.03.0-ce, build 0520e24302
```
古い。。。
TODO: 後で更新

## docker-machine --version
```
docker-machine.exe version 0.14.0, build 89b8332
```

## docker-machine ip
```
192.168.99.100
```

## ネットワーク
### ホスト側のDNSサーバー
`192.168.0.1`

### ipconfig.exe /all
抜粋

```
イーサネット アダプター VirtualBox Host-Only Network #5:
   接続固有の DNS サフィックス . . . . .:
   説明. . . . . . . . . . . . . . . . .: VirtualBox Host-Only Ethernet Adapter #5
   IPv4 アドレス . . . . . . . . . . . .: 192.168.56.1(優先)
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .:
   DNS サーバー. . . . . . . . . . . . .: fec0:0:0:ffff::1%1
                                          fec0:0:0:ffff::2%1
                                          fec0:0:0:ffff::3%1


イーサネット アダプター VirtualBox Host-Only Network #6:
   接続固有の DNS サフィックス . . . . .:
   説明. . . . . . . . . . . . . . . . .: VirtualBox Host-Only Ethernet Adapter #6
   IPv4 アドレス . . . . . . . . . . . .: 192.168.99.1(優先)
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .:
   DNS サーバー. . . . . . . . . . . . .: fec0:0:0:ffff::1%1
                                          fec0:0:0:ffff::2%1
                                          fec0:0:0:ffff::3%1


イーサネット アダプター ローカル エリア接続:
   接続固有の DNS サフィックス . . . . .:
   IPv4 アドレス . . . . . . . . . . . .: 192.168.0.13(優先)
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: 192.168.0.1
   DHCP サーバー . . . . . . . . . . . .: 192.168.0.1
   DNS サーバー. . . . . . . . . . . . .: 192.168.0.1
```

# 検証方法
- コンテナ内にDNSサーバーを起動
    - ベース: [tksugimoto/docker-dns-proxy-logger](https://github.com/tksugimoto/docker-dns-proxy-logger)
    - dnsmasq を使用
    - DNSサーバーはホスト側のDNSサーバーへ転送するだけ
        - `192.168.0.1`
    - docker-machine を使用しているため、 作成したDNSサーバーのIPは `192.168.99.100`
- ホスト側からコンテナ内にDNSサーバーにnslookup
    - [example.com](https://example.com/) を名前解決

# 検証結果

## dns指定無しの場合
docker networkのいずれかとホスト側のDNSサーバーのIPと衝突しててもコンテナ内で **名前解決はできる**


### nslookup
```
$ winpty nslookup.exe example.com 192.168.99.100
サーバー:  UnKnown
Address:  192.168.99.100

権限のない回答:
名前:    example.com
Address:  93.184.216.34
```
※ `192.168.99.100` は問い合わせに使うDNSサーバーのIP（コンテナ内DNSサーバー）

### docker network
ホストのDNSサーバーのIP `192.168.0.1`は `my-network-13` (`192.168.0.0/20`) と衝突している

```sh
$ docker network inspect $(docker network ls -q) | grep -E "Subnet|Name" | grep -C5 192.168.0.0
        "Name": "my-network-11",
                    "Subnet": "172.30.0.0/16",
        "Name": "my-network-12",
                    "Subnet": "172.31.0.0/16",
        "Name": "my-network-13",
                    "Subnet": "192.168.0.0/20",
        "Name": "my-network-14",
                    "Subnet": "192.168.16.0/20",
        "Name": "my-network-15",
                    "Subnet": "192.168.32.0/20",
        "Name": "my-network-16",
```

※ docker networkを大量に作る方法 → [docker networkを限界まで作る - Qiita](https://qiita.com/tksugimoto/items/e42b0d6931898671734f#%E5%AE%9F%E8%A1%8C%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89)

### docker-compose.yml
`dns` オプションは未指定（デフォルト値が使われる）

```yaml
version: '3'

services:
  dns-server:
    build:
      context: ./
      args:
        - http_proxy
        - https_proxy
    ports:
      - "192.168.99.100:53:53"
      - "192.168.99.100:53:53/udp"
    restart: always
```

## dns指定ありの場合（docker networkとIPが衝突した場合）
**名前解決がタイムアウトする**

DNSサーバーへ到達できていないと考えられる

### nslookup
```
$ winpty nslookup.exe example.com 192.168.99.100
DNS request timed out.
    timeout was 2 seconds.
サーバー:  UnKnown
Address:  192.168.99.100

DNS request timed out.
    timeout was 2 seconds.
DNS request timed out.
    timeout was 2 seconds.
```
※ `192.168.99.100` は問い合わせに使うDNSサーバーのIP（コンテナ内DNSサーバー）

### docker network
ホストのDNSサーバーのIP `192.168.0.1`は `my-network-13` (`192.168.0.0/20`) と衝突している

```sh
$ docker network inspect $(docker network ls -q) | grep -E "Subnet|Name" | grep -C5 192.168.0.0
        "Name": "my-network-11",
                    "Subnet": "172.30.0.0/16",
        "Name": "my-network-12",
                    "Subnet": "172.31.0.0/16",
        "Name": "my-network-13",
                    "Subnet": "192.168.0.0/20",
        "Name": "my-network-14",
                    "Subnet": "192.168.16.0/20",
        "Name": "my-network-15",
                    "Subnet": "192.168.32.0/20",
        "Name": "my-network-16",
```

### docker-compose.yml
`dns` オプションに `192.168.0.1` を指定 

```yaml
version: '3'

services:
  dns-server:
    build:
      context: ./
      args:
        - http_proxy
        - https_proxy
    ports:
      - "192.168.99.100:53:53"
      - "192.168.99.100:53:53/udp"
    dns:
      - 192.168.0.1
    restart: always
```


## dns指定ありの場合（docker networkとIPが衝突してない場合）
一つ前の条件から docker networkが削除削除された状態

```sh
docker network prune
```

**名前解決できる**

### nslookup
```
$ winpty nslookup.exe example.com 192.168.99.100
サーバー:  UnKnown
Address:  192.168.99.100

権限のない回答:
名前:    example.com
Address:  93.184.216.34
```
※ `192.168.99.100` は問い合わせに使うDNSサーバーのIP（コンテナ内DNSサーバー）

### docker network
`my-network-13` (`192.168.0.0/20`) は削除されたため、 ホストのDNSサーバーのIP `192.168.0.1`と衝突している docker networkは存在しない

```sh
$ docker network inspect $(docker network ls -q) | grep -E "Subnet|Name" | grep -C5 192.168.0.0        
# （存在しない）
```

### docker-compose.yml
`dns` オプションに `192.168.0.1` を指定 

```yaml
version: '3'

services:
  dns-server:
    build:
      context: ./
      args:
        - http_proxy
        - https_proxy
    ports:
      - "192.168.99.100:53:53"
      - "192.168.99.100:53:53/udp"
    dns:
      - 192.168.0.1
    restart: always
```

# まとめ
- dockerの `dns` オプションを指定した際は、docker networkとの衝突に注意が必要
    - コンテナ内から名前解決できなくなる
- 以下の2つは等価ではない
    - docker の `dns` オプションを指定しない（デフォルトを使用する）
    - ホスト側DNSサーバーIPアドレス(例えば `192.168.0.1`) を dockerの `dns` オプションに指定する