1. 程式人生 > >Docker 網路模式詳解及容器間網路通訊

Docker 網路模式詳解及容器間網路通訊

  當專案大規模使用 Docker 時,容器通訊的問題也就產生了。要解決容器通訊問題,必須先了解很多關於網路的知識。Docker 作為目前最火的輕量級容器技術,有很多令人稱道的功能,如 Docker 的映象管理。然而,Docker 同樣有著很多不完善的地方,網路方面就是 Docker 比較薄弱的部分。因此,我們有必要深入瞭解 Docker 的網路知識,以滿足更高的網路需求。    ## 預設網路      安裝 Docker 以後,會預設建立三種網路,可以通過 `docker network ls` 檢視。 ```shell [root@localhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 688d1970f72e bridge bridge local 885da101da7d host host local f4f1b3cf1b7f none null local ```      在學習 Docker 網路之前,我們有必要先來了解一下這幾種網路模式都是什麼意思。 | 網路模式 | 簡介 | | --------- | ------------------------------------------------------------ | | bridge | 為每一個容器分配、設定 IP 等,並將容器連線到一個 `docker0` 虛擬網橋,預設為該模式。 | | host | 容器將不會虛擬出自己的網絡卡,配置自己的 IP 等,而是使用宿主機的 IP 和埠。 | | none | 容器有獨立的 Network namespace,但並沒有對其進行任何網路設定,如分配 veth pair 和網橋連線,IP 等。 | | container | 新建立的容器不會建立自己的網絡卡和配置自己的 IP,而是和一個指定的容器共享 IP、埠範圍等。 |    ### bridge 網路模式      在該模式中,Docker 守護程序建立了一個虛擬乙太網橋 `docker0`,新建的容器會自動橋接到這個介面,附加在其上的任何網絡卡之間都能自動轉發資料包。   預設情況下,守護程序會建立一對對等虛擬裝置介面 `veth pair`,將其中一個介面設定為容器的 `eth0` 介面(容器的網絡卡),另一個介面放置在宿主機的名稱空間中,以類似 `vethxxx` 這樣的名字命名,從而將宿主機上的所有容器都連線到這個內部網路上。   比如我執行一個基於 `busybox` 映象構建的容器 `bbox01`,檢視 `ip addr`: > busybox 被稱為嵌入式 Linux 的瑞士軍刀,整合了很多小的 unix 下的通用功能到一個小的可執行檔案中。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/20207760dbf44b608539ed29b9f0365e~tplv-k3u1fbpfcp-zoom-1.image " ")   然後宿主機通過 `ip addr` 檢視資訊如下: ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/be67589037c246c48c2be2af63072976~tplv-k3u1fbpfcp-zoom-1.image " ")   通過以上的比較可以發現,證實了之前所說的:守護程序會建立一對對等虛擬裝置介面 `veth pair`,將其中一個介面設定為容器的 `eth0` 介面(容器的網絡卡),另一個介面放置在宿主機的名稱空間中,以類似 `vethxxx` 這樣的名字命名。   同時,守護程序還會從網橋 `docker0` 的私有地址空間中分配一個 IP 地址和子網給該容器,並設定 docker0 的 IP 地址為容器的預設閘道器。也可以安裝 `yum install -y bridge-utils` 以後,通過 `brctl show` 命令檢視網橋資訊。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5c7acd76f39f4901b2aa74842065109a~tplv-k3u1fbpfcp-zoom-1.image " ")   對於每個容器的 IP 地址和 Gateway 資訊,我們可以通過 `docker inspect 容器名稱|ID` 進行檢視,在 `NetworkSettings` 節點中可以看到詳細資訊。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/529902ce85a140cea8043970053c3cd3~tplv-k3u1fbpfcp-zoom-1.image " ")   我們可以通過 `docker network inspect bridge` 檢視所有 `bridge` 網路模式下的容器,在 `Containers` 節點中可以看到容器名稱。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9df1ce538e964fe6980b19785def99f6~tplv-k3u1fbpfcp-zoom-1.image " ") >   關於 `bridge` 網路模式的使用,只需要在建立容器時通過引數 `--net bridge` 或者 `--network bridge` 指定即可,當然這也是建立容器預設使用的網路模式,也就是說這個引數是可以省略的。    ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4f5206a75a884cc2968ceb1f6c14acb6~tplv-k3u1fbpfcp-zoom-1.image " ") Bridge 橋接模式的實現步驟主要如下: - Docker Daemon 利用 veth pair 技術,在宿主機上建立一對對等虛擬網路介面裝置,假設為 veth0 和 veth1。而 veth pair 技術的特性可以保證無論哪一個 veth 接收到網路報文,都會將報文傳輸給另一方。 - Docker Daemon 將 veth0 附加到 Docker Daemon 建立的 docker0 網橋上。保證宿主機的網路報文可以發往 veth0; - Docker Daemon 將 veth1 新增到 Docker Container 所屬的 namespace 下,並被改名為 eth0。如此一來,宿主機的網路報文若發往 veth0,則立即會被 Container 的 eth0 接收,實現宿主機到 Docker Container 網路的聯通性;同時,也保證 Docker Container 單獨使用 eth0,實現容器網路環境的隔離性。    ### host 網路模式    - host 網路模式需要在建立容器時通過引數 `--net host` 或者 `--network host` 指定; - 採用 host 網路模式的 Docker Container,可以直接使用宿主機的 IP 地址與外界進行通訊,若宿主機的 eth0 是一個公有 IP,那麼容器也擁有這個公有 IP。同時容器內服務的埠也可以使用宿主機的埠,無需額外進行 NAT 轉換; - host 網路模式可以讓容器共享宿主機網路棧,這樣的好處是外部主機與容器直接通訊,但是容器的網路缺少隔離性。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/32ab62e6be9d4b4dbe9280ca3b9206f9~tplv-k3u1fbpfcp-zoom-1.image " ")      比如我基於 `host` 網路模式建立了一個基於 `busybox` 映象構建的容器 `bbox02`,檢視 `ip addr`: ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ff4a5f7151a143c99878964833332e38~tplv-k3u1fbpfcp-zoom-1.image " ")   然後宿主機通過 `ip addr` 檢視資訊如下: ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b756f89bbcb045b4b4b180eddd307e8e~tplv-k3u1fbpfcp-zoom-1.image " ")   對,你沒有看錯,返回資訊一模一樣,我也可以肯定我沒有截錯圖,不信接著往下看。我們可以通過 `docker network inspect host` 檢視所有 `host` 網路模式下的容器,在 `Containers` 節點中可以看到容器名稱。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c11969f9c87a46f088280d50263cffa7~tplv-k3u1fbpfcp-zoom-1.image " ")    ### none 網路模式    - none 網路模式是指禁用網路功能,只有 lo 介面 local 的簡寫,代表 127.0.0.1,即 localhost 本地環回介面。在建立容器時通過引數 `--net none` 或者 `--network none` 指定; - none 網路模式即不為 Docker Container 建立任何的網路環境,容器內部就只能使用 loopback 網路裝置,不會再有其他的網路資源。可以說 none 模式為 Docke Container 做了極少的網路設定,但是俗話說得好“少即是多”,在沒有網路配置的情況下,作為 Docker 開發者,才能在這基礎做其他無限多可能的網路定製開發。這也恰巧體現了 Docker 設計理念的開放。      比如我基於 `none` 網路模式建立了一個基於 `busybox` 映象構建的容器 `bbox03`,檢視 `ip addr`: ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8c55e1e006db44edacbe76f4a66d7d5c~tplv-k3u1fbpfcp-zoom-1.image " ")   我們可以通過 `docker network inspect none` 檢視所有 `none` 網路模式下的容器,在 `Containers` 節點中可以看到容器名稱。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ed2060f2dbd04d07a5f4c6926d96a271~tplv-k3u1fbpfcp-zoom-1.image " ")    ### container 網路模式    - Container 網路模式是 Docker 中一種較為特別的網路的模式。在建立容器時通過引數 `--net container:已執行的容器名稱|ID` 或者 `--network container:已執行的容器名稱|ID` 指定; - 處於這個模式下的 Docker 容器會共享一個網路棧,這樣兩個容器之間可以使用 localhost 高效快速通訊。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/905bc296603243ad8ee09e13b651e5ba~tplv-k3u1fbpfcp-zoom-1.image " ")   **Container 網路模式即新建立的容器不會建立自己的網絡卡,配置自己的 IP,而是和一個指定的容器共享 IP、埠範圍等**。同樣兩個容器除了網路方面相同之外,其他的如檔案系統、程序列表等還是隔離的。      比如我基於容器 `bbox01` 建立了 `container` 網路模式的容器 `bbox04`,檢視 `ip addr`: ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d73dd522f2b2426980c16f8bf6c208f0~tplv-k3u1fbpfcp-zoom-1.image " ")   容器 `bbox01` 的 `ip addr` 資訊如下: ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4096c75836e2470f8b1554fadf32309e~tplv-k3u1fbpfcp-zoom-1.image " ")   宿主機的 `ip addr` 資訊如下: ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9c01adabe22947ac94c460f0f31ae3b7~tplv-k3u1fbpfcp-zoom-1.image " ")   通過以上測試可以發現,Docker 守護程序只建立了一對對等虛擬裝置介面用於連線 bbox01 容器和宿主機,而 bbox04 容器則直接使用了 bbox01 容器的網絡卡資訊。   這個時候如果將 bbox01 容器停止,會發現 bbox04 容器就只剩下 lo 介面了。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7f2ad30a92fc479a91163483880ccd62~tplv-k3u1fbpfcp-zoom-1.image " ")   然後 bbox01 容器重啟以後,bbox04 容器也重啟一下,就又可以獲取到網絡卡資訊了。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ff5616d7bf9841d4a739a3934267db98~tplv-k3u1fbpfcp-zoom-1.image " ")    ### ~~link~~      `docker run --link` 可以用來連結兩個容器,使得源容器(被連結的容器)和接收容器(主動去連結的容器)之間可以互相通訊,並且接收容器可以獲取源容器的一些資料,如源容器的環境變數。   這種方式**官方已不推薦使用**,並且在未來版本可能會被移除,所以這裡不作為重點講解,感興趣可自行了解。   官網警告資訊:https://docs.docker.com/network/links/ ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c5055248e51c42ec98c5b6d4753a00cb~tplv-k3u1fbpfcp-zoom-1.image " ")    ## 自定義網路      雖然 Docker 提供的預設網路使用比較簡單,但是為了保證各容器中應用的安全性,在實際開發中更推薦使用自定義的網路進行容器管理,以及啟用容器名稱到 IP 地址的自動 DNS 解析。 >   從 Docker 1.10 版本開始,docker daemon 實現了一個內嵌的 DNS server,使容器可以直接通過容器名稱通訊。方法很簡單,只要在建立容器時使用 `--name` 為容器命名即可。 > >   但是使用 Docker DNS 有個限制:**只能在 user-defined 網路中使用**。也就是說,預設的 bridge 網路是無法使用 DNS 的,所以我們就需要自定義網路。    ### 建立網路      通過 `docker network create` 命令可以建立自定義網路模式,命令提示如下: ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa11cfbc030c43e6ae59605e7e0c364c~tplv-k3u1fbpfcp-zoom-1.image " ")      進一步檢視 `docker network create` 命令使用詳情,發現可以通過 `--driver` 指定網路模式且預設是 `bridge` 網路模式,提示如下: ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1d85bdfe5b9146c2a06cf4a6cf18ad96~tplv-k3u1fbpfcp-zoom-1.image " ")      建立一個基於 `bridge` 網路模式的自定義網路模式 `custom_network`,完整命令如下: ```shell docker network create custom_network ```      通過 `docker network ls` 檢視網路模式: ```shell [root@localhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE b3634bbd8943 bridge bridge local 062082493d3a custom_network bridge local 885da101da7d host host local f4f1b3cf1b7f none null local ```      通過自定義網路模式 `custom_network` 建立容器: ```shell docker run -di --name bbox05 --net custom_network busybox ```      通過 `docker inspect 容器名稱|ID` 檢視容器的網路資訊,在 `NetworkSettings` 節點中可以看到詳細資訊。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9870393ae7264736a92760346a658685~tplv-k3u1fbpfcp-zoom-1.image " ")    ### 連線網路      通過 `docker network connect 網路名稱 容器名稱` 為容器連線新的網路模式。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ca8a513089204fb395d50d4b09a473cd~tplv-k3u1fbpfcp-zoom-1.image " ") ```shell docker network connect bridge bbox05 ```   通過 `docker inspect 容器名稱|ID` 再次檢視容器的網路資訊,多增加了預設的 `bridge`。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5edf3faf3452498a87706d6579530274~tplv-k3u1fbpfcp-zoom-1.image " ")    ### 斷開網路      通過 `docker network disconnect 網路名稱 容器名稱` 命令斷開網路。 ```shell docker network disconnect custom_network bbox05 ```   通過 `docker inspect 容器名稱|ID` 再次檢視容器的網路資訊,發現只剩下預設的 `bridge`。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2ada8ecd5219441ab1060911c46876df~tplv-k3u1fbpfcp-zoom-1.image " ")    ### 移除網路      可以通過 `docker network rm 網路名稱` 命令移除自定義網路模式,網路模式移除成功會返回網路模式名稱。 ```shell docker network rm custom_network ``` > 注意:如果通過某個自定義網路模式建立了容器,則該網路模式無法刪除。    ## 容器間網路通訊      接下來我們通過所學的知識實現容器間的網路通訊。首先明確一點,容器之間要互相通訊,必須要有屬於同一個網路的網絡卡。   我們先建立兩個基於預設的 `bridge` 網路模式的容器。 ```shell docker run -di --name default_bbox01 busybox docker run -di --name default_bbox02 busybox ```      通過 `docker network inspect bridge` 檢視兩容器的具體 IP 資訊。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7f54dc0b4a66411b950cf8e9173be5f4~tplv-k3u1fbpfcp-zoom-1.image " ")      然後測試兩容器間是否可以進行網路通訊。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/77e83da2ffbb4470b2bb4ae7ef38cf3c~tplv-k3u1fbpfcp-zoom-1.image " ")      經過測試,從結果得知兩個屬於同一個網路的容器是可以進行網路通訊的,但是 IP 地址可能是不固定的,有被更改的情況發生,那容器內所有通訊的 IP 地址也需要進行更改,能否使用容器名稱進行網路通訊?繼續測試。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ecbc2db61b8d4cdc98246c1684411ff2~tplv-k3u1fbpfcp-zoom-1.image " ")      經過測試,從結果得知使用容器進行網路通訊是不行的,那怎麼實現這個功能呢?   從 Docker 1.10 版本開始,docker daemon 實現了一個內嵌的 DNS server,使容器可以直接通過容器名稱通訊。方法很簡單,只要在建立容器時使用 `--name` 為容器命名即可。   但是使用 Docker DNS 有個限制:**只能在 user-defined 網路中使用**。也就是說,預設的 bridge 網路是無法使用 DNS 的,所以我們就需要自定義網路。   我們先基於 `bridge` 網路模式建立自定義網路 `custom_network`,然後建立兩個基於自定義網路模式的容器。 ```shell docker run -di --name custom_bbox01 --net custom_network busybox docker run -di --name custom_bbox02 --net custom_network busybox ```      通過 `docker network inspect custom_network` 檢視兩容器的具體 IP 資訊。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ff791f1e736b4cf3bef92b5a5350aa38~tplv-k3u1fbpfcp-zoom-1.image " ")      然後測試兩容器間是否可以進行網路通訊,分別使用具體 IP 和容器名稱進行網路通訊。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc09527ce8854195b37a2d44d3d8ef77~tplv-k3u1fbpfcp-zoom-1.image " ")      經過測試,從結果得知兩個屬於同一個自定義網路的容器是可以進行網路通訊的,並且可以使用容器名稱進行網路通訊。   那如果此時我希望 `bridge` 網路下的容器可以和 `custom_network` 網路下的容器進行網路又該如何操作?其實答案也非常簡單:讓 `bridge` 網路下的容器連線至新的 `custom_network` 網路即可。 ```shell docker network connect custom_network default_bbox01 ``` ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d13129eb080b40419bb32f71a0416390~tplv-k3u1fbpfcp-zoom-1.image " ") > 學完容器網路通訊,大家就可以練習使用多個容器完成常見應用叢集的部署了。後面就該學習 Docker 進階部分的內容 Docker Compose 和 Docker Swarm。 ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fd373a4509194a7fa7728e56506b522e~tplv-k3u1fbpfcp-zoom-1.image) 本文采用 `知識共享「署名-非商業性使用-禁止演繹 4.0 國際」許可協議`。 大家可以通過 `分類` 檢視更多關於 `Docker` 的文章。