外部ネットワーク側からDockerコンテナに通信できる環境を作成する

dockerコンテナを活用した開発環境を整備するにあたり、既存ネットワーク機器、サーバ機器との通信できる環境が不可欠になります。そんな時に、必要最小限の手間がdocker-network環境を整備する方法を、まとめてみました。

![docker-network.001.png](https://qiita-image-store.s3.amazonaws.com/0/103595/0c8cc231-52a2-ccdd-56b8-07d6e94d4613.png)



# ◼️ Linux Host環境の事前構成を確認しておく
### (1) UbuntuOSのバージョン確認

```
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"
```
### (2) NICインタフェース確認

```
$ ifconfig ens6
ens6      Link encap:Ethernet  HWaddr 52:54:00:a8:f6:26  
          inet addr:192.168.100.201  Bcast:192.168.100.255  Mask:255.255.255.0
          inet6 addr: 240b:11:53a0:400:5054:ff:fea8:f626/64 Scope:Global
          inet6 addr: fe80::5054:ff:fea8:f626/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:21888 errors:0 dropped:0 overruns:0 frame:0
          TX packets:19053 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:48194353 (48.1 MB)  TX bytes:1774186 (1.7 MB)
```
### (3) dockerバージョン確認

```
$ docker version
Client:
 Version:           18.06.1-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        e68fc7a
 Built:             Tue Aug 21 17:24:56 2018
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.06.1-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       e68fc7a
  Built:            Tue Aug 21 17:23:21 2018
  OS/Arch:          linux/amd64
  Experimental:     false
```

# ◼️ 通常のDocker環境で、外部ネットワークと通信してみる
### (1) Dockerコンテナを起動する
手始めに、次のようなdocker-compose.yamlを活用して、dockerコンテナを起動します。

```text:docker-compose.yaml
version: "2"

services:
  host1:
    image: ubuntu:14.04
    container_name: host1
    command: /bin/sh -c "tail -f /dev/null"
    hostname: host1
  host2:
    image: ubuntu:14.04
    container_name: host2
    command: /bin/sh -c "tail -f /dev/null"
    hostname: host2
```
```
$ docker-compose up -d
```
```
$ docker-compose ps
Name              Command              State   Ports
----------------------------------------------------
host1   /bin/sh -c tail -f /dev/null   Up           
host2   /bin/sh -c tail -f /dev/null   Up 
```
### (2) dockerコンテナ動作を確認する

```          
$ docker exec -it host1 bash
root@host1:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:ac:1b:00:03  
          inet addr:172.27.0.3  Bcast:172.27.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:18 errors:0 dropped:0 overruns:0 frame:0
          TX packets:4 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1408 (1.4 KB)  TX bytes:280 (280.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
```
```
root@host1:/# ping 192.168.100.1
PING 192.168.100.1 (192.168.100.1) 56(84) bytes of data.
64 bytes from 192.168.100.1: icmp_seq=1 ttl=64 time=0.116 ms
64 bytes from 192.168.100.1: icmp_seq=2 ttl=64 time=0.054 ms
64 bytes from 192.168.100.1: icmp_seq=3 ttl=64 time=0.054 ms
64 bytes from 192.168.100.1: icmp_seq=4 ttl=64 time=0.059 ms
64 bytes from 192.168.100.1: icmp_seq=5 ttl=64 time=0.059 ms
64 bytes from 192.168.100.1: icmp_seq=6 ttl=64 time=0.058 ms
^C
--- 192.168.100.1 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 4997ms
rtt min/avg/max/mdev = 0.054/0.066/0.116/0.024 ms

```

### (3) docker構成を確認しておく
今回、起動したdockerコンテナが収容されているdocker-network構成を確認しておく

```
$ docker inspect sample-docker_default 
[
    {
        "Name": "sample-docker_default",
        "Id": "0de2654c44bf4639f4bb599b9d9f800f8789bfd55aae702a378c19ba0efbdd9e",
        "Created": "2018-10-11T07:23:42.069601379+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.27.0.0/16",
                    "Gateway": "172.27.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "1d86065eda54e3f3130a54f05038274db8b872b88672fc4e2797e42d245c1d41": {
                "Name": "host2",
                "EndpointID": "73e5807379a1fcde47e3c0222c52c6e93e36eedc4347994d367963decfdbd47b",
                "MacAddress": "02:42:ac:1b:00:02",
                "IPv4Address": "172.27.0.2/16",
                "IPv6Address": ""
            },
            "f75fa1b14dca5fabe5ce925be832044071925be253e814814e5708ef874f28e3": {
                "Name": "host1",
                "EndpointID": "28945f60e55ddb20ba6ed53d2282fa43f5566d186355e880a917da31be114fa3",
                "MacAddress": "02:42:ac:1b:00:03",
                "IPv4Address": "172.27.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
```
dockerコンテナから、外部ネットワークに通信する場合には、通常のdockerコンテナ構成でも、特に手間をかけることなく、通信できます。しかしながら、外部ネットワークから、dockerコンテナに通信する場合には、このままのdocker環境では、実現できません。
強引に、iptablesを使って、dockerコンテナに割り当てられたIPアドレスを、外部ネットワークのIPアドレスにNAT変換すれば、実現できそうですが、docker環境が、かなり煩雑になりそうです。

# ◼️ Docker環境を構成変更する

### (1) Linuxブリッジ"br0"を作成する
Linux Host環境において、"br0"を作成しておきます。以下のようにコンフィグファイルを修正して、Ubuntuを再起動しました。

```text:/etc/network/interfaces
... (snip)

auto ens6
iface ens6 inet manual

auto br0
iface br0 inet static
        address 192.168.100.201
        netmask 255.255.255.0
        network 192.168.100.0
        broadcast 192.168.100.255
        gateway 192.168.100.1
        dns-nameservers 192.168.100.1
        bridge_ports ens6
        bridge_stp off
        bridge_fd 0
        bridge_maxwait 1
```
### (2) docker-networkを作成して、linuxブリッジ"br0"を配備する
Dockerコンテナを収容できるように、docker-networkを作成しておきます。
なお、このとき、ssh等による遠隔ログインで作業すると、ログインターミナルが使用できなくなる恐れがあるので、consoleログインで作業してください。

```
$ docker network create \
  --driver=bridge \
  --subnet 192.168.100.0/24 \
  --gateway 192.168.100.201 \
  --opt "com.docker.network.bridge.name"="br0" \
  sample-network
```
### (3) UbuntuOSを再起動する
再起動後、linuxブリッジの構成を確認しておきます

```
$ brctl show
bridge name	bridge id		    STP enabled	 interfaces	
br0		    8000.525400a8f626	no		     ens6
docker0		8000.02421684adc9	no		
```
### (4) dockerコンテナを起動する
先ほど使用したdocker-compose.yamlを一部カスタマイズして、dockerコンテナを起動します。

```text:docker-compose.yaml
version: "2"

services:
  host1:
    image: ubuntu:14.04
    container_name: host1
    command: /bin/sh -c "tail -f /dev/null"
    hostname: host1
    networks:
      sample-network: 
        ipv4_address: 192.168.100.221
  host2:
    image: ubuntu:14.04
    container_name: host2
    command: /bin/sh -c "tail -f /dev/null"
    hostname: host2
    networks:
      sample-network: 
        ipv4_address: 192.168.100.222

networks:
    sample-network:
      external: true
```
```
$ docker-compose up -d
Creating host2 ... done
Creating host1 ... done
```
```
$ docker-compose ps
Name              Command              State   Ports
----------------------------------------------------
host1   /bin/sh -c tail -f /dev/null   Up           
host2   /bin/sh -c tail -f /dev/null   Up
```
### (5) dockerコンテナ動作を確認する

```          
$ docker exec -it host1 bash
root@host1:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:c0:a8:64:dd  
          inet addr:192.168.100.221  Bcast:192.168.100.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:54 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:13573 (13.5 KB)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
```
```
root@host1:/# ping 192.168.100.1
PING 192.168.100.1 (192.168.100.1) 56(84) bytes of data.
64 bytes from 192.168.100.1: icmp_seq=1 ttl=255 time=1.84 ms
64 bytes from 192.168.100.1: icmp_seq=2 ttl=255 time=1.04 ms
64 bytes from 192.168.100.1: icmp_seq=3 ttl=255 time=1.37 ms
64 bytes from 192.168.100.1: icmp_seq=4 ttl=255 time=1.32 ms
^C
--- 192.168.100.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 1.044/1.396/1.841/0.288 ms
```
### (6) 外部ネットワーク側から、dockerコンテナにアクセスしてみる
dockerコンテナ"host1", "host2"に向けて、ping疎通性を確認してみる。

```
ttsubo@ubuntu:~$ ping 192.168.100.221
PING 192.168.100.221 (192.168.100.221) 56(84) bytes of data.
64 bytes from 192.168.100.221: icmp_seq=1 ttl=64 time=0.508 ms
64 bytes from 192.168.100.221: icmp_seq=2 ttl=64 time=0.275 ms
64 bytes from 192.168.100.221: icmp_seq=3 ttl=64 time=0.292 ms
64 bytes from 192.168.100.221: icmp_seq=4 ttl=64 time=0.285 ms
^C
--- 192.168.100.221 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2997ms
rtt min/avg/max/mdev = 0.275/0.340/0.508/0.097 ms
```
```
ttsubo@ubuntu:~$ ping 192.168.100.222
PING 192.168.100.222 (192.168.100.222) 56(84) bytes of data.
64 bytes from 192.168.100.222: icmp_seq=1 ttl=64 time=0.316 ms
64 bytes from 192.168.100.222: icmp_seq=2 ttl=64 time=0.236 ms
64 bytes from 192.168.100.222: icmp_seq=3 ttl=64 time=0.252 ms
64 bytes from 192.168.100.222: icmp_seq=4 ttl=64 time=0.253 ms
^C
--- 192.168.100.222 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2997ms
rtt min/avg/max/mdev = 0.236/0.264/0.316/0.032 ms
```
外部ネットワーク側から、dockerコンテナに向けて、通信できるようになりました。
### (7) docker構成を確認しておく

```
$ docker inspect sample-network
[
    {
        "Name": "sample-network",
        "Id": "e8d9426b605b9f1a33a1bba398f34648c7f63811fa0d36ab9eaba3b30daeaa1f",
        "Created": "2018-10-12T20:56:23.04226508+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.100.0/24",
                    "Gateway": "192.168.100.201"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "41828c1fe2bd1e3478b8c4a836ac411cc032b23b38b31e38ead636b5986172b1": {
                "Name": "host1",
                "EndpointID": "59b969ac86263962681d84e484d33a13808b877cbf9f6d7bfd9f6cde236d0fc2",
                "MacAddress": "02:42:c0:a8:64:dd",
                "IPv4Address": "192.168.100.221/24",
                "IPv6Address": ""
            },
            "53190efb688fa76d8c592838c8aec5f2a3b4e93992297055acfc0f16ad94a988": {
                "Name": "host2",
                "EndpointID": "f1346174c2cf92e8afb0fb7ae717cdbde5cb75571cc1547b4e8993d8e61dfa92",
                "MacAddress": "02:42:c0:a8:64:de",
                "IPv4Address": "192.168.100.222/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.name": "br0"
        },
        "Labels": {}
    }
]
```
以上です。

# ◼️ 補足事項
上記のDockerネットワーク環境を作成した後、dockerイメージのbuildがうまくいかなくなるケースがあります。
（Dockerfileの中で、pipを用いて、packageインストールを実行しているような場合）

https://stackoverflow.com/questions/48277599/pip-inside-dockerfile-under-proxy

このような事象が発生した場合には、 --network=host オプションを指定すると回避できました。