docker.svg

【Docker】Dockerネットワーク

Docker

Dockerネットワークとは

Dockerネットワークは、コンテナ間やホストマシンとの通信を行うための仕組みです。 例えば、Webサーバー、APサーバー、DBサーバーの三層構造でシステムを構築する場合、互いの通信無しでは構築できません。

Dockerではデフォルトで3つのネットワークが作成されます。 これらは、docker network lsコマンドを実行することで確認できます。

ネットワークの一覧表示
$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
84ee3eb8011a   bridge    bridge    local
2608c71b8b1b   host      host      local
08401ea912ba   none      null      local

この中でNAMEがbridgeのネットワークがデフォルトとなっており、ネットワークを指定しないすべてのコンテナはbridgeに属します。 後述しますが、DRIVERがbridgeとあるのはブリッジネットワークであることを示しています。 ここでは主にこのブリッジネットワークについて説明をしていきます。

ポートの関連付け

ブリッジネットワークの説明の前に、ホストとコンテナのポートの関連付けについて説明をしておきます。

例えば、NGINXのコンテナを作成します。 NGINXではデフォルトで80番ポートが使用されますが、http://localhostとしてもページは表示されません。 よくよく考えてみると、仮に80番ポートを使用しているNGINXのコンテナが複数作成した場合に、どのコンテナにアクセスすればいいかわからなくなります。

そこで、ホストのポートとコンテナ及びコンテナのポートを関連づけます。 要はホストのXX番ポートにアクセスした場合、対象コンテナのXX番ポートにアクセスしたことにするといった感じです。 この設定は、docker container runコマンドの-pオプションで{ホストのポート}:{コンテナのポート}を指定することで行います。

ポートの関連付け
$ docker container run -p 3000:80 -d --rm --name web1 nginx
$ docker container run -p 3001:80 -d --rm --name web2 nginx

上記の場合、http://localhost:3000とすればweb1の80番ポートにアクセスしたことになり、http://localhost:3001とすればweb2の80番ポートにアクセスしたことになります。

このポートの設定は、docker portコマンドによって確認することができます。

ポートの関連付けの確認
$ docker port web1
80/tcp -> 0.0.0.0:3000

デフォルトネットワーク

前述の通り、コンテナはデフォルトネットワークとしてbridgeという名前のブリッジネットワークに接続されます。 ブリッジネットワークは、同一ホスト内のコンテナ同士で通信を行うためのネットワークになります。

設定を確認するためにdocker container inspectNetworkSettings.Networksの値を確認してみます。

ネットワーク設定の確認 u1
$ docker container run -itd --name u1 ubuntu bash
$ docker container inspect --format '{{json .NetworkSettings.Networks}}' u1
{
    "bridge": {
        "IPAMConfig":null,
        "Links":null,
        "Aliases":null,
        "NetworkID":"3990788d3dfb5317c7b2dbcfcb3bb22ac2524c3f9a6fe0de3c1de79f00dbcc20",
        "EndpointID":"2cd95efbb28c0c6bd07a256d100a000f8d908b382f1bb216ebcc04ced1c4cb35",
        "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
    }
}

最初のbridgeはネットワークの名前です。つまり、bridgeのネットワークに接続されていることを表します。 さらに、IPアドレスに172.17.0.2、ゲートウェイに172.17.0.1が設定されていることがわかります。

さらにもう1つコンテナを作成してみます。

ネットワーク設定の確認 u2
$ docker container run -itd --name u2 ubuntu bash
$ docker container inspect --format '{{json .NetworkSettings.Networks}}' u2
{
    "bridge": {
        "IPAMConfig":null,
        "Links":null,
        "Aliases":null,
        "NetworkID":"3990788d3dfb5317c7b2dbcfcb3bb22ac2524c3f9a6fe0de3c1de79f00dbcc20",
        "EndpointID":"ea1efded02ba5851a53e5578236d677dd7fc2d72425a3eccc9144114d5bb1507",
        "Gateway":"172.17.0.1",
        "IPAddress":"172.17.0.3",
        "IPPrefixLen":16,
        "IPv6Gateway":"",
        "GlobalIPv6Address":"",
        "GlobalIPv6PrefixLen":0,
        "MacAddress":"02:42:ac:11:00:03",
        "DriverOpts":null
    }
}

設定からu1と同じくbridgeに接続されていることがわかります。 同じネットワーク上に存在することから、u1u2は互いに通信できるはずです。 では実際にこの2つのコンテナが通信できることを確認してみます。

コンテナ間通信の確認 u1 → u2
$ docker container exec u1 ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.061 ms
コンテナ間通信の確認 u2 → u1
% docker container exec u2 ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.060 ms

