Dockerの基本的なネットワークについて

# はじめに
Dockerの基本的なネットワークの設定について学習.
デフォルトでは3種類のネットワークがある。


#取り扱ったコマンド

### docker network
- docker network ls
- docker network create 
- docker network connect 
- docker network disconnect
- docker network inspect


## bridgeネットワーク
- デフォルトではここに所属
- L2接続
- dockerデーモンによるDNS機能が提供されないため名前解決ができない(コンテナ名で通信できない)
	
## ユーザ定義ネットワーク

- bridgeでDNS機能が使える(コンテナ名で通信ができる) 


## hostネットワーク
- ホストのIPと同じIPがコンテナに適用される(NAPT)
- ポート番号でコンテナごとに識別している
	
## noneネットワーク
- スタンドアロン(今回詳細な説明は省きます。)
    - ループバックインターフェースしか持たない
    - このネットワークに所属するには他のネットワークに所属してはいけない
    

## それぞれのネットワークが接続されている状態は下図の様になる。
![スクリーンショット 2021-07-25 7.44.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320230/def57ebb-ef5c-71e9-3ef8-5d64e9a4ced4.png)



# bridgeネットワークの接続と疎通確認
bridgeネットワークはコンテナを作成した時にデフォルトで接続されるネットワークで明示的に指定する必要はない。

## alpineLinuxコンテナとnginxコンテナを作成。

```
docker@nw1-vm:~$ docker run -itd --name bridge-alpine1 alpine
24646f5761bcc86e22033c14544e95e8deafe231c6db313e86811046fad283c0
docker@nw1-vm:~$ docker run -itd --name bridge-nginx -p 8080:80 nginx
f13da9c76e923960a445847b737a0dfbc3b3655fb2ebba41a0dbd98f0fa29db0
docker@nw1-vm:~$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
f13da9c76e92        nginx               "/docker-entrypoint.…"   3 seconds ago       Up 2 seconds        0.0.0.0:8080->80/tcp   bridge-nginx
24646f5761bc        alpine              "/bin/sh"                16 seconds ago      Up 16 seconds                              bridge-alpine1
docker@nw1-vm:~$
```

## ネットワーク側の接続確認
docker network inspectコマンドでネットワークの詳細を確認できる
長いので一部抜粋
172.17.0.0/16のネットワークに
172.16.0.2(alpine1),172.168.0.3(nginx)が接続されていることがわかります。



```
 
        "Name": "bridge",
        "Id": "af3424acd307e3d96cc99e839670afb5503a859261aac66db6ccbbc7061005b5",
        "Created": "2021-07-24T22:00:22.322553435Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {

                    "Subnet": "172.17.0.0/16", #ネットワークアドレス
                    "Gateway": "172.17.0.1"    #ホストのIP
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "24646f5761bcc86e22033c14544e95e8deafe231c6db313e86811046fad283c0": {
                "Name": "bridge-alpine1",
                "EndpointID": "c9bde7f3053b8dd0c43b498e5f34106c98f38b53ea071367c0df37a59aab41b5",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16", #bridge-alpine1のIP
                "IPv6Address": ""
            },
            "f13da9c76e923960a445847b737a0dfbc3b3655fb2ebba41a0dbd98f0fa29db0": {
                "Name": "bridge-nginx",
                "EndpointID": "9ea7e5ccf04466ac9acfae5754bb00b5a72030204d147e747cacfc4ac2e4bbd0",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16", #bridge-nginxのIP
                "IPv6Address": ""
            }
        },

```

gatewayにあるIPアドレスはホストのIPアドレスです。
ip aやifconfigaなどでホストのインターフェースを確認すると同じアドレスがあるかと思います。

ip aの結果

```
6: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:d8:e0:8a:a2 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:d8ff:fee0:8aa2/64 scope link
       valid_lft forever preferred_lft forever

```

## コンテナ側のネットワーク確認
docker inspectコマンドで表示できるコンテナの詳細で確認できます。
こちらも長いので一部抜粋。出力の最下部に表示されます。

```
"Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "af3424acd307e3d96cc99e839670afb5503a859261aac66db6ccbbc7061005b5",
                    "EndpointID": "c9bde7f3053b8dd0c43b498e5f34106c98f38b53ea071367c0df37a59aab41b5",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
```

## 通信確認

この状態でのネットワーク図は以下の通りです。
![スクリーンショット 2021-07-25 8.13.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320230/794c14b6-44aa-93a3-9496-839b64a7dc3d.png)


### コンテナ間
bridge-alpine1とbridge-nginx間の通信。L2接続なのでもちろん通信可能です。

```
docker@nw1-vm:~$ docker exec -it bridge-alpine1 /bin/sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    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
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0
20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.096 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.086 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.086 ms
64 bytes from 172.17.0.3: seq=3 ttl=64 time=0.089 ms
64 bytes from 172.17.0.3: seq=4 ttl=64 time=0.470 ms
^C
--- 172.17.0.3 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.086/0.165/0.470 ms
/ #

```

