🪶

Dockerのブリッジ・ネットワークを利用したコンテナ間通信を行う

2023/08/19に公開

概要

ブリッジ・ネットワークを利用したDockerコンテナのコンテナ間通信について、公式ドキュメントの内容を参考にしつつ解説します。

Dockerのネットワーク機能の種類

Dockerのネットワーク機能は以下の7種類に分類されます。
本記事ではこのうち、bridgeを用いた接続を行います。
参考:https://docs.docker.jp/network/index.html

  • bridge

デフォルトのネットワーク・ドライバです。ネットワーク作成時にドライバを指定しなければ、このネットワークになります。 通常、ブリッジ・ネットワークは、アプリケーションがスタンドアロン・コンテナ内で動作する時、このコンテナが通信するために使います 。詳しくは ブリッジ・ネットワーク をご覧ください。

  • host
  • overlay
  • ipvlan
  • macvlan
  • none

ブリッジ・ネットワークについて

Dockerにおけるブリッジは、単一ホスト内で動作するコンテナ同士の通信を可能にするネットワークドライバです。
単一ホスト内に複数のブリッジ・ネットワークを作成することができ、同じブリッジネットワークに接続されたコンテナ間では相互に通信を行うことができます。

デフォルトではbridgeという名前のブリッジ・ネットワークが存在しており、ネットワークのオプション指定なしで作成したコンテナはこのネットワークに接続されます。
Dockerのネットワーク一覧はdocker network lsコマンドで確認できます。
ネットワークを作成していない状態で上記のコマンドを実行すると以下の3つが表示され、一番上に表示されているものがデフォルトのブリッジ・ネットワークです。

$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
38bcf0d1750a   bridge    bridge    local
af1b8f32ee2a   host      host      local
d1e9a5e4a34d   none      null      local

実践

単一コンテナの起動

まずは単一のコンテナを作成して、デフォルトのbridgeに接続されていることを確認します。
確認には軽量のlinuxイメージであるalpineを使います。

以下のコマンドでalpineのコンテナが起動します。
詳細は省きますが、-iおよび-tオプションを付与することでコンテナ内でシェルを起動し、ホストマシンの標準入出力を通してコンテナのシェルを操作できるようになります。
また、-dオプションを付与することでコンテナをバックグラウンドで起動できます。
docker run -dit --name alpine1 alpine

$ docker run -dit --name alpine1 alpine
f7f1a42b2db0666f37b2fb43ec1c84c5ffa9e83a7dff5aad6edfccc66c547ef6
$ docker ps
CONTAINER ID   IMAGE     COMMAND     CREATED         STATUS         PORTS     NAMES
f7f1a42b2db0   alpine    "/bin/sh"   4 seconds ago   Up 3 seconds             alpine1

コンテナが起動できていることを確認できました。

続いて、bridgeの詳細を確認して先程のコンテナが接続されていることを確認します。
ネットワークの詳細はdocker network inspect {ネットワーク名}で確認できます。

$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "38bcf0d1750a68312400606aa88bd0dba5b4a6bbc3fd7c4a95875e46c80d93ea",
        "Created": "2023-08-19T00:23:45.468693593+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "f7f1a42b2db0666f37b2fb43ec1c84c5ffa9e83a7dff5aad6edfccc66c547ef6": {
                "Name": "alpine1",
                "EndpointID": "0da982bcb71279848ba59b144b69d0e2c806f95dbceced7f6aba6e79aa06cf8b",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "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": "1500"
        },
        "Labels": {}
    }
]

IPAMの欄を見るとサブネットアドレスやデフォルトゲートウェイの情報が確認できます。
172.17.0.0/16がこのブリッジ・ネットワークのサブネットアドレスになっていることが分かります。

        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },

