網絡-Docker 提供的幾種原生網絡和自定義網絡(11)

https://www.cnblogs.com/hanxiaohui/p/8550506.html

Docker 網絡從覆蓋範圍可分爲單個 host 上的容器網絡和跨多個 host 的網絡,本章重點討論前一種

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

146.png

下面我們分別討論它們。

none 網絡

故名思議,none 網絡就是什麼都沒有的網絡。掛在這個網絡下的容器除了 lo,沒有其他任何網卡。容器創建時,可以通過 --network=none 指定使用 none 網絡。

我們不禁會問,這樣一個封閉的網絡有什麼用呢?

其實還真有應用場景。封閉意味着隔離,一些對安全性要求高並且不需要聯網的應用可以使用 none 網絡。

比如某個容器的唯一用途是生成隨機密碼,就可以放到 none 網絡中避免密碼被竊取。

當然大部分容器是需要網絡的,我們接着看 host 網絡。

host 網絡

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

在容器中可以看到 host 的所有網卡,並且連 hostname 也是 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 可以供容器使用。

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

 

容器之間的通信我們已經搞清楚了,接下來要考慮的是容器如何與外部世界通信?這將是下一節的主題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章