Docker(十四)-Docker四種網絡模式


Docker 安裝時會自動在 host 上創建三個網絡,我們可用 docker network ls 命令查看:

146.png

  • none模式,使用--net=none指定,該模式關閉了容器的網絡功能。
  • host模式,使用--net=host指定,容器將不會虛擬出自己的網卡,配置自己的IP等,而是使用宿主機的IP和端口。
  • bridge模式,使用--net=bridge指定,默認設置 ,此模式會為每一個容器分配、設置IP等,並將容器連接到一個docker0虛擬網橋,通過docker0網橋以及Iptables nat表配置與宿主機通信。
  • container模式,使用--net=container:NAME_or_ID指定,創建的容器不會創建自己的網卡,配置自己的IP,而是和一個指定的容器共享IP、端口范圍。

none 網絡

只能訪問本地網絡,沒有外網。掛在這個網絡下的容器除了 lo,沒有其他任何網卡。容器創建時,可以通過 --network=none 指定使用 none 網絡。

host 網絡

連接到 host 網絡的容器共享 Docker host 的網絡棧,容器的網絡配置與 host 完全一樣。可以通過 --network=host 指定使用 host 網絡。

直接使用 Docker host 的網絡最大的好處就是性能,如果容器對網絡傳輸效率有較高要求,則可以選擇 host 網絡。當然不便之處就是犧牲一些靈活性,比如要考慮端口沖突問題,Docker host 上已經使用的端口就不能再用了。

Docker host 的另一個用途是讓容器可以直接配置 host 網路。比如某些跨 host 的網絡解決方案,其本身也是以容器方式運行的,這些方案需要對網絡進行配置,比如管理 iptables。

bridge 網絡

Docker 安裝時會創建一個 命名為 docker0 的 linux bridge。如果不指定--network,創建的容器默認都會掛到 docker0 上

brctl show #查看bridge網絡  yum install bridge-utils

docker network inspect bridge   #查看bridge 網絡的詳細信息

149.png

當前 docker0 上沒有任何其他網絡設備,我們創建一個容器看看有什么變化。

一個新的網絡接口 veth28c57df 被掛到了 docker0 上,veth28c57df就是新創建容器的虛擬網卡。

下面看一下容器的網絡配置。

容器有一個網卡 eth0@if34。大家可能會問了,為什么不是veth28c57df 呢?

實際上 eth0@if34 和 veth28c57df 是一對 veth pair。veth pair 是一種成對出現的特殊網絡設備,可以把它們想象成由一根虛擬網線連接起來的一對網卡,網卡的一頭(eth0@if34)在容器中,另一頭(veth28c57df)掛在網橋 docker0 上,其效果就是將 eth0@if34 也掛在了 docker0 上。

我們還看到 eth0@if34 已經配置了 IP 172.17.0.2,為什么是這個網段呢?讓我們通過 docker network inspect bridge 看一下 bridge 網絡的配置信息:

原來 bridge 網絡配置的 subnet 就是 172.17.0.0/16,並且網關是 172.17.0.1。這個網關在哪兒呢?大概你已經猜出來了,就是 docker0。

當前容器網絡拓撲結構如圖所示:

容器創建時,docker 會自動從 172.17.0.0/16 中分配一個 IP,這里 16 位的掩碼保證有足夠多的 IP 可以供容器使用。

other container 網絡

$ docker run -it --name feiyu-con --net=container:feiyu busybox sh

除了 none, host, bridge 這三個自動創建的網絡,用戶也可以根據業務需要創建 user-defined 網絡。

user-defined 網絡

Docker 提供三種 user-defined 網絡驅動:bridge, overlay 和 macvlan。overlay 和 macvlan 用於創建跨主機的網絡。

    我們可通過 bridge 驅動創建類似前面默認的 bridge 網絡,例如:

155.png

    查看一下當前 host 的網絡結構變化:

    新增了一個網橋 br-eaed97dc9a77,這里 eaed97dc9a77 正好新建 bridge 網絡 my_net 的短 id。執行 docker network inspect 查看一下 my_net 的配置信息:

    這里 172.18.0.0/16 是 Docker 自動分配的 IP 網段。

    我們可以自己指定 IP 網段嗎?
答案是:可以。

    只需在創建網段時指定 --subnet 和 --gateway 參數:

    這里我們創建了新的 bridge 網絡 my_net2,網段為 172.22.16.0/24,網關為 172.22.16.1。與前面一樣,網關在 my_net2 對應的網橋 br-5d863e9f78b6 上:

    容器要使用新的網絡,需要在啟動時通過 --network 指定:

    容器分配到的 IP 為 172.22.16.2。

    到目前為止,容器的 IP 都是 docker 自動從 subnet 中分配,我們能否指定一個靜態 IP 呢?

    答案是:可以,通過--ip指定。

    注:只有使用 --subnet 創建的網絡才能指定靜態 IP

my_net 創建時沒有指定 --subnet,如果指定靜態 IP 報錯如下:

    好了,我們來看看當前 docker host 的網絡拓撲結構。


通過前面小節的實踐,當前 docker host 的網絡拓撲結構如下圖所示,今天我們將討論這幾個容器之間的連通性。

