1. 程式人生 > >Docker(十四)-Docker四種網路模式

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 網路

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就是新建立容器的虛擬網絡卡。

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

容器有一個網絡卡 [email protected]。大家可能會問了,為什麼不是veth28c57df 呢?

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

我們還看到 [email protected] 已經配置了 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 可以供容器使用。

除了 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.             希望監控其他容器的網路流量,比如執行在獨立容器中的網路監控程式。