独立容器的网络连接

这一系列教程讨论了独立 Docker 容器的网络连接。对于 swarm 服务的网络连接,请参阅Swarm 服务网络连接。如果您需要了解更多关于 Docker 网络的一般信息,请参阅概述

本主题包括两个不同的教程。您可以在 Linux、Windows 或 Mac 上运行它们中的任何一个,但对于最后一个,您需要在其他地方运行第二个 Docker 主机。

  • 使用默认桥接网络演示了如何使用 Docker 自动为您设置的默认 bridge 网络。这个网络不是生产系统的最佳选择。

  • 使用用户定义的桥接网络展示了如何创建和使用您自己的自定义桥接网络,以连接在同一 Docker 主机上运行的容器。这被推荐用于在生产环境中运行的独立容器。

虽然覆盖网络通常用于 swarm 服务,但您也可以为独立容器使用覆盖网络。这部分内容包含在使用覆盖网络的教程中。

使用默认桥接网络

在这个例子中,您将在同一个 Docker 主机上启动两个不同的 alpine 容器,并进行一些测试来了解它们之间如何通信。您需要安装并运行 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。后两者不是功能完备的网络,而是用于启动直接连接到 Docker 守护进程主机网络栈的容器,或启动没有网络设备的容器。本教程将把两个容器连接到 bridge 网络。

  2. 启动两个运行 ashalpine 容器,ash 是 Alpine 的默认 shell 而不是 bash-dit 标志意味着以分离模式(在后台)、交互模式(能够向其中输入)和带 TTY(以便您可以看到输入和输出)的方式启动容器。由于您是以分离模式启动它,您不会立即连接到容器。相反,容器的 ID 将被打印出来。因为您没有指定任何 --network 标志,容器将连接到默认的 bridge 网络。

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

    检查两个容器是否都已启动:

    $ 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 地址信息(alpine1 的 IP 是 172.17.0.2alpine2 的 IP 是 172.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
    

    第一个接口是环回设备。暂时忽略它。请注意,第二个接口的 IP 地址是 172.17.0.2,这与上一步中为 alpine1 显示的地址相同。

  5. alpine1 内部,通过 ping google.com 来确保您可以连接到互联网。-c 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. 现在尝试 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
    

    这会成功。接下来,尝试通过容器名称 ping alpine2 容器。这将会失败。

    # ping -c 2 alpine2
    
    ping: bad address 'alpine2'
    
  7. 使用分离序列 CTRL + p CTRL + q(按住 CTRL 并输入 p 然后输入 q)从 alpine1 分离而不停止它。如果您愿意,可以附加到 alpine2 并重复步骤 4、5 和 6,将 alpine1 替换为 alpine2

  8. 停止并移除两个容器。

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

请记住,不建议在生产环境中使用默认的 bridge 网络。要了解用户定义的桥接网络,请继续阅读下一个教程

使用用户定义的桥接网络

在这个例子中,我们再次启动两个 alpine 容器,但将它们附加到一个我们已经创建的用户定义网络 alpine-net。这些容器完全没有连接到默认的 bridge 网络。然后,我们启动第三个 alpine 容器,它连接到 bridge 网络,但没有连接到 alpine-net,以及第四个 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 地址以及没有容器连接到它的事实:

    $ 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. 创建你的四个容器。注意 --network 标志。在 docker run 命令期间,你只能连接到一个网络,所以你需要稍后使用 docker network connect 来将 alpine4 也连接到 bridge 网络。

    $ 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": {}
        }
    ]
    

    容器 alpine3alpine4 连接到了 bridge 网络。

    $ 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": {}
        }
    ]
    

    容器 alpine1alpine2alpine4 连接到了 alpine-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,因为它不在 alpine-net 网络上。

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

    不仅如此,你也不能通过 alpine3 的 IP 地址从 alpine1 连接到它。回顾一下 docker network inspectbridge 网络的输出,找到 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
    

    使用分离序列 CTRL + p CTRL + q(按住 CTRL 并输入 p 然后输入 q)从 alpine1 分离。

  7. 请记住,alpine4 同时连接到默认的 bridge 网络和 alpine-net。它应该能够访问所有其他容器。但是,您需要通过其 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. 作为最后的测试,通过 ping google.com 确保您的所有容器都可以连接到互联网。您已经附加到 alpine4,所以从那里开始尝试。接下来,从 alpine4 分离并连接到 alpine3(它只附加到 bridge 网络)再试一次。最后,连接到 alpine1(它只连接到 alpine-net 网络)再试一次。

    # 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
    

其他网络教程

© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.