163.png

兩個 busybox 容器都掛在 my_net2 上,應該能夠互通,我們驗證一下:

可見同一網絡中的容器、網關之間都是可以通信的。

my_net2 與默認 bridge 網絡能通信嗎?

從拓撲圖可知,兩個網絡屬於不同的網橋,應該不能通信,我們通過實驗驗證一下,讓 busybox 容器 ping httpd 容器:

確實 ping 不通,符合預期。

“等等!不同的網絡如果加上路由應該就可以通信了吧?”我已經聽到有讀者在建議了。

這是一個非常非常好的想法。

確實,如果 host 上對每個網絡的都有一條路由,同時操作系統上打開了 ip forwarding,host 就成了一個路由器,掛接在不同網橋上的網絡就能夠相互通信。下面我們來看看 docker host 滿不滿足這些條件呢?

ip r 查看 host 上的路由表:

# ip r

......

172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1

172.22.16.0/24 dev br-5d863e9f78b6  proto kernel  scope link  src 172.22.16.1

......

172.17.0.0/16 和 172.22.16.0/24 兩個網絡的路由都定義好了。再看看 ip forwarding:

# sysctl net.ipv4.ip_forward

net.ipv4.ip_forward = 1

ip forwarding 也已經啟用了。

    條件都滿足,為什么不能通行呢?

    我們還得看看 iptables:

# iptables-save

......

-A DOCKER-ISOLATION -i br-5d863e9f78b6 -o docker0 -j DROP

-A DOCKER-ISOLATION -i docker0 -o br-5d863e9f78b6 -j DROP

......

原因就在這里了:iptables DROP 掉了網橋 docker0 與 br-5d863e9f78b6 之間雙向的流量

    從規則的命名 DOCKER-ISOLATION 可知 docker 在設計上就是要隔離不同的 netwrok。

    那么接下來的問題是:怎樣才能讓 busybox 與 httpd 通信呢?

    答案是:為 httpd 容器添加一塊 net_my2 的網卡。這個可以通過docker network connect 命令實現。

    我們在 httpd 容器中查看一下網絡配置:

    容器中增加了一個網卡 eth1,分配了 my_net2 的 IP 172.22.16.3。現在 busybox 應該能夠訪問 httpd 了,驗證一下:

busybox 能夠 ping 到 httpd,並且可以訪問 httpd 的 web 服務。當前網絡結構如圖所示:

容器之間可通過 IP,Docker DNS Server 或 joined 容器三種方式通信

 

IP 通信

 從上一節的例子可以得出這樣一個結論:兩個容器要能通信,必須要有屬於同一個網絡的網卡。

 滿足這個條件后,容器就可以通過 IP 交互了。具體做法是在容器創建時通過 --network 指定相應的網絡,或者通過 docker network connect 將現有容器加入到指定網絡。可參考上一節 httpd 和 busybox 的例子,這里不再贅述。

 

Docker DNS Server

通過 IP 訪問容器雖然滿足了通信的需求,但還是不夠靈活。因為我們在部署應用之前可能無法確定 IP,部署之后再指定要訪問的 IP 會比較麻煩。對於這個問題,可以通過 docker 自帶的 DNS 服務解決。

 從 Docker 1.10 版本開始,docker daemon 實現了一個內嵌的 DNS server,使容器可以直接通過“容器名”通信。方法很簡單,只要在啟動時用 --name 為容器命名就可以了。

 下面啟動兩個容器 bbox1 和 bbox2:

 docker run -it --network=my_net2 --name=bbox1 busybox

 docker run -it --network=my_net2 --name=bbox2 busybox

 然后,bbox2 就可以直接 ping 到 bbox1 了:

 

     使用 docker DNS 有個限制:只能在 user-defined 網絡中使用。也就是說,默認的 bridge 網絡是無法使用 DNS 的。下面驗證一下:

     創建 bbox3 和 bbox4,均連接到 bridge 網絡。

 docker run -it --name=bbox3 busybox

 docker run -it --name=bbox4 busybox

     bbox4 無法 ping 到 bbox3。

 171.png

 

joined 容器

 joined 容器是另一種實現容器間通信的方式。

 joined 容器非常特別,它可以使兩個或多個容器共享一個網絡棧,共享網卡和配置信息,joined 容器之間可以通過 127.0.0.1 直接通信。請看下面的例子:

     先創建一個 httpd 容器,名字為 web1。

docker run -d -it --name=web1 httpd 然后創建 busybox 容器並通過 --network=container:web1 指定 jointed 容器為 web1:

 

     請注意 busybox 容器中的網絡配置信息,下面我們查看一下 web1 的網絡:

 

     看!busybox 和 web1 的網卡 mac 地址與 IP 完全一樣,它們共享了相同的網絡棧。busybox 可以直接用 127.0.0.1 訪問 web1 的 http 服務。

 

 

joined 容器非常適合以下場景:

  1.             不同容器中的程序希望通過 loopback 高效快速地通信,比如 web server 與 app server。        

  2.             希望監控其他容器的網絡流量,比如運行在獨立容器中的網絡監控程序。        


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM