獨立容器的網路連線

這一系列教程討論了獨立 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
    

其他網路教程