スタンドアロンコンテナーのネットワークチュートリアル

読む時間の目安: 10 分

ここに示すチュートリアルは、スタンドアロンの Docker コンテナーに対するネットワークを扱います。 Swarm サービスにおけるネットワークについては、Swarm サービスにおけるネットワーク を参照してください。 Docker ネットワークの全般的なことを確認したい場合は ネットワーク概要 を参照してください。

このトピックには 3 つのチュートリアルがあります。 それぞれは Linux、Windows、Mac 上において実行することができます。 ただし Windows と Mac の場合は、2 つめの Docker ホストを、どこか別に用意することが必要になります。

  • デフォルトのブリッジネットワーク利用 では、Docker が自動的に設定するデフォルトのbridgeネットワークの利用方法を示します。 このネットワークは、本番環境向けには適していません。

  • ユーザー定義のブリッジネットワーク利用 では、独自にブリッジネットワークを生成して、同一の Docker ホスト上で稼動する複数コンテナーに接続して利用する方法を示します。 本番環境においてスタンドアロンコンテナーを稼動させる場合には、この方法が推奨されます。

Swarm サービスにおいて利用されるのは、一般的には オーバーレイネットワーク です。 このオーバーレイネットワークは、スタンドアロンコンテナーに対しても利用することができます。 このことは オーバーレイネットワークのチュートリアル において触れています。

デフォルトのブリッジネットワーク利用

