Yuki's Tech Blog

仕事で得た知見や勉強した技術を書きます。

【さわって学ぶクラウドインフラ docker 基礎からのコンテナ構築】第6章 コンテナのネットワークで知らなかったことをざっくりまとめてみた

目次

概要

Dockerホスト上では、複数の独立したコンテナを実行する事ができます。時にはコンテナ間での通信が必要な場面があるので、コンテナのネットワークについて学びます。

3つのネットワーク

Dockerでは、 さまざまなネットワークを作り、Dockerホストとコンテナ、またはコンテナ間で通信する事ができます。 以下のコマンドでDockerが管理するネットワークを確認できます。

ubuntu@ip-10-0-12-65:~$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
49ea1ad2bf81   bridge    bridge    local
d8850b15265b   host      host      local
a8e3cb52fef7   none      null      local
ubuntu@ip-10-0-12-65:~$

規定では「bridge」「host」「none」という3つのネットワークがあります。このうちよく使われるのが「bridge」ネットワークです。

bridgeネットワーク

bridgeネットワークとは、 デフォルトのネットワークのことです。docker run(もしくはdocker create)でコンテナを作成する場合、ネットワークオプションを指定しなかったら、このネットワークが使われます。このネットワークにコンテナが属すことになります。 bridgeネットワークにおいては、それぞれのコンテナのネットワークは独立しており、-pオプションでどのコンテナ(またはDockerホスト)と通信するかを決めます。

(注) bridgeネットワークはIPマスカレード(ポート番号を利用することで、1つのグローバルIPアドレスと複数のプライベートIPアドレスを紐付けて変換する技術)を使って構成されています。

コンテナに割り当てられているIPアドレスを確認する

DockerホストやDockerコンテナは一つの仮想的なbirdgeネットワークによて接続されます。ネットワーク通信なので、DockerホストやDockerコンテナにはIPアドレスが付与されています。

コンテナのIPアドレスを確認するためには、コンテナを起動させて、docker container inspectコマンドを実行します。このうち「NetworkSettings」の部分にIPアドレスが記載されています。

(※) コンテナのIPアドレスを確認するためには、docker container inspect以外に、コンテナ内でipコマンドやifconfigコマンドを実行して確認する事ができます。イメージによっては、軽量化のためにipコマンドやifconfigコマンドが入っていない場合もあります。

docker container inspect コンテナ名
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "ff3b23c1fcb196bcc3ad90310c87b73d56a21977c04ea38b6c34aae574a829ec",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {
                "80/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "8080"
                    },
                    {
                        "HostIp": "::",
                        "HostPort": "8080"
                    }
                ]
            },
            "SandboxKey": "/var/run/docker/netns/ff3b23c1fcb1",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "a2e7c6bb5d3fa4c254754499871c086bb39f54ca7e53fda8cd28472a95fbce48",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "49ea1ad2bf81b1c4315446f90cb1e10339d886434673c2e1c78146875beaa801",
                    "EndpointID": "a2e7c6bb5d3fa4c254754499871c086bb39f54ca7e53fda8cd28472a95fbce48",
                    "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
                }
            }
        }

docker container inspectを実行すると、そのコンテナの全情報が表示されるので、--formatオプションを指定して特定の項目だけ表示することもできます。以下のコマンドでは、IPアドレスの部分だけを取得しています。

docker container inspect --format="{{.NetworkSettings.IPAddress}}" コンテナ名

コンテナ同士の通信

コンテナはそれぞれIPアドレスを持ち、bridgeネットワークに接続されています。そのため、コンテナ同士は、このネットワークを通じて互いに通信できます。 コンテナ間の通信の場合、-pオプションを指定しなくても可能です。-pオプションはあくまでDockerホストで受信したリクエストをDockerコンテナの特定のポートに転送するための設定です。Dockerコンテナ間で通信する場合は、Dockerホストを経由しないので、-pオプションは関係ないです。

コンテナのシェルを立ち上げて、curlコマンドやpingコマンドを使うことで、別のコンテナと通信できるかを確認できます。

