【Docker】コンテナネットワークの基礎知識（bridgeネットワークについて）

#コンテナネットワークについて
Dockerにおいてデフォルトで存在するネットワークは以下の３つがあります。
###bridge
bridgeはDockerが提供するNAT用のネットワークです。基本的にはbridgeが最もコンテナの利点を活用できます。
Dockerをインストールしたホスト環境には、`docker0`という`デフォルトbridgeネットワーク`が作成されます。
このデフォルトのbridgeはコンテナ名ベースの名前解決機能を持っていません。
反対にユーザ自身が新たに作成したbridge（`ユーザ定義bridgeネットワーク`）はこのコンテナ名ベースの名前解決機能を持っています。
詳細は後ほど記載します。
###host
hostはDockerホストのネットワークをそのまま利用するため、ホストのインターフェースと同じIPアドレスでhostネットワークに属するコンテナにアクセスすることができます。（利用するポートもホストと共有されるため、ホストでポート80を使っていればコンテナはポート80を使えません）
###none
noneネットワークに属したコンテナはネットワークインターフェースを持たないため、インターネットや他のコンテナとも通信ができません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644352/fc7c753a-e589-35c5-c402-1af9719c3a9b.png)
（参照先：https://knowledge.sakura.ad.jp/23899/）

#bridgeネットワーク
ここでは、デフォルトのbridgeネットワーク（docker0）を例にbridgeネットワークについて説明します。
##デフォルトbridgeネットワーク
`docker network inspect bridge`でデフォルトのブリッジネットワーク(docker0)について確認できます。

```console
$ docker network inspect bridge
[
   {
       "Name": "bridge",
       "Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
       "Scope": "local",
       "Driver": "bridge",
       "IPAM": {
           "Driver": "default",
           "Config": [
               {
                   "Subnet": "172.17.0.1/16",
                   "Gateway": "172.17.0.1"
               }
           ]
       },
       "Containers": {},
       "Options": {
           "com.docker.network.bridge.default_bridge": "true",
           "com.docker.network.bridge.enable_icc": "true",
           "com.docker.network.bridge.enable_ip_masquerade": "true",
           "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
           "com.docker.network.bridge.name": "docker0",
           "com.docker.network.driver.mtu": "9001"
       }
   }
]
```

デフォルトのbridgeネットワークであるdocker0では、以下のようなコンテナアクセスを実現します。
（参照先：https://christina04.hatenablog.com/entry/2016/07/22/193000）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644352/4068d843-cf20-f07c-a1ac-933e21ef1d57.png)



ホスト環境のNICに対して、eth0というネットワークインターフェースが割り当てられ、
このeth0にdocker0（デフォルトのbridgeネットワーク）が接続されています。
ホストOSが提供するiptablesにより、外部ネットワークとdocker0のネットワークアドレス変換（NAT：Network Address Translation）が行われ、コンテナは外部ネットワークと通信できます。

上の図のContainer1とContainer2は、それぞれの同一ネットワーク内の通信となるため、双方のコンテナ間では`L2通信（ARPによるアドレス解決）`が行われます。（厳密にはそれぞれのコンテナのethのペアであるveth間でL2通信が行われる。それより先は各vethに接続されたコンテナに中継される。）

また、内部にあるコンテナからインターネットなどの外部ネットワークへの通信においては、
`NAPT（IPマスカレード）`を使ってグローバルIPアドレスに変換して通信を行います。
そして返りの通信でも`NAPT（IPマスカレード）`を使って、元のプライベートIPアドレスに変換されて送信元のコンテナへ送られます。

もちろん、`NAPT（IPマスカレード）`では`ポートフォワーディング`も行います。
（Dockerコンテナにおけるポートフォワーディングについては、以前の記事を参照してください。）

###docker0
DockerをインストールしたLinuxホストには、docker0という仮想ブリッジがデフォルトで作成されます。
仮想ブリッジとは、Linux上に構成されている仮想的なL2スイッチのことです。
このブリッジに到達したパケットは、宛先情報を確認され、宛先のコンテナや外部へ中継されます。

###veth（virtual eth）
vethとは、仮想NICのことです。ただ、コンテナネットワークにおいては2つのveth(仮想NIC)のペアによって通信が行われます。
この2つのveth間で通信を行うことができ、１つはホスト側のネットワークネームスペースに割り当てられ、もう1つはコンテナ側のネットワークネームスペースに割り当てられます。（下図のような感じ）
ホスト側のネットワークネームスペースに割り当てられたvethは、ホスト上にあるブリッジ(docker0)に接続すると、ブリッジを介してホスト外との通信が可能となります。
この2つのvethは、異なるネットワークネームスペースに割り当てられていないと通信ができません。（※）
なぜ、このようなペアで作られるのでしょうか。それはコンテナにおけるネットワークネームスペースの仕組みにあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644352/e596265d-19db-ab75-e0c5-d969823fc3e8.png)
（参照：https://gihyo.jp/admin/serial/01/linux_containers/0006）

##コンテナにおけるネットワークネームスペース
ネットワークネームスペースにおいては、ネットワークリソース（IPアドレスやRTテーブル、ポート番号など）をネームスペースという区切られた空間ごとに独立して持つことができます。（上図の青色の部分と、黄色の部分）
これにより、コンテナ側はホスト環境とは別のネットワークとして扱われるため、ホスト側とIPアドレスやポートが重複しても問題なく通信が可能となります。
つまり、ホスト上のインターフェースにそのままコンテナが割り当てられている場合（これがhostネットワーク）、ホスト環境とIPアドレスやポート番号が共有されてしまいます。

##ユーザ定義ブリッジネットワーク
デフォルトのbridgeネットワークとは別に、自分で作成する`ユーザ定義bridgeネットワーク`があり、この２つは名前解決機能において差があります。

コンテナネットワークにおける名前解決は、普通の名前解決の仕組みと比べて少し特殊です。
複数のコンテナを連携させるためには、`コンテナ名ベース`で名前解決を実現するのが一般的です。コンテナは使い捨てする運用が多いため、IPアドレスが頻繁に変わります。そのため、コンテナ名ベースで名前解決を行ったほうが都合が良いのです。
コンテナ名ベースで名前解決を行う場合は、デフォルトのbridgeネットワーク（docker0）を使うのではなく、自身で新たに作成したbridgeネットワーク（ユーザ定義bridgeネットワーク）を使うのが一般的です。
ユーザ定義されたbridgeネットワークは、デフォルトのbridgeネットワークと異なり、`コンテナ名ベースでの名前解決`が可能となります。（コンテナ名を指定してping疎通を行うことができる）

`docker network create`コマンドで、`-d`オプションでbridgeを指定すれば新しいbridgeネットワークを作成できます。
ここではbridge1という新しいbridgeネットワークを新規作成します。

```console
$ docker network create -d bridge bridge1
daa4195aab0bd7da4f20371d15660529a016457aee8eaef936f028f62c18229f
```

`docker network ls`コマンドで新しく作成されていることを確認します。

```console
$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
1916875b8e0e        bridge              bridge              local
9d80476b295c        host                host                local
daa4195aab0b        bridge1             bridge              local
f1b79f29f8a4        none                null                local
```

新しいbridgeネットワーク上でコンテナを起動するときは、`docker container run`コマンドの`--network`オプションでネットワークを指定して起動できます。

```console
$ docker container run --rm -d --name ct1 --network bridge1 ubuntu:16.04 
398a562546a16530a9569b9a54734c3be3ce5676286935105745c66021d1bb45
```

#参考サイト
https://knowledge.sakura.ad.jp/23899/
https://gihyo.jp/admin/serial/01/linux_containers/0006