以下の例では、2 つのalpineコンテナーを同じ Docker ホスト上に稼動させます。 そしてテストを行ってみて、コンテナー同士がどのようにやりとりを行うかを見ていきます。 Docker がインストール済みであり、起動していることを確認してください。

  1. ターミナル画面を開きます。 まず初めに、現在のネットワーク一覧を確認しておきます。 ネットワークをまったく追加せず、Docker デーモン上において Swarm の初期化も行っていなければ、以下のような表示になるはずです。 複数のネットワークが表示されるはずであり、最低で以下のものがあるはずです。 (ネットワーク ID は異なります。)

    $ docker network ls
    
    NETWORK ID          NAME                DRIVER              SCOPE
    17e324f45964        bridge              bridge              local
    6ed54d316334        host                host                local
    7092879f2cc8        none                null                local
    

    デフォルトのbridgeネットワークが一覧に表示されます。 これとともにhostnoneがあります。 この 2 つは完全なネットワークではありませんが、コンテナーを起動して Docker デーモンホストのネットワークに直接接続するために、あるいはネットワークデバイスのないコンテナーを起動するために必要となります。 このチュートリアルでは、2 つのコンテナーをbridgeネットワークに接続します。

  2. alpineコンテナーを 2 つ起動してashを実行します。 Alpine のデフォルトシェルがbashではなくashです。 -ditフラグは、コンテナーをデタッチモードで(バックグラウンドで)実行し、対話を行い(入力を可能とし)、TTY を利用する(入出力が確認できる)ことを意味します。 デタッチモードで起動するため、コンテナーに即座に接続されるわけではありません。 その前にコンテナー ID が出力されます。 --networkフラグを何も指定しなかったので、コンテナーはデフォルトのbridgeネットワークに接続されます。

    $ docker run -dit --name alpine1 alpine ash
    
    $ docker run -dit --name alpine2 alpine ash
    

    2 つのコンテナーが実際に開始されたことを確認します。

    $ docker container ls
    
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
    602dbf1edc81        alpine              "ash"               4 seconds ago       Up 3 seconds                            alpine2
    da33b7aa74b0        alpine              "ash"               17 seconds ago      Up 16 seconds                           alpine1
    
  3. bridgeネットワークを参照して、どのコンテナーがこれに接続しているかを確認します。

    $ docker network inspect bridge
    
    [
        {
            "Name": "bridge",
            "Id": "17e324f459648a9baaea32b248d3884da102dde19396c25b30ec800068ce6b10",
            "Created": "2017-06-22T20:27:43.826654485Z",
            "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,
            "Containers": {
                "602dbf1edc81813304b6cf0a647e65333dc6fe6ee6ed572dc0f686a3307c6a2c": {
                    "Name": "alpine2",
                    "EndpointID": "03b6aafb7ca4d7e531e292901b43719c0e34cc7eef565b38a6bf84acf50f38cd",
                    "MacAddress": "02:42:ac:11:00:03",
                    "IPv4Address": "172.17.0.3/16",
                    "IPv6Address": ""
                },
                "da33b7aa74b0bf3bda3ebd502d404320ca112a268aafe05b4851d1e3312ed168": {
                    "Name": "alpine1",
                    "EndpointID": "46c044a645d6afc42ddd7857d19e9dcfb89ad790afb5c239a35ac0af5e8a5bc5",
                    "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": {}
        }
    ]
    

    上の方にbridgeネットワークに関する情報が一覧表示されます。 Docker ホストとbridgeネットワーク間のゲートウェイに対する IP アドレス(172.17.0.1)も表示されています。 Containersキーの配下に、接続されているコンテナーがそれぞれ表示されています。 そこには IP アドレスの情報もあります(alpine1172.17.0.2alpine2172.17.0.3となっています)。

  4. コンテナーはバックグラウンドで実行しています。 docker attachコマンドを使ってalpine1に接続してみます。

    $ docker attach alpine1
    
    / #
    

    プロンプトが#に変わりました。 これはコンテナー内のrootユーザーであることを意味します。 ip addr showコマンドを使って、コンテナー内部からalpine1のネットワークインターフェースを見てみます。

    # ip addr show
    
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
        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
        inet6 ::1/128 scope host
           valid_lft forever preferred_lft forever
    27: eth0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
        link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.2/16 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::42:acff:fe11:2/64 scope link
           valid_lft forever preferred_lft forever
    

    1 つめのインターフェースはループバックデバイスです。 今はこれを無視します。 2 つめのインターフェースの IP アドレスは172.17.0.2となっています。 前の手順で確認したalpine1のアドレスと同じです。

  5. alpine1の内部からgoogle.comへの ping を行って、インターネットに接続してみます。 -c 2フラグにより 2 回だけpingを行います。

    # ping -c 2 google.com
    
    PING google.com (172.217.3.174): 56 data bytes
    64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.841 ms
    64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.897 ms
    
    --- google.com ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 9.841/9.869/9.897 ms
    
  6. そこで 2 つめのコンテナーに対して ping してみます。 最初は IP アドレス172.17.0.3を使って ping します。

    # 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.086 ms
    64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.094 ms
    
    --- 172.17.0.3 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.086/0.090/0.094 ms
    

    成功しました。 次にalpine2コンテナーに向けて、コンテナー名により ping をしてみます。 これは失敗します。

    # ping -c 2 alpine2
    
    ping: bad address 'alpine2'
    
  7. alpine1を停止させることなくデタッチします。 これはデタッチを行うキー操作、つまりCTRL+pCTRL+qにより行います(CTRLを押したまま、pqを順に押します)。 この後alpine2に対して同じことをするなら、手順の 4、5、6 をもう一度行います。 alpine1のところはalpine2に変えて実施します。

  8. 2 つのコンテナーを停止させ削除します。

    $ docker container stop alpine1 alpine2
    $ docker container rm alpine1 alpine2
    

デフォルトのbridgeネットワークは、本番環境向けとしては推奨されない点を覚えておいてください。 ユーザー定義のブリッジネットワークについては、次のチュートリアル に進んでください。

ユーザー定義のブリッジネットワーク利用

以下の例では、すでに生成している 2 つのalpineコンテナーをもう一度使います。 ただしこれをアタッチするのは、alpine-netという名前のユーザー定義ネットワークです。 もうデフォルトのbridgeネットワークへの接続は行いません。 そして 3 つめのalpineコンテナーを用意します。 これはbridgeネットワークに接続させるものの、alpine-netには接続しません。 さらに 4 つめのalpineコンテナーを、その両方のネットワークに接続するようにします。

  1. alpine-netネットワークを生成します。 --driver bridgeフラグは不要です。 なぜならそれがデフォルトであるからです。 ただし以下の例では、指定方法を示すために含めます。

    $ docker network create --driver bridge alpine-net
    
  2. Docker のネットワーク一覧を表示します。

    $ docker network ls
    
    NETWORK ID          NAME                DRIVER              SCOPE
    e9261a8c9a19        alpine-net          bridge              local
    17e324f45964        bridge              bridge              local
    6ed54d316334        host                host                local
    7092879f2cc8        none                null                local
    

    alpine-netネットワークを確認します。 そこから IP アドレスがわかります。 また接続されているコンテナーが 1 つもないことがわかります。

    $ docker network inspect alpine-net
    
    [
        {
            "Name": "alpine-net",
            "Id": "e9261a8c9a19eabf2bf1488bf5f208b99b1608f330cff585c273d39481c9b0ec",
            "Created": "2017-09-25T21:38:12.620046142Z",
            "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,
            "Containers": {},
            "Options": {},
            "Labels": {}
        }
    ]
    

    ネットワークのゲートウェイは172.18.0.1となっています。 デフォルトのブリッジネットワークのときとは違っていて、その際には172.17.0.1でした。 IP アドレスの実際は、システムによって異なるかもしれません。

  3. 4 つのコンテナーを生成します。 それぞれの--networkフラグに注目してください。 docker runコマンドの実行において、接続指定できるネットワークはただ 1 つです。 したがってalpine4bridgeにも接続させるために、後からdocker network connectを実行することが必要になります。

    $ docker run -dit --name alpine1 --network alpine-net alpine ash
    
    $ docker run -dit --name alpine2 --network alpine-net alpine ash
    
    $ docker run -dit --name alpine3 alpine ash
    
    $ docker run -dit --name alpine4 --network alpine-net alpine ash
    
    $ docker network connect bridge alpine4
    

    コンテナーすべてが実行していることを確認します。

    $ docker container ls
    
    CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
    156849ccd902        alpine              "ash"               41 seconds ago       Up 41 seconds                           alpine4
    fa1340b8d83e        alpine              "ash"               51 seconds ago       Up 51 seconds                           alpine3
    a535d969081e        alpine              "ash"               About a minute ago   Up About a minute                       alpine2
    0a02c449a6e9        alpine              "ash"               About a minute ago   Up About a minute                       alpine1
    
  4. bridgeネットワークとalpine-netネットワークを再度確認してみます。

    $ docker network inspect bridge
    
    [
        {
            "Name": "bridge",
            "Id": "17e324f459648a9baaea32b248d3884da102dde19396c25b30ec800068ce6b10",
            "Created": "2017-06-22T20:27:43.826654485Z",
            "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,
            "Containers": {
                "156849ccd902b812b7d17f05d2d81532ccebe5bf788c9a79de63e12bb92fc621": {
                    "Name": "alpine4",
                    "EndpointID": "7277c5183f0da5148b33d05f329371fce7befc5282d2619cfb23690b2adf467d",
                    "MacAddress": "02:42:ac:11:00:03",
                    "IPv4Address": "172.17.0.3/16",
                    "IPv6Address": ""
                },
                "fa1340b8d83eef5497166951184ad3691eb48678a3664608ec448a687b047c53": {
                    "Name": "alpine3",
                    "EndpointID": "5ae767367dcbebc712c02d49556285e888819d4da6b69d88cd1b0d52a83af95f",
                    "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": {}
        }
    ]
    

    alpine3alpine4bridgeネットワークに接続されています。

    $ docker network inspect alpine-net
    
    [
        {
            "Name": "alpine-net",
            "Id": "e9261a8c9a19eabf2bf1488bf5f208b99b1608f330cff585c273d39481c9b0ec",
            "Created": "2017-09-25T21:38:12.620046142Z",
            "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,
            "Containers": {
                "0a02c449a6e9a15113c51ab2681d72749548fb9f78fae4493e3b2e4e74199c4a": {
                    "Name": "alpine1",
                    "EndpointID": "c83621678eff9628f4e2d52baf82c49f974c36c05cba152db4c131e8e7a64673",
                    "MacAddress": "02:42:ac:12:00:02",
                    "IPv4Address": "172.18.0.2/16",
                    "IPv6Address": ""
                },
                "156849ccd902b812b7d17f05d2d81532ccebe5bf788c9a79de63e12bb92fc621": {
                    "Name": "alpine4",
                    "EndpointID": "058bc6a5e9272b532ef9a6ea6d7f3db4c37527ae2625d1cd1421580fd0731954",
                    "MacAddress": "02:42:ac:12:00:04",
                    "IPv4Address": "172.18.0.4/16",
                    "IPv6Address": ""
                },
                "a535d969081e003a149be8917631215616d9401edcb4d35d53f00e75ea1db653": {
                    "Name": "alpine2",
                    "EndpointID": "198f3141ccf2e7dba67bce358d7b71a07c5488e3867d8b7ad55a4c695ebb8740",
                    "MacAddress": "02:42:ac:12:00:03",
                    "IPv4Address": "172.18.0.3/16",
                    "IPv6Address": ""
                }
            },
            "Options": {},
            "Labels": {}
        }
    ]
    

    alpine1alpine2alpine4alpine-netネットワークに接続されています。

  5. alpine-netのようなユーザー定義ネットワークでは、IP アドレスによる通信が可能です。 さらにコンテナー名から IP アドレスを解決することもできます。 この機能のことを 自動サービス検出 と呼びます。 ではalpine1に接続して、いろいろテストしてみます。 alpine1は、alpine2alpine4(そしてalpine1そのもの)の IP アドレスが解決できなければなりません。

    $ docker container attach alpine1
    
    # ping -c 2 alpine2
    
    PING alpine2 (172.18.0.3): 56 data bytes
    64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.085 ms
    64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.090 ms
    
    --- alpine2 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.085/0.087/0.090 ms
    
    # ping -c 2 alpine4
    
    PING alpine4 (172.18.0.4): 56 data bytes
    64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.076 ms
    64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.091 ms
    
    --- alpine4 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.076/0.083/0.091 ms
    
    # ping -c 2 alpine1
    
    PING alpine1 (172.18.0.2): 56 data bytes
    64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.026 ms
    64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.054 ms
    
    --- alpine1 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.026/0.040/0.054 ms
    
  6. alpine1からalpine3へはまったく接続できないはずです。 alpine3alpine-netネットワーク上にないからです。

    # ping -c 2 alpine3
    
    ping: bad address 'alpine3'
    

    それだけでなく、alpine1からalpine3へは、IP アドレスを利用しても接続できません。 docker network inspectを使ったbridgeネットワークの確認時の出力結果をもう一度確認してください。 alpine3の IP アドレスは172.17.0.2であったはずです。 そこで ping を行ってみます。

    # 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
    

    デタッチキー操作によりalpine1をデタッチします。 CTRL+pCTRL+qを行います(CTRLを押したまま、pqを順に押します)。

  7. alpine4は、デフォルトのbridgeネットワークとalpine-netネットワークの両方に接続していました。 このコンテナーは、別のコンテナーすべてに接続できるはずです。 ただしそれを行うには、alpine3の IP アドレスを知っておく必要があります。 そこでalpine3にアタッチして、いろいろ確認してみます。

    $ docker container attach alpine4
    
    # ping -c 2 alpine1
    
    PING alpine1 (172.18.0.2): 56 data bytes
    64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.074 ms
    64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.082 ms
    
    --- alpine1 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.074/0.078/0.082 ms
    
    # ping -c 2 alpine2
    
    PING alpine2 (172.18.0.3): 56 data bytes
    64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.075 ms
    64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.080 ms
    
    --- alpine2 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.075/0.077/0.080 ms
    
    # ping -c 2 alpine3
    ping: bad address 'alpine3'
    
    # ping -c 2 172.17.0.2
    
    PING 172.17.0.2 (172.17.0.2): 56 data bytes
    64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.089 ms
    64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.075 ms
    
    --- 172.17.0.2 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.075/0.082/0.089 ms
    
    # ping -c 2 alpine4
    
    PING alpine4 (172.18.0.4): 56 data bytes
    64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.033 ms
    64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.064 ms
    
    --- alpine4 ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 0.033/0.048/0.064 ms
    
  8. 確認の最後として、どのコンテナーもインターネットには接続できるはずですから、google.comへの ping を行ってみます。 alpine4はすでにアタッチしていますから、そこから始めます。 そしてalpine4のデタッチの後にalpine3(これは唯一bridgeネットワークに接続しているものです)に接続して、同じことを確認します。 最終的に(alpine-netネットワークにのみ接続している)alpine1への接続と同様の確認まで進めます。

    # ping -c 2 google.com
    
    PING google.com (172.217.3.174): 56 data bytes
    64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.778 ms
    64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.634 ms
    
    --- google.com ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 9.634/9.706/9.778 ms
    
    CTRL+p CTRL+q
    
    $ docker container attach alpine3
    
    # ping -c 2 google.com
    
    PING google.com (172.217.3.174): 56 data bytes
    64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.706 ms
    64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.851 ms
    
    --- google.com ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 9.706/9.778/9.851 ms
    
    CTRL+p CTRL+q
    
    $ docker container attach alpine1
    
    # ping -c 2 google.com
    
    PING google.com (172.217.3.174): 56 data bytes
    64 bytes from 172.217.3.174: seq=0 ttl=41 time=9.606 ms
    64 bytes from 172.217.3.174: seq=1 ttl=41 time=9.603 ms
    
    --- google.com ping statistics ---
    2 packets transmitted, 2 packets received, 0% packet loss
    round-trip min/avg/max = 9.603/9.604/9.606 ms
    
    CTRL+p CTRL+q
    
  9. コンテナーすべてを停止し削除します。 そしてalpine-netネットワークを削除します。

    $ docker container stop alpine1 alpine2 alpine3 alpine4
    
    $ docker container rm alpine1 alpine2 alpine3 alpine4
    
    $ docker network rm alpine-net
    

その他のネットワークチュートリアル

スタンドアロンコンテナー向けのネットワークチュートリアルを終えたので、以下に示すような別のネットワークチュートリアルも見てください。

networking, bridge, routing, ports, overlay