作業手順

手順1:
以下のコマンドを実行して、ubuntuのコンテナを立ち上げます。

docker run --rm -it ubuntu /bin/bash

手順2:
ip、pingcurlという3つのコマンドを使いたいので、aptコマンドでインストールします。

apt update
apt -y upgrade
apt install -y iproute2 iputils-ping curl

手順3:
ipコマンドを入力して、コンテナ自身のIPアドレスを確認します。

ip address

コンテナのIPアドレスは172.17.0.4である事が分かりました。

root@00f8a5490627:/# ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

手順4:
既に存在しているコンテナに対してpingコマンドを使って疎通確認をします。結果が「0% packet loss」になっていれば疎通できています。

root@00f8a5490627:/# ping -c 4 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.104 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.063 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.066 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.065 ms

--- 172.17.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3054ms
rtt min/avg/max/mdev = 0.063/0.074/0.104/0.017 ms
root@00f8a5490627:/# ping -c 4 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.098 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.066 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.067 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.065 ms

--- 172.17.0.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3058ms
rtt min/avg/max/mdev = 0.065/0.074/0.098/0.013 ms

(※) -c 4は4回試したら終わるオプションです。オプションを省略すると、ずっと動きっぱなしになるので、ctrl + cで停止します。

手順5:
curlコマンドでWebサーバーに接続してコンテンツを取得できるかを確認します。ポートは省略すると既定のポート80番に接続されます。

curl http://IPアドレス/
root@00f8a5490627:/# curl http://172.17.0.2
<html><body><h1>It works!</h1></body></html>

(注) pingコマンドやcurlコマンドにIPアドレスではなくコンテナ名を指定すると、エラーになるので、気をつけましょう。

Dockerのネットワークを新規に作成して通信を分ける

コンテナ間で通信するためには、IPアドレスを指定する必要があります。しかし、コンテナのIPアドレスをdocker container inspectで確認しないといけないので、面倒です。 コンテナ名で通信するためには、Dockerネットワークを新規作成するか、linkオプションを指定します。linkオプションは推奨されていないので、コンテナ名でコンテナ間通信をするために、Dockerネットワークを作成します。Dockerネットワークを作成して、その中に複数のコンテナを所属させれば、そのDockerネットワークに所属しているコンテナ間でコンテナ名を使って通信ができます。

Dockerネットワーク

Dockerネットワークとは、 ユーザーが作成した任意のネットワークのことです。 ネットワークを作ることで、「あるコンテナは、こちらのネットワークに、別のコンテナはこちらのネットワークに」というように、別々のネットワークにコンテナを所属させる事ができます。

Dockerネットワークを作ると、その数だけ、Dockerホスト上には「br-DockerのネットワークIDの先頭」という名前のネットワークインターフェースが作られます。

Dockerネットワークのメリット

Dockerネットワークのメリットは、コンテナ間で通信するときに、コンテナ名を指定して通信ができるところです。bridgeネットワークで問題となっていたIPアドレスでしか通信できないという問題を解決します。

Dockerネットワークを作る

以下のコマンドを実行して、Dockerネットワークを作成します。

docker network create ネットワーク名

(※) Dockerネットワークを作成すると、それに伴い、Dockerホストにネットワークインターフェースが追加されます。

ubuntu@ip-10-0-12-65:~$ docker network create mydockernet
51b0d74b3e53d3e295802c483dfe40d22b02ebc6481d89a51677d372ad59a566
ubuntu@ip-10-0-12-65:~$ docker network ls
NETWORK ID     NAME          DRIVER    SCOPE
49ea1ad2bf81   bridge        bridge    local
d8850b15265b   host          host      local
51b0d74b3e53   mydockernet   bridge    local
a8e3cb52fef7   none          null      local

新しく「mydockernet」という名前のネットワークがbridgeというドライバーで作成された事が分かります。

Dockerネットワークの詳細情報を見るためには、以下のコマンドを実行します。

