デフォルトネットワークで EXPOSE 指定の無いポートで通信できちゃう現象に出会いました。 あれ?ネットワーク作ってないのに・・・

Dockerの コンテナネットワークについて、 きちんと理解できていないような気がしてきたので きちんと調べてみました。

環境

root@ubuntu:~# docker version
Client:
 Version:   17.12.0-ce
 API version:   1.35
 Go version:    go1.9.2
 Git commit:    c97c6d6
 Built: Wed Dec 27 20:11:14 2017
 OS/Arch:   linux/amd64

Server:
 Engine:
  Version:  17.12.0-ce
  API version:  1.35 (minimum version 1.12)
  Go version:   go1.9.2
  Git commit:   c97c6d6
  Built:    Wed Dec 27 20:09:47 2017
  OS/Arch:  linux/amd64
  Experimental: false

調査

準備

テストコンテナをデフォルトネットワークに2つ立てます

# docker run -dP --name test1 moremagic/ubuntu-sshd
# docker run -dP --name test2 moremagic/ubuntu-sshd

こんな感じになります

root@ubuntu:~# docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "ef63456b96b3b25c816de8252b5e7d507380eec7c08adf7a25a5df00b24ba861",
        "Created": "2018-01-29T12:02:13.127525987+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": {
            "39018bcf7003c48e8fe4356c98d83dfa0e4e0536879fd42fef02defc393ea2f9": {
                "Name": "test2",
                "EndpointID": "5df80c71fbe1307bff8e8494bf8df70c38d9349a86cc2344e7fbeb95ff84a165",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",
                "IPv6Address": ""
            },
            "ccb0aa9e8ec770dd552aefcf811c709497cfb6b616ae4a0d8d56ac6c4f795a5d": {
                "Name": "test1",
                "EndpointID": "3a769eabedb861821d796e10b93c10d99e54e336b0a9ee539a5c5d303aa8169b",
                "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": {}
    }
]

それぞれのコンテナで 検証ツール類(ping, socat) をインストールしておきます

test1コンテナ

root@ubuntu:~# docker exec -ti test1 bash
root@ccb0aa9e8ec7:/# apt update -y && apt install -y inetutils* socat

test2コンテナ

root@ubuntu:~# docker exec -ti test2 bash
root@39018bcf7003:/# apt update -y && apt install -y inetutils* socat

疎通確認

デフォルトネットワークで検証

test1 → test2 へ ping

root@ubuntu:~# docker exec -ti test1 bash
root@ccb0aa9e8ec7:/#

root@ccb0aa9e8ec7:/# ping test2
ping: unknown host test2

root@ccb0aa9e8ec7:/# 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.057 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.080 ms
^C
--- 172.17.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1016ms
rtt min/avg/max/mdev = 0.057/0.068/0.080/0.014 ms

ホスト名でのPing はできないけど IPアドレスでのPing はできる

test1 -> test2 に SSH(EXPOSEしているポートを使う)

root@ccb0aa9e8ec7:/# ssh test2
ssh: Could not resolve hostname test2: Name or service not known

root@ccb0aa9e8ec7:/# ssh 172.17.0.3
root@172.17.0.3's password:
Welcome to Ubuntu 16.04.1 LTS (GNU/Linux 4.13.0-31-generic x86_64)


root@39018bcf7003:~#

ホスト名でのSSH はできないけど IPアドレスでのSSH はできる

test1 -> test2 に UDP(EXPOSEしてないポートを使う) まずtest2 でリッスンしておきます

root@ubuntu:~# docker exec -ti test2 bash
root@39018bcf7003:/# socat udp-listen:70000 stdout

test1 から test2へ接続します

root@ubuntu:~# docker exec -ti test1 bash
root@ccb0aa9e8ec7:/# socat udp-connect:172.17.0.3:70000 stdin

ここで何か入力すると、test2側で入力した文字列が出力されました。