デフォルトではpingコマンドはインストールされていないため、apt-get update, apt-get install iputils-pingを事前に実行しておきます。

互いにPING応答があることから通信できていることがわかります。

ネットワークの作成

コンテナはデフォルトネットワークであるbridgeに接続することになりますが、関連性のないコンテナ同士が通信できてしまうのはよくありません。 そこで、docker network createコマンドでネットワークを作成し、作成したネットワークに接続をします。

ネットワークの作成
$ docker network create my-network
976249e112709ff51735b239f8e3b4535dfb3f99a42ea8d7724b0546b77b55d5
$ docker network ls
NETWORK ID     NAME         DRIVER    SCOPE
3990788d3dfb   bridge       bridge    local
2608c71b8b1b   host         host      local
976249e11270   my-network   bridge    local
08401ea912ba   none         null      local

上記コマンドにより、my-networkという名前のブリッジネットワークが作成できました。 作成したネットワークにコンテナを接続するには、docker container runコマンドの--networkオプションを指定します。

ネットワークの設定
$ docker container run -itd --name u3 --network my-network ubuntu bash

作成したコンテナのネットワーク設定を確認します。

ネットワーク設定の確認
$ docker container inspect --format '{{json .NetworkSettings.Networks}}' u3
{
    "my-network": {
        "IPAMConfig":null,
        "Links":null,
        "Aliases":["5ae5c71cb7be"],
        "NetworkID":"976249e112709ff51735b239f8e3b4535dfb3f99a42ea8d7724b0546b77b55d5",
        "EndpointID":"bbcfc63b2ae6a4c7a2054b2ec66edac0a0c3b210e4e6c89a1d93bbe5459a946d",
        "Gateway":"172.19.0.1",
        "IPAddress":"172.19.0.2",
        "IPPrefixLen":16,
        "IPv6Gateway":"",
        "GlobalIPv6Address":"",
        "GlobalIPv6PrefixLen":0,
        "MacAddress":"02:42:ac:13:00:02",
        "DriverOpts":null
    }
}

設定からmy-networkに接続されていることがわかります。 また先ほど作成したu1u2とゲートウェイが異なっていることからも、別のネットワークであることがわかります。 念の為、u1と通信できないことを確認します。

コンテナ間通信の確認 u1 → u3
$ docker container exec u1 ping -w 5 172.19.0.2
PING 172.19.0.2 (172.19.0.2) 56(84) bytes of data.

--- 172.19.0.2 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 4133ms
コンテナ間通信の確認 u3 → u1
$ docker container exec u3 ping -w 5 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.

--- 172.17.0.2 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 4119ms

次にmy-networkに接続するu4コンテナを作成し、u3と通信できることを確認します。

ネットワークの設定
$ docker container run -itd --name u4 --network my-network ubuntu bash
#IPの確認は省略(172.19.0.3)
コンテナ間通信の確認 u3 → u4
$ docker container exec u3 ping 172.19.0.3
PING 172.19.0.3 (172.19.0.3) 56(84) bytes of data.
64 bytes from 172.19.0.3: icmp_seq=1 ttl=64 time=0.085 ms
コンテナ間通信の確認 u4 → u3
$ docker container exec u4 ping 172.19.0.2
PING 172.19.0.2 (172.19.0.2) 56(84) bytes of data.
64 bytes from 172.19.0.2: icmp_seq=1 ttl=64 time=0.067 ms

一般的にはデフォルトネットワークではなく、作成したネットワークを使用します。 理由の1つとして、デフォルトネットワークを使用していた場合、意図せず関連のないコンテナが介入することがあるからです。 明示的にネットワークを指定すれば、このようなミスは減るはずです。 またネットワークの管理や設定の自由度の観点からも作成したネットワークを使う方がよいとされています。

ネットワークの管理

これまでにネットワークの一覧表示(docker network ls)とネットワークの作成(docker network create)のコマンドを使用しました。 以下よりその他のネットワークに関するコマンドについて説明をします。

詳細表示

ネットワークの詳細表示は、docker network inspectコマンドを実行します。 Containersは接続されているコンテナの一覧になります。