ただし、デフォルトのbridgeネットワークではコンテナ名の名前解決が行えません。(デフォルトではDockerデーモンがDNS機能を提供していないとのこと)

```
/ # ping bridge-nginx
ping: bad address 'bridge-nginx'
/ #
```

ホストに戻って、bridge-nginxにhttpリクエストを送ります。nginxはデフォルトでは80番ポートでListenしているのでポートを指定せずリクエストを送ります。ホストも172.17.0.1のIPを持っているためコンテナ間と同じL2の通信となります。(alpineにcurlが入ってなかった。。)
作成時に8080にマッピングしていますが、理由は後ほど説明します。

```
docker@nw1-vm:~$ curl http://172.17.0.3
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

問題なくレスポンスが返ってきますね。

### 外部からの通信
外部とは、図でいうと192.168.99.0/24が該当し、「ホストマシンの中のネットワーク」に含まれていないところとしています。
試しに192.168.99.1から、先程のコンテナ172.17.0.2にpingを送っても返ってきません。(192.168.99.111は、172.17.0.Xにルーティングしてくれません。)

```
 tohi  ~  ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
36 bytes from 175.129.17.97: Communication prohibited by filter
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  00 5400 7bc0   0 0000  3b  01 9720 192.168.0.12  172.17.0.3

Request timeout for icmp_seq 0
36 bytes from 175.129.17.97: Communication prohibited by filter
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst
 4  5  00 5400 701f   0 0000  3b  01 a2c1 192.168.0.12  172.17.0.3

^C
--- 172.17.0.3 ping statistics ---
2 packets transmitted, 0 packets received, 100.0% packet loss
```

外部からの通信を受け付けるにはコンテナのポートをホストのポートにマッピングする必要があります。
bridge-nginxコンテナを作成した時ホストの8080ポートとコンテナの80ポートをマッピングしているため、192.168.99.111の8080ポートにアクセスするとbridge-nginxの80ポートに転送されbridge-nginxからレスポンスがあります。
直接ホストの80ポートにアクセスしてもホストは80をListenしていないのでレスポンスはありません。


```
tohi  ~  curl http://192.168.99.111:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

 tohi  ~  curl http://192.168.99.111:80
curl: (7) Failed to connect to 192.168.99.111 port 80: Connection refused
 tohi  ~ 

```




# hostネットワークの接続と疎通確認
ホストのネットワーク情報を共有して使います。
例えば、先ほどbridgeネットワークでコンテナの80ポートとホストの8080ポートを紐付け外部からのアクセスを受け付けていましたが、
hostネットワークではポートの紐付けは必要ありません。bridge-nginxをこのネットワークに接続すると、ホストとネットワーク情報を共有することとなり、
外部から192.168.99.111:80へのアクセスが可能となります。

## host-nginx2を作成しhostネットワークに接続
作成時、--networkオプションに引数としてネットワーク名を指定することで最初から指定したネットワークに所属させることができる。
この場合デフォルトのbridgeネットワークには自動で接続されないので注意

作成前にホストでListenしているポートを確認します。

```
docker@nw1-vm:~$ netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0    280 10.0.2.15:22            10.0.2.2:54714          ESTABLISHED
tcp        0      0 :::2376                 :::*                    LISTEN
tcp        0      0 :::22                   :::*                    LISTEN
docker@nw1-vm:~$
```
80ポートはListenされていません。
次にhost-nginx2コンテナを作成します。

```
docker@nw1-vm:~$ docker run -d --name host-nginx2 --network host nginx
a9da74b6fb649412d49e0e0510370b0b56cfea0b2ed6e6afcba0b135344f4561
docker@nw1-vm:~$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
a9da74b6fb64        nginx               "/docker-entrypoint.…"   3 seconds ago       Up 3 seconds                            host-nginx2
docker@nw1-vm:~$

```

作成後、ホストのListenポートを確認します。

```
docker@nw1-vm:~$ netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0     36 10.0.2.15:22            10.0.2.2:54714          ESTABLISHED
tcp        0      0 :::2376                 :::*                    LISTEN
tcp        0      0 :::80                   :::*                    LISTEN
tcp        0      0 :::22                   :::*                    LISTEN
```

hostネットワークにnginxコンテナが作成されたことによってポートでListenしていることがわかります。

試しにbridgeネットワークでホストの8080ポートとコンテナの80ポートをマッピングした時のホストのListenポートを確認すると、しっかり8080ポートでListenされていることがわかります。

```
docker@nw1-vm:~$ docker run -d --name bridge-nginx2 -p 8080:80 nginx
f1f34d127e518822363ecc1662f0e7b988ac5bd2d6d230c7289a4e95c721ed38
docker@nw1-vm:~$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
f1f34d127e51        nginx               "/docker-entrypoint.…"   2 seconds ago       Up 1 second         0.0.0.0:8080->80/tcp   bridge-nginx2
a9da74b6fb64        nginx               "/docker-entrypoint.…"   2 minutes ago       Up 2 minutes                               host-nginx2
docker@nw1-vm:~$ netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 10.0.2.15:22            10.0.2.2:54714          ESTABLISHED
tcp        0      0 :::2376                 :::*                    LISTEN
tcp        0      0 :::8080                 :::*                    LISTEN
tcp        0      0 :::80                   :::*                    LISTEN
tcp        0      0 :::22                   :::*                    LISTEN
docker@nw1-vm:~$
```

## 通信確認
ここまででネットワークは下図の様になっています。
![スクリーンショット 2021-07-25 9.12.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320230/d3a3e3b5-2538-d9fb-0040-010214bb6f21.png)

### hostネットワークのnginxへアクセス
先程netstatコマンドで確認した様に、ホストの80ポートでListenしているため、ホストのIP：80とすることでアクセスできます。
bridgeネットワークにいるnginxと区別するためindex.htmlの内容を変更しました。

hostネットワークのhost-nginx2へアクセス

```
 tohi  ~  curl http://192.168.99.111