test1 -> test2 に TCP(EXPOSEしてないポートを使う) まずtest2 でリッスンしておきます

root@ubuntu:~# docker exec -ti test2 bash
root@39018bcf7003:/# socat tcp-listen:70000 stdout

test1 から test2へ接続します

root@ubuntu:~# docker exec -ti test1 bash
root@ccb0aa9e8ec7:/# socat tcp-connect:172.17.0.3:70000 stdin

ここで何か入力すると、test2側で入力した文字列が出力されました。

EXPOSE していない TCP, UDPポートへの接続はできました

Bridgeネットワーク(mynet)での検証

ネットワークを作成します

root@ubuntu:~# docker network create mynet
928f7bb31aeb52d5f2fc6416e22986e50f9d83b50b159819e11a7f37b6bb5398
root@ubuntu:~# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
ef63456b96b3        bridge              bridge              local
e237038b6196        host                host                local
928f7bb31aeb        mynet               bridge              local
b17d8a6746bc        none                null                local

test1test2 コンテナを mynet ネットワークに所属させます

root@ubuntu:~# docker network disconnect bridge test1
root@ubuntu:~# docker network disconnect bridge test2

root@ubuntu:~# docker network connect mynet test1
root@ubuntu:~# docker network connect mynet test2
root@ubuntu:~# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "928f7bb31aeb52d5f2fc6416e22986e50f9d83b50b159819e11a7f37b6bb5398",
        "Created": "2018-02-13T09:06:28.009216768+09:00",
        "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": {
            "39018bcf7003c48e8fe4356c98d83dfa0e4e0536879fd42fef02defc393ea2f9": {
                "Name": "test2",
                "EndpointID": "926e5878a1fe5c2c59f35fe27c336c726cec46117bfa876563d6b563fa812ba6",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "ccb0aa9e8ec770dd552aefcf811c709497cfb6b616ae4a0d8d56ac6c4f795a5d": {
                "Name": "test1",
                "EndpointID": "eca65cd1e594a360992333ad8655f2845a5c237fcc723b63d65ccd4ccec77a32",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

test1-> test2 (PING)

root@ubuntu:~# docker exec -ti test1 bash
root@ccb0aa9e8ec7:/# ping test2
PING test2 (172.18.0.3) 56(84) bytes of data.
64 bytes from test2.mynet (172.18.0.3): icmp_seq=1 ttl=64 time=0.073 ms
64 bytes from test2.mynet (172.18.0.3): icmp_seq=2 ttl=64 time=0.112 ms
^C
--- test2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1028ms
rtt min/avg/max/mdev = 0.073/0.092/0.112/0.021 ms

ping 応答があった

test1-> test2 (UDP)

root@ubuntu:~# docker exec -ti test2 bash
root@39018bcf7003:/# socat udp-listen:70000 stdout
root@ubuntu:~# docker exec -ti test1 bash
root@ccb0aa9e8ec7:/# socat udp-connect:test2:70000 stdin

入力したデータが表示された

test1-> test2 (TCP)

root@ubuntu:~# docker exec -ti test2 bash
root@39018bcf7003:/# socat tcp-listen:70000 stdout
root@ubuntu:~# docker exec -ti test1 bash
root@ccb0aa9e8ec7:/# socat tcp-connect:test2:70000 stdin

入力したデータが表示された

まとめ

  • デフォルト・ネットワークでは コンテナ名での名前解決は出来ない
  • ブリッジネットワークであればコンテナ名での名前解決は可能

  • 同一ネットワーク同士であれば EXPOSE していないポートでも コンテナ間通信は可能

  • デフォルトネットワークでも EXPOSE していないポートでも コンテナ間通信は可能(← これを知らなかった)

思い込みはダメね・・・orz

資料

Docker コンテナ・ネットワークの理解 — Docker-docs-ja 17.06.Beta ドキュメント

socatを使ってソケットテストする