docker network inspect Dockerネットワーク名
ubuntu@ip-10-0-12-65:~$ docker network inspect mydockernet
[
    {
        "Name": "mydockernet",
        "Id": "51b0d74b3e53d3e295802c483dfe40d22b02ebc6481d89a51677d372ad59a566",
        "Created": "2022-11-23T06:46:47.833364427Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

Dockerネットワーク内にコンテナを作る

Dockerネットワーク内にコンテナを作成するためには、--netオプションを指定します。

docker run -dit --name web01 -p 8080:80 --net Dockerネットワーク名 httpd:2.4

docker network inspect ネットワーク名で、Containersの部分にネットワークに所属させたコンテナが存在すればOKです。

既存のコンテナを別のDockerネットワークに所属させる

既存のコンテナを別のDockerネットワークに所属させるためには、docker network connectコマンドおよびdocker network disconnectコマンドを使用します。docker network connectでコンテナをネットワークに所属させたり、docker network disconnectでコンテナをネットワークから切断したりできます。

docker network connect ネットワーク名(ネットワークID) コンテナ名(コンテナID)
docker network disconnect ネットワーク名(ネットワークID) コンテナ名(コンテナID)

bridgeネットワークから別のDockerネットワークにコンテナを所属させるためには、以下のコマンドを実行します。

docker network disconnect bridge web01
docker network connect mydockernet web01

コンテナ名で通信ができることを確認する

Dockerネットワーク内のコンテナ間では、以下のようにpingcurlでコンテナ名を指定して通信する事ができます。

root@9ed0ae318511:/# ping -c 4 web01
PING web01 (172.18.0.2) 56(84) bytes of data.
64 bytes from web01.mydockernet (172.18.0.2): icmp_seq=1 ttl=64 time=0.085 ms
64 bytes from web01.mydockernet (172.18.0.2): icmp_seq=2 ttl=64 time=0.082 ms
64 bytes from web01.mydockernet (172.18.0.2): icmp_seq=3 ttl=64 time=0.069 ms
64 bytes from web01.mydockernet (172.18.0.2): icmp_seq=4 ttl=64 time=0.068 ms

--- web01 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3060ms
rtt min/avg/max/mdev = 0.068/0.076/0.085/0.007 ms
root@9ed0ae318511:/#
root@9ed0ae318511:/# curl http://web01
<html><body><h1>It works!</h1></body></html>

(※)Dockerネットワークを使ってコンテナ間で通信する際にコンテナ名でアクセスできる理由は、Dockerが用意しているDNSサーバーを使って、コンテナ名とIPアドレスを紐づけているからです。

Dockerネットワークの削除

ネットワークを削除するためには、まずはネットワークを利用しているコンテナを削除 or ネットワークから切断する必要があります。その後Dockerネットワークを削除できます。 以下のコマンドでDockerネットワークを削除できます。

docker network rm Dockerネットワーク名

(※) Dockerネットワークを削除すると、それに対応するDockerホストのインターフェースも削除されます。

hostネットワーク

hostネットワークでは、IPマスカレードを使わずに、コンテナがホストのIPアドレスを共有します。あまり使わないので、使う時になったら調べます。

noneネットワーク

noneネットワークでは、コンテナはネットワークに接続されません。docker run(もしくはdocker create)の際に、--net noneを指定します。または、docker network disconnectでネットワークから切断しても同じ状態になります。セキュリティを高めたいなどの理由でコンテナをネットワーク通信から隔離したい時に使います。hostネットワークと同様、あまり使わないので、使う時になったら調べます。

参考記事

さわって学ぶクラウドインフラ docker基礎からのコンテナ構築 | 大澤 文孝, 浅居 尚 |本 | 通販 | Amazon

Docker の bridge と host ネットワークについて勉強する - Qiita

第29回 Docker Networkingの基礎知識 標準的なネットワークを理解する:古賀政純の「攻めのITのためのDocker塾」(5/5 ページ) - ITmedia エンタープライズ

【 ping 】コマンド/【 ping6 】コマンド――通信相手にパケットを送って応答を調べる:Linux基本コマンドTips(143) - @IT

IPマスカレード