使用覆蓋網路進行網路連線
這一系列教程涉及 swarm 服務的網路。對於獨立容器的網路,請參閱 獨立容器網路。如果您需要了解更多關於 Docker 網路的一般資訊,請參閱概述。
本頁包含以下教程。您可以在 Linux、Windows 或 Mac 上執行它們中的每一個,但對於最後一個,您需要另一個在別處執行的 Docker 主機。
使用預設覆蓋網路 演示瞭如何使用 Docker 在您初始化或加入 swarm 時自動為您設定的預設覆蓋網路。該網路不是生產系統的最佳選擇。
使用使用者定義的覆蓋網路 展示瞭如何建立和使用您自己的自定義覆蓋網路來連線服務。建議在生產環境中執行的服務使用此方法。
為獨立容器使用覆蓋網路 展示瞭如何使用覆蓋網路在不同 Docker 守護程序上的獨立容器之間進行通訊。
先決條件
這些要求您至少擁有一個單節點 swarm,這意味著您已經啟動了 Docker 並在主機上運行了 `docker swarm init`。您也可以在多節點 swarm 上執行這些示例。
使用預設覆蓋網路
在此示例中,您將啟動一個 `alpine` 服務,並從單個服務容器的角度檢查網路的特性。
本教程不涉及覆蓋網路如何實現的作業系統特定細節,而是側重於從服務的角度看覆蓋網路如何運作。
先決條件
本教程需要三臺可以相互通訊的物理或虛擬 Docker 主機。本教程假設這三臺主機在同一網路上執行,且沒有防火牆干預。
這些主機將被稱為 `manager`、`worker-1` 和 `worker-2`。`manager` 主機將同時作為管理器和工作節點,這意味著它既可以執行服務任務,也可以管理 swarm。`worker-1` 和 `worker-2` 將僅作為工作節點。
如果您手頭沒有三臺主機,一個簡單的解決方案是在雲提供商(如 Amazon EC2)上設定三臺 Ubuntu 主機,所有主機都在同一個網路上,並且允許該網路上所有主機之間的所有通訊(使用 EC2 安全組等機制),然後按照在 Ubuntu 上安裝 Docker Engine - Community 的說明進行操作。
演練
建立 swarm
在此過程結束時,所有三個 Docker 主機都將加入 swarm,並使用名為 `ingress` 的覆蓋網路連線在一起。
在 `manager` 上初始化 swarm。如果主機只有一個網路介面,`--advertise-addr` 標誌是可選的。
$ docker swarm init --advertise-addr=<IP-ADDRESS-OF-MANAGER>
記下打印出的文字,因為它包含了您將用於將 `worker-1` 和 `worker-2` 加入 swarm 的令牌。最好將令牌儲存在密碼管理器中。
在 `worker-1` 上,加入 swarm。如果主機只有一個網路介面,`--advertise-addr` 標誌是可選的。
$ docker swarm join --token <TOKEN> \ --advertise-addr <IP-ADDRESS-OF-WORKER-1> \ <IP-ADDRESS-OF-MANAGER>:2377
在 `worker-2` 上,加入 swarm。如果主機只有一個網路介面,`--advertise-addr` 標誌是可選的。
$ docker swarm join --token <TOKEN> \ --advertise-addr <IP-ADDRESS-OF-WORKER-2> \ <IP-ADDRESS-OF-MANAGER>:2377
在 `manager` 上,列出所有節點。此命令只能從管理器執行。
$ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS d68ace5iraw6whp7llvgjpu48 * ip-172-31-34-146 Ready Active Leader nvp5rwavvb8lhdggo8fcf7plg ip-172-31-35-151 Ready Active ouvx2l7qfcxisoyms8mtkgahw ip-172-31-36-89 Ready Active
您也可以使用 `--filter` 標誌按角色進行篩選
$ docker node ls --filter role=manager ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS d68ace5iraw6whp7llvgjpu48 * ip-172-31-34-146 Ready Active Leader $ docker node ls --filter role=worker ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS nvp5rwavvb8lhdggo8fcf7plg ip-172-31-35-151 Ready Active ouvx2l7qfcxisoyms8mtkgahw ip-172-31-36-89 Ready Active
在 `manager`、`worker-1` 和 `worker-2` 上列出 Docker 網路,並注意它們現在都有一個名為 `ingress` 的覆蓋網路和一個名為 `docker_gwbridge` 的橋接網路。這裡只顯示了 `manager` 的列表
$ docker network ls NETWORK ID NAME DRIVER SCOPE 495c570066be bridge bridge local 961c6cae9945 docker_gwbridge bridge local ff35ceda3643 host host local trtnl4tqnc3n ingress overlay swarm c8357deec9cb none null local
`docker_gwbridge` 將 `ingress` 網路連線到 Docker 主機的網路介面,以便流量可以進出 swarm 管理器和工作節點。如果您建立 swarm 服務並且不指定網路,它們將連線到 `ingress` 網路。建議您為每個應用程式或將協同工作的應用程式組使用單獨的覆蓋網路。在下一個過程中,您將建立兩個覆蓋網路並將一個服務連線到每個網路。
建立服務
在 `manager` 上,建立一個名為 `nginx-net` 的新覆蓋網路
$ docker network create -d overlay nginx-net
您不需要在其他節點上建立覆蓋網路,因為當這些節點中的一個開始執行需要它的服務任務時,它將自動建立。
在 `manager` 上,建立一個連線到 `nginx-net` 的 5 副本 Nginx 服務。該服務將向外部世界釋出埠 80。所有服務任務容器都可以在不開啟任何埠的情況下相互通訊。
注意服務只能在管理器上建立。
$ docker service create \ --name my-nginx \ --publish target=80,published=80 \ --replicas=5 \ --network nginx-net \ nginx
當您沒有為 `--publish` 標誌指定 `mode` 時,使用的預設釋出模式是 `ingress`,這意味著如果您在 `manager`、`worker-1` 或 `worker-2` 上瀏覽埠 80,您將被連線到 5 個服務任務之一的埠 80,即使您瀏覽的節點上當前沒有任務在執行。如果您想使用 `host` 模式釋出埠,可以向 `--publish` 輸出新增 `mode=host`。但是,在這種情況下,您還應該使用 `--mode global` 而不是 `--replicas=5`,因為在給定節點上只有一個服務任務可以繫結一個給定埠。
執行 `docker service ls` 監控服務啟動進度,這可能需要幾秒鐘。
在 `manager`、`worker-1` 和 `worker-2` 上檢查 `nginx-net` 網路。請記住,您不需要在 `worker-1` 和 `worker-2` 上手動建立它,因為 Docker 已經為您建立了。輸出會很長,但請注意 `Containers` 和 `Peers` 部分。`Containers` 列出了從該主機連線到覆蓋網路的所有服務任務(或獨立容器)。
從 `manager` 使用 `docker service inspect my-nginx` 檢查服務,並注意有關該服務使用的埠和端點的資訊。
建立一個新網路 `nginx-net-2`,然後更新服務以使用此網路而不是 `nginx-net`
$ docker network create -d overlay nginx-net-2
$ docker service update \ --network-add nginx-net-2 \ --network-rm nginx-net \ my-nginx
執行 `docker service ls` 以驗證服務已更新且所有任務已重新部署。執行 `docker network inspect nginx-net` 以驗證沒有容器連線到它。對 `nginx-net-2` 執行相同的命令,並注意所有服務任務容器都連線到它。
注意儘管覆蓋網路會根據需要在 swarm 工作節點上自動建立,但它們不會自動刪除。
清理服務和網路。從 `manager` 執行以下命令。管理器將指示工作節點自動刪除網路。
$ docker service rm my-nginx $ docker network rm nginx-net nginx-net-2
使用使用者定義的覆蓋網路
先決條件
本教程假設 swarm 已經設定好,並且您在一個管理器上。
演練
建立使用者定義的覆蓋網路。
$ docker network create -d overlay my-overlay
使用覆蓋網路啟動服務,並將埠 80 釋出到 Docker 主機的埠 8080。
$ docker service create \ --name my-nginx \ --network my-overlay \ --replicas 1 \ --publish published=8080,target=80 \ nginx:latest
執行 `docker network inspect my-overlay` 並透過檢視 `Containers` 部分驗證 `my-nginx` 服務任務已連線到該網路。
移除服務和網路。
$ docker service rm my-nginx $ docker network rm my-overlay
為獨立容器使用覆蓋網路
此示例演示了 DNS 容器發現——具體來說,是如何使用覆蓋網路在不同 Docker 守護程序上的獨立容器之間進行通訊。步驟如下
- 在 `host1` 上,將節點初始化為 swarm(管理器)。
- 在 `host2` 上,將節點加入 swarm(工作節點)。
- 在 `host1` 上,建立一個可附加的覆蓋網路 (`test-net`)。
- 在 `host1` 上,在 `test-net` 上執行一個互動式的 alpine 容器 (`alpine1`)。
- 在 `host2` 上,在 `test-net` 上執行一個互動式且分離的 alpine 容器 (`alpine2`)。
- 在 `host1` 上,在 `alpine1` 的會話中,ping `alpine2`。
先決條件
對於此測試,您需要兩個可以相互通訊的不同 Docker 主機。每個主機必須在兩個 Docker 主機之間開啟以下埠:
- TCP 埠 2377
- TCP 和 UDP 埠 7946
- UDP 埠 4789
一個簡單的設定方法是擁有兩臺虛擬機器(本地或在 AWS 等雲提供商上),每臺都安裝並執行 Docker。如果您使用 AWS 或類似的雲計算平臺,最簡單的配置是使用一個安全組,在兩臺主機之間開啟所有傳入埠,並從您客戶端的 IP 地址開啟 SSH 埠。
此示例將我們 swarm 中的兩個節點稱為 `host1` 和 `host2`。此示例還使用 Linux 主機,但相同的命令在 Windows 上也適用。
演練
設定 swarm。
a. 在 `host1` 上,初始化一個 swarm(如果提示,使用 `--advertise-addr` 指定與 swarm 中其他主機通訊的介面的 IP 地址,例如 AWS 上的私有 IP 地址)
$ docker swarm init Swarm initialized: current node (vz1mm9am11qcmo979tlrlox42) is now a manager. To add a worker to this swarm, run the following command: docker swarm join --token SWMTKN-1-5g90q48weqrtqryq4kj6ow0e8xm9wmv9o6vgqc5j320ymybd5c-8ex8j0bc40s6hgvy5ui5gl4gy 172.31.47.252:2377 To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
b. 在 `host2` 上,按照上述說明加入 swarm
$ docker swarm join --token <your_token> <your_ip_address>:2377 This node joined a swarm as a worker.
如果節點加入 swarm 失敗,`docker swarm join` 命令會超時。要解決此問題,請在 `host2` 上執行 `docker swarm leave --force`,驗證您的網路和防火牆設定,然後重試。
在 `host1` 上,建立一個名為 `test-net` 的可附加覆蓋網路
$ docker network create --driver=overlay --attachable test-net uqsof8phj3ak0rq9k86zta6ht
注意返回的 **NETWORK ID** —— 當你從 `host2` 連線到它時,你會再次看到它。
在 `host1` 上,啟動一個連線到 `test-net` 的互動式 (`-it`) 容器 (`alpine1`)
$ docker run -it --name alpine1 --network test-net alpine / #
在 `host2` 上,列出可用網路——注意 `test-net` 尚不存在
$ docker network ls NETWORK ID NAME DRIVER SCOPE ec299350b504 bridge bridge local 66e77d0d0e9a docker_gwbridge bridge local 9f6ae26ccb82 host host local omvdxqrda80z ingress overlay swarm b65c952a4b2b none null local
在 `host2` 上,啟動一個連線到 `test-net` 的分離式 (`-d`) 且互動式 (`-it`) 容器 (`alpine2`)
$ docker run -dit --name alpine2 --network test-net alpine fb635f5ece59563e7b8b99556f816d24e6949a5f6a5b1fbd92ca244db17a4342
注意自動 DNS 容器發現僅適用於唯一的容器名稱。
在 `host2` 上,驗證 `test-net` 已被建立(並且與 `host1` 上的 `test-net` 具有相同的 NETWORK ID)
$ docker network ls NETWORK ID NAME DRIVER SCOPE ... uqsof8phj3ak test-net overlay swarm
在 `host1` 上,在 `alpine1` 的互動式終端內 ping `alpine2`
/ # ping -c 2 alpine2 PING alpine2 (10.0.0.5): 56 data bytes 64 bytes from 10.0.0.5: seq=0 ttl=64 time=0.600 ms 64 bytes from 10.0.0.5: seq=1 ttl=64 time=0.555 ms --- alpine2 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.555/0.577/0.600 ms
這兩個容器透過連線兩個主機的覆蓋網路進行通訊。如果你在 `host2` 上執行另一個*未分離*的 alpine 容器,你可以從 `host2` ping `alpine1`(這裡我們添加了 remove 選項 以便自動清理容器)
$ docker run -it --rm --name alpine3 --network test-net alpine / # ping -c 2 alpine1 / # exit
在 `host1` 上,關閉 `alpine1` 會話(這也會停止容器)
/ # exit
清理您的容器和網路
您必須在每個主機上獨立停止和刪除容器,因為 Docker 守護程序是獨立執行的,並且這些是獨立容器。您只需要在 `host1` 上刪除網路,因為當您在 `host2` 上停止 `alpine2` 時,`test-net` 會消失。
a. 在 `host2` 上,停止 `alpine2`,檢查 `test-net` 是否已移除,然後移除 `alpine2`
$ docker container stop alpine2 $ docker network ls $ docker container rm alpine2
a. 在 `host1` 上,移除 `alpine1` 和 `test-net`
$ docker container rm alpine1 $ docker network rm test-net