Containersの欄を確認すると、先ほど作成したalpine1が接続されていることが分かります。
ipv4Addressを確認すると127.17.0.2が割り振られていることが分かります。

        "Containers": {
            "f7f1a42b2db0666f37b2fb43ec1c84c5ffa9e83a7dff5aad6edfccc66c547ef6": {
                "Name": "alpine1",
                "EndpointID": "0da982bcb71279848ba59b144b69d0e2c806f95dbceced7f6aba6e79aa06cf8b",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },

図にするとこんな感じです。

複数コンテナ

続いて、デフォルトネットワークにコンテナを追加してみます。

$ docker run -dit --name alpine2 alpine
04bb63f52c335b470eb1f5d663ee9f354e3230929b8245ca5dbd555ee13f51d8

先ほどと同様、docker network inspect bridgeで詳細を確認すると以下のようになっています。
(Containersの欄のみ抜粋)

        "Containers": {
            "04bb63f52c335b470eb1f5d663ee9f354e3230929b8245ca5dbd555ee13f51d8": {
                "Name": "alpine2",
                "EndpointID": "5b715b50e0668207b29f1919b12968a3a1b0f3f68543cd1ec3b27248b3b38b57",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "f7f1a42b2db0666f37b2fb43ec1c84c5ffa9e83a7dff5aad6edfccc66c547ef6": {
                "Name": "alpine1",
                "EndpointID": "0da982bcb71279848ba59b144b69d0e2c806f95dbceced7f6aba6e79aa06cf8b",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }

alpine2が追加されていることが分かります。
図にすると以下のようになります。

続いて、alpine1とalpine2の間で通信が行えることを確認します。
docker attach {コンテナ名}コマンドでコンテナのシェルにアタッチできます。

$ docker attach alpine1
/ # 

pingコマンドでalpine2(172.17.0.3)に通信できることを確認します。
-cコマンドでパケットを送信する回数を指定できます。

/ # ping -c 2 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.146 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.159 ms

--- 172.17.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.146/0.152/0.159 ms

確認が終了したら、Ctrl + p Ctrl + qコマンドでコンテナのシェルからデタッチできます。
このとき、exitコマンドなどを実行してしまうとコンテナがシャットダウンされてしまうので注意してください。

複数コンテナ・複数ネットワーク

docker network create --driver bridge {ネットワーク名}コマンドでブリッジ・ネットワークが新規に作成できます。--driverオプションはデフォルトでbridgeになっているので今回の場合は無しでも結果は変わりません。

$ docker network create --driver bridge alpine-net
c54fb5965e677cf26fceca357767eac4210c1a67c22959ae45362ed6648ff504

alpine-netが作られていることを確認します。

$ docker network ls
NETWORK ID     NAME         DRIVER    SCOPE
c54fb5965e67   alpine-net   bridge    local
38bcf0d1750a   bridge       bridge    local
af1b8f32ee2a   host         host      local
d1e9a5e4a34d   none         null      local

続いて、先程作成したalpine-netにコンテナを追加します。

$ docker run -dit --name alpine3 --network alpine-net alpine
6b17f38ef3ac75b6828b518f2fc51f820cf3b3b2d856ab21da9f24655b786112
$ docker run -dit --name alpine4 --network alpine-net alpine
72333b1c452b2f49f9cc7edf73ceb8b2a5fcf82c44781c50395e340985d580dc

alpine-netにコンテナが追加されていることを確認します。

$ docker network inspect alpine-net
[
    {
        "Name": "alpine-net",
        "Id": "c54fb5965e677cf26fceca357767eac4210c1a67c22959ae45362ed6648ff504",
        "Created": "2023-08-19T14:08:07.355322927+09:00",
        "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": {
            "6b17f38ef3ac75b6828b518f2fc51f820cf3b3b2d856ab21da9f24655b786112": {
                "Name": "alpine3",
                "EndpointID": "9e7b579e6cee6c78b2925bd56e750cf8c7b43ddfc03bfbedc1cf376e6f8aa0ff",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            },
            "72333b1c452b2f49f9cc7edf73ceb8b2a5fcf82c44781c50395e340985d580dc": {
                "Name": "alpine4",
                "EndpointID": "97d58ebe62e7955bf02aefffe7fa1625f7c24e85a762f30897d69bf952b57dcb",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

ここまでで作成したものを図にすると以下のようになります。

alpine3とalpine4の間で通信が行えることを確認します。

$ docker attach alpine3
/ # ping -c 2 172.19.0.3
PING 172.19.0.3 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.181 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.176 ms

--- 172.19.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.176/0.178/0.181 ms

また、ユーザが作成したブリッジ・ネットワークではコンテナ名による名前解決ができます。
試しにalpine3からalpine4に向けて、コンテナ名を指定してpingを実行してみます。

/ # ping -c 2 alpine4
PING alpine4 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.110 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.170 ms

--- alpine4 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.110/0.140/0.170 ms

デフォルトのブリッジ・ネットワークではコンテナ名による通信が行えません。
alpine1にアタッチして、alpine2に向けてコンテナ名を指定してpingを実行してみます。

$ docker attach alpine1
/ # ping -c 2 alpine2
ping: bad address 'alpine2'

通信に失敗しました。

異なるブリッジネットワーク間での通信も行うことができないため、alpine1とalpine3の通信やalpine2とalpine4の通信もできません。
試しにalpine3にアタッチして、alpine1(172.17.0.2)に向けてpingを実行してみます。

$ docker attach alpine3
/ # ping -c 2 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes

--- 172.17.0.2 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss

パケットが届いていないことが分かります。

今回は取り上げませんでしたが、単一のコンテナが複数のブリッジ・ネットワークに所属することも可能です。
その場合、複数のブリッジネットワーク内に存在するすべてのコンテナと通信ができるようになります。

まとめ

Dockerのブリッジ・ネットワークを作成し、コンテナ間の通信が行えることを確認しました。

参考

https://docs.docker.com/network/
https://docs.docker.com/network/network-tutorial-standalone/#use-user-defined-bridge-networks
https://docs.docker.jp/network/index.html
https://docs.docker.jp/network/bridge.html
https://tech.quartetcom.co.jp/2022/06/29/docker-bridge-network/

Discussion