Dockerネットワークを理解したい

# はじめに
コンテナ間をネットワークで繋ぐことができる。という知識くらいしかなかったが、それだと運用でハマるので勉強しようと思った。必要と思ったところだけ記録していきます。

# 事前知識
## 仮想ブリッジ
Dockerをインストールした時点で、docker0という名前の仮想ブリッジが生成される。
ブリッジなのでOSI参照モデルのデータリンク層(L2)で動作する。ブリッジの説明は割愛。

## 仮想NIC
コンテナに対して仮想NICが割り当てられる。

## ネットワーク分離
各コンテナは名前空間で分離されている。
なので、ネットワークを繋いでやらないと、コンテナ間で通信ができない。

## よく使うネットワーク種別
- bridge: 同一ネットワーク内でしか通信できない
- overlay: Swarmモードを有効化するなどしたばネットワークまたぎで通信可能（IPアドレス重複に注意！）

## 内部DNS
Dockerには内部DNSが動いており、コンテナ名での名前解決をすることができる。
同じネットワークであれば、コンテナ名で通信が可能。

# 基本的なコマンド
## ネットワーク一覧を表示
```
# docker network ls
```

## ネットワーク詳細を表示
```
# docker network inspect <ネットワーク名>
```

## ネットワーク作成
bridgeネットワークとして作成されます。
docker-composeで書く場合はコマンドでcreateしなくてもよいです。
```
# docker network create <ネットワーク名>
```

## ネットワーク削除
### 不要なネットワークを個別に削除
```
# docker network rm <ネットワーク名>
```
### 利用していないネットワークをまとめて削除
```
# docker network prune
```

## 仮想インターフェースのIPアドレスを確認
```
# ip a
```

## 内部ネットワーク一覧を確認
```
# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
cbfda3037a97   bridge    bridge    local
e1b9462ba7c6   host      host      local
10b4e5654b78   none      null      local
```
ネットワークの種類はDRIVERに記されている。
この3つは、初期で作られるネットワークになります。
- bridge : docker-composeでネットワークを指定せずに構築すると、このネットワークが割り当てられる。このデフォルトネットワークでは、コンテナ間の名前解決ができない。名前解決させたい場合は、ユーザー定義したbridgeネットワークを作成する必要がある。
- host : ホストと同じネットワークインターフェースを使用する。ホストのポートと、コンテナのポートが一緒になるので、-pオプションを指定しなくても良くなる。（docker-composeの場合、port設定が不要）
- none : ネットワークを有しない。ループバックインターフェースのみ有する。

# docker-compose.ymlへの記述
myappディレクトリに、docker-compose.ymlを保存した状態でコンテナを立てます。
```
/myapp
  └ docker-compose.yml
```
## ネットワークに何も記載しない場合
```
version: "3.8"
services:
  web:
    image: nginx:latest
    container_name: nginx_container
    hostname: web_server
    ports:
      - 80:80
    restart: always
    environment:
      TZ: Asia/Tokyo
```
`myapp_default`という、フォルダ名に`_default`という名前のついたネットワークが作成されました。

## defaultネットワークを作成しない方法
トップレベルのnetworksを以下のように記述。
以下の場合、デフォルトで作成されているbridgeという名前のネットワークに所属させることができます。
```
version: "3.8"
services:
  web:
    image: nginx:latest
    container_name: nginx_container
    hostname: web_server
    ports:
      - 80:80
    restart: always
    environment:
      TZ: Asia/Tokyo
networks:
  default:
    name: bridge
```
## 名前付きネットワークを作成
my_networkという名前のついたネットワークを作成します。
トップレベルのnetworksでネットワークを作成しています。
subnetを宣言しない場合、docker内で空いているセグメントが自動的に付与されます。
servicesの中のコンテナ内に宣言されているnetworksでは、どのネットワークに属するかを記載しています。
ここで、ネットワーク内のどのIPアドレスを割り当てるかを記載できます。
```
version: "3.8"
services:
  web:
    image: nginx:latest
    container_name: nginx_container
    hostname: web_server
    ports:
      - 80:80
    restart: always
    environment:
      TZ: Asia/Tokyo
    networks:
      my_network:
        ipv4_address: 172.20.0.2
networks:
  default:
    name: bridge
  my_network:
    name: my_network
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/24
```
## 既存のネットワークを割り当てたい場合
ネットワークの設定内に`external: true`を付けます。
```
networks:
  default:
    name: bridge
  my_network:
    name: my_network
    driver: bridge
    external: true
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/24
```

## 注意したいこと
Dockerネットワーク内のIPアドレスと、ホストが接続できる外部サーバーのIPアドレスが重複している場合、Dockerネットワーク内のIPアドレスが優先されてしまいます。
社内ネットワークで利用する場合は、アドレス重複には注意しましょう。

また、ここで割り当てているIPアドレス(172.20.0.2)は、あくまでdocker内で使われるIPアドレスなので、Nginx(Webサーバ)にアクセスする場合はホストサーバーに割り当てられたIPアドレスからアクセスしましょう。