ネットワークの詳細表示
$ docker network inspect my-network
[
    {
        "Name": "my-network",
        "Id": "976249e112709ff51735b239f8e3b4535dfb3f99a42ea8d7724b0546b77b55d5",
        "Created": "2024-02-24T12:09:50.304071296Z",
        "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": {
            "2c98bc67ff22bd5455893bf520b9e1f44282cd8e21ce16bad1fa978c723e88f0": {
                "Name": "u4",
                "EndpointID": "4e4a401f108f76e53ccd46d45ac0ace49f01462726b9eb8c07d17ffd6933187a",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            },
            "5ae5c71cb7be1298b232a0547283c40524f9c8915bfa594aa871ea94b07003b6": {
                "Name": "u3",
                "EndpointID": "bbcfc63b2ae6a4c7a2054b2ec66edac0a0c3b210e4e6c89a1d93bbe5459a946d",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

接続/切断

ネットワークの接続は、docker container runコマンドの--networkオプション以外に、docker network connectコマンドによって可能です。 あらかじめ作成されたコンテナに対してネットワークを接続する場合に使用します。

ネットワークの接続
$ docker container run -itd --name u5 ubuntu bash
$ docker network connect my-network u5

$ docker container inspect --format '{{json .NetworkSettings.Networks}}' u5
{
    "bridge": {
        "IPAMConfig":null,
        "Links":null,
        "Aliases":null,
        "NetworkID":"3990788d3dfb5317c7b2dbcfcb3bb22ac2524c3f9a6fe0de3c1de79f00dbcc20",
        "EndpointID":"babc5aee02c971d7a99a3f96f113348aea4695059519baf328419839037cf35b",
        "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
    },
    "my-network":{
        "IPAMConfig":{},
        "Links":null,
        "Aliases":["c0c50bedecfb"],
        "NetworkID":"976249e112709ff51735b239f8e3b4535dfb3f99a42ea8d7724b0546b77b55d5",
        "EndpointID":"9a5e5368ec3f1128c6ba9a831cd5f251d63b0d26196662e9491bb0febc509ea1",
        "Gateway":"172.19.0.1",
        "IPAddress":"172.19.0.4",
        "IPPrefixLen":16,
        "IPv6Gateway":"",
        "GlobalIPv6Address":"",
        "GlobalIPv6PrefixLen":0,
        "MacAddress":"02:42:ac:13:00:04",
        "DriverOpts":{}
    }
}

上記は、新しく作成したu5コンテナをmy-networkに接続しています。 しかし、ネットワーク設定を見ての通りデフォルトのbridgeにも接続されてしまっています。そこで、docker network disconnectコマンドによって、不要なネットワークから切断をします。

ネットワークの切断
$ docker network disconnect bridge u5

$ docker container inspect --format '{{json .NetworkSettings.Networks}}' u5
{
    "my-network":{
        "IPAMConfig":{},
        "Links":null,
        "Aliases":["c0c50bedecfb"],
        "NetworkID":"976249e112709ff51735b239f8e3b4535dfb3f99a42ea8d7724b0546b77b55d5",
        "EndpointID":"9a5e5368ec3f1128c6ba9a831cd5f251d63b0d26196662e9491bb0febc509ea1",
        "Gateway":"172.19.0.1",
        "IPAddress":"172.19.0.4",
        "IPPrefixLen":16,
        "IPv6Gateway":"",
        "GlobalIPv6Address":"",
        "GlobalIPv6PrefixLen":0,
        "MacAddress":"02:42:ac:13:00:04",
        "DriverOpts":{}
    }
}

削除

ネットワークを削除する場合は、docker network rmコマンドを使用します。 コンテナが接続されている場合は削除できません。

ネットワークの削除
$ docker network rm test-network

またdocker network pruneコマンドによって、接続のないネットワークを一括で削除することができます。

ネットワークの一括削除
$ docker network prune

IPアドレスの設定

ネットワークを作成するdocker network createコマンドでは、オプションによって以下のようなIPアドレスに関する設定ができます。

  • --gateway: ゲートウェアいのIPアドレスを設定
  • --ip-range: 自動でコンテナに割り当てるIPの範囲を設定
  • --subnet: サブネットマスクを設定
IPアドレス設定例
$ docker network create --subnet '172.20.1.0/24' --gateway '172.20.1.254' --ip-range '172.20.1.1/30' test-network
0f7f8536b14d401ca09ecd25b0cbcc2741d457c4d8dea75c89683334f8eecd60

$ docker network inspect test-network
[
    {
        "Name": "test-network",
        "Id": "d0a26f6bc7c37a601c6b776a458cfd08fd01476b822388259c9e433e57a66770",
        "Created": "2024-02-24T14:39:46.415690044Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.20.1.0/24",
                    "IPRange": "172.20.1.1/30",
                    "Gateway": "172.20.1.254"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

--ip-rangeの設定はサブネットマスクと同じ考え方で、上記の場合だと172.16.20.0は使用できないため、172.20.1.1172.20.1.3までのIPがコンテナに割り当てられます。

ネットワーク設定の確認
$ docker container run -itd --name u6 --network test-network ubuntu bash

$ docker container inspect --format '{{json .NetworkSettings.Networks}}' u6
{
    "test-network": {
        "IPAMConfig": null,
        "Links": null,
        "Aliases": ["40e7faa9ca94"],
        "MacAddress": "02:42:ac:14:01:01",
        "NetworkID": "d0a26f6bc7c37a601c6b776a458cfd08fd01476b822388259c9e433e57a66770",
        "EndpointID": "2dd9652f82e32d8fd064172c2f753dec041155865232b50c6d4a270c1c8dd60b",
        "Gateway": "172.20.1.254",
        "IPAddress": "172.20.1.1",
        "IPPrefixLen": 24,
        "IPv6Gateway": "",
        "GlobalIPv6Address": "",
        "GlobalIPv6PrefixLen": 0,
        "DriverOpts": null
    }
}

上記のIPすべてが使用されている場合は以下のようにエラーになります。

IPアドレスの割り当てエラー
$ docker container run -itd --name u9 --network test-network ubuntu bash 
9e9e19d0c28ffbc0e0e0ca32b7ac1598ce06dcbe1c87fb9f417e8d7ff2566024
docker: Error response from daemon: no available IPv4 addresses on this network's address pools: test-network 
/(d0a26f6bc7c37a601c6b776a458cfd08fd01476b822388259c9e433e57a66770).

--ip-rangeはあくまで自動で設定されるIPアドレスの範囲です。 docker container runコマンドでは、--ipオプションによって自身のIPアドレスを設定することができます。 このようにIPアドレスを明示的に指定する場合は、--ip-rangeの範囲外でも設定できます。

IPアドレスの指定
$ docker container run -itd --name u9 --network test-network --ip '172.20.1.10' ubuntu bash

$ docker container inspect --format '{{json .NetworkSettings.Networks}}' u9
{
    "test-network": {
        "IPAMConfig": {
            "IPv4Address": "172.20.1.10"
        },
        "Links": null,
        "Aliases": ["e9855f0b09e9"],
        "MacAddress": "02:42:ac:14:01:0a",
        "NetworkID": "d0a26f6bc7c37a601c6b776a458cfd08fd01476b822388259c9e433e57a66770",
        "EndpointID": "68082e7eee4fc519eabebab7aabfe3a6d679821ed70c5dc07a4108b71bb1ecd0",
        "Gateway": "172.20.1.254",
        "IPAddress": "172.20.1.10",
        "IPPrefixLen": 24,
        "IPv6Gateway": "",
        "GlobalIPv6Address": "",
        "GlobalIPv6PrefixLen": 0,
        "DriverOpts": null
    }
}

名前解決

自身で作成したネットワークであれば、IPをコンテナ名で名前解決してくれます。 例えばu4 → u3のPINGをIPではなく、コンテナ名で実行しても応答が返ってきます。

名前解決の確認
$ docker container exec u4 ping u3        
PING u3 (172.19.0.2) 56(84) bytes of data.
64 bytes from u3.my-network (172.19.0.2): icmp_seq=1 ttl=64 time=0.055 ms

デフォルトネットワークを使用している場合は、名前解決されない点に注意してください。

名前解決の確認
$ docker container exec u2 ping u1     
ping: u1: Name or service not known

コンテナ名以外にエイリアスとして別名をつけることができます。 docker container runコマンドの場合は--network-aliasオプション、docker network connectコマンドの場合は--aliasオプションを使用します。 エイリアスを追加したとしても、コンテナ名での名前解決もそのまま使用できます。

エイリアスの設定(docker container run)
$ docker container run -itd --name u21 --network my-network --network-alias 'test' ubuntu

$ docker % docker container exec u3 ping test
PING test (172.19.0.5) 56(84) bytes of data.
64 bytes from u21.my-network (172.19.0.5): icmp_seq=1 ttl=64 time=0.109 ms
エイリアスの設定(docker network connect)
$ docker container run -itd --name u22 ubuntu
$ docker network connect --alias 'test2' my-network u22

$ docker container exec u3 ping test2
PING test2 (172.19.0.6) 56(84) bytes of data.
64 bytes from u22.my-network (172.19.0.6): icmp_seq=1 ttl=64 time=0.087 ms

ネットワークの種類

最後にネットワークの種類について簡単に触れておきます。 これまで説明してきたブリッジネットワーク以外に以下のネットワークがあります。

ネットワークドライバー説明
ホストネットワークhostホストのネットワーク名前空間を共有する(Linux)
オーバーレイネットワークoverlay異なるホストにあるDockerデーモン間の通信を行う
macvlanネトワークmacvlan仮想NICを作成し、固有のMACアドレスを割り当てることで外部との通信を可能にする
設定なしnullネットワークを利用しない(デフォルトで作成されているnoneを使用する)