<h1>Host Network Container</h1>
```

bridgeネットワークのbridge-nginxにアクセス

```
 tohi  ~  curl http://192.168.99.111:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

ポート番号によってそれぞれ別のコンテナにアクセスされていることがわかりました。

# ユーザ定義のbridgeネットワークの接続と疎通確認
ユーザ定義のbridgeネットワークではデフォルトのbridgeネットワークでできなかった名前解決が可能となっている。

## ユーザ定義のbridgeネットワーク作成
docker network createコマンドで新規ネットワークを作成。
docker network lsコマンドでネットワーク一覧を表示

```
docker@nw1-vm:~$ docker network create my_nw
df1f71cba9fbe4d09a0cbbd61a50a098b95a749d112f59811c75e41ae25b23a5
docker@nw1-vm:~$ docker network  ls
NETWORK ID          NAME                DRIVER              SCOPE
af3424acd307        bridge              bridge              local
f4b054a543f0        host                host                local
df1f71cba9fb        my_nw               bridge              local
23c0b52380c9        none                null                local
docker@nw1-vm:~$
```

## ユーザ定義のbridgeネットワークに接続
既存のbridge-nginx2は、
docker network disconnectコマンドでデフォルトbridgeネットワークから切断し
docker network connectコマンドでユーザ定義のbridgeネットワークに接続します。
※noneネットワーク以外であれば1つのコンテナを複数のネットワークに接続することは可能ですが、今回はわかりやすくするため既存のネットワークからは切断しています。

疎通確認のためalpine-linuxをユーザ定義bridgeネットワークに作成します。

```
docker@nw1-vm:~$ docker network disconnect bridge bridge-nginx2
docker@nw1-vm:~$ docker network connect my_nw  bridge-nginx2
docker@nw1-vm:~$ docker run -itd  --name bridge-alpine3 --network my_nw alpine
c3a9558ee438f1939b73db4b1fb8702adfd9407b1c0fed003423667448743ef6

docker@nw1-vm:~$ docker network inspect  my_nw
[
    {
        "Name": "my_nw",
        "Id": "df1f71cba9fbe4d09a0cbbd61a50a098b95a749d112f59811c75e41ae25b23a5",
        "Created": "2021-07-25T00:20:35.81237485Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "c3a9558ee438f1939b73db4b1fb8702adfd9407b1c0fed003423667448743ef6": {
                "Name": "bridge-alpine3",
                "EndpointID": "351a3422376daaed611c5b686b6c5e56cb62f8d7dc4e97c734579cc75ad53761",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            },
            "f1f34d127e518822363ecc1662f0e7b988ac5bd2d6d230c7289a4e95c721ed38": {
                "Name": "bridge-nginx2",
                "EndpointID": "7ef1d91219bd4b9f1acf063ac9051c6ff72e217ab7f88f95389497b7594eda80",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
docker@nw1-vm:~$

```
作成したmy_nwにbridge-nginx2が接続されていることが確認できます。

## 疎通確認
IPアドレスでの通信はもちろん可能ですが、ユーザ定義bridgeネットワークではコンテナ名での通信が可能です。

```
docker@nw1-vm:~$ docker exec -it bridge-alpine3 /bin/sh
/ # ping 172.19.0.2
PING 172.19.0.2 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.286 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.104 ms
^C
--- 172.19.0.2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.104/0.195/0.286 ms
/ # ping bridge-nginx2
PING bridge-nginx2 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.130 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.093 ms
64 bytes from 172.19.0.2: seq=2 ttl=64 time=0.088 ms
64 bytes from 172.19.0.2: seq=3 ttl=64 time=0.096 ms
^C
--- bridge-nginx2 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.088/0.101/0.130 ms
/ #

```
外部からのアクセスはbrigdeネットワークと同様にマッピングしたポートへ通信する必要があります。
ここまでやるとネットワーク図は以下の様になります。

![スクリーンショット 2021-07-25 9.40.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320230/f2968f8e-1041-dd79-ba6d-a1b637bbc3b6.png)


# 最後に
Dockerのネットワークについて基本的なところは以上になるかと思います。
自分用の面が強いですが、参考になれば幸いです。