1. 程式人生 > >Docker | 網路 | (一)

Docker | 網路 | (一)

                      --昨夜西風凋碧樹,獨上高樓,望盡天涯路

Docker網路可以分為單個host上的容器網路和跨多個host的網路。

Docker安裝時預設在host上建立了三個網路:

下面分別來介紹。

  • none網路

掛在這個網路下的容器,除了lo沒有任何網絡卡。容器建立時,可以通過--network=none指定none網路。這是一個封閉的網路,一些對安全性要高並且不需要聯網的應用可以使用none網路。

  • host網路

連線host網路的容器共享Docker host的網路棧,容器的網路配置與host完全一樣。通過--network=host指定使用host網路:   

進入容器可以看到host的所有網絡卡,hostname都是Docker host的。

使用host網路的最大好處就是效能,對網路傳輸效率要求高可以選擇host網路。缺點是會犧牲靈活性:比如要考慮埠衝突問題(Docker host上已經使用的埠就不能再用了)。

Docker host的另一個用途可以讓容器直接配置host網路。

  • bridge網路

Docker安裝時會建立一個網橋docker0。這是預設使用的網橋,使用其他的網路必須用--network指定

通過brctl show檢視,沒有容器啟動時:

啟動容器,發現有個新的網路介面被掛到了docker0下,veth403af2c是該容器的虛擬網絡卡:

 進入容器,檢視網路配置:

檢視容器的網路配置發現容器有一塊網絡卡[email protected],和掛到docker0下面的網絡卡不一樣。why?

實際上這兩塊網絡卡是一對veth pair。beth pair是一種成對出現的特殊網路裝置,可以理解為由一根虛擬網線連線起來的一對網絡卡,網絡卡的一頭([email protected])在容器中,另一頭(veth403af2c)在網橋docker0,相當於也就是[email protected]掛在了docker0上。

我們還可以看到IP為172.17.0.2/16,通過docker network inspect bridge檢視bridge網路的配置資訊,發現,bridge網路配置的subnet為172.17.0.0/16,並且閘道器是172.17.0.1(這個閘道器就是docker0)。

 容器建立時,docker會自動從172.17.0.0/16中分配一個IP。

  • user-defined網路

除了自動建立的網路,我們也可以根據業務需要建立user-defined網路。

Docker提供三種user-defined網路驅動:bridge、overlay和macvlan。overlay和macvlan用於建立跨主機的網路,下一章討論。

通過bridge驅動建立類似前面預設的bridge網路:

檢視host的網路結構發現新增了一個網橋br-834b932f1cc,834b932f1cc是新建bridge網路my_net的短id。

產看my_net的配置資訊:

172.17.0.0/16是Docker自動分配的IP網段,也可以通過--subnet和--gateway引數自己指定IP網段:

 建立新的bridge網路my_net2,網段為172.22.16.0/24,閘道器為172.22.16.1。閘道器在my_net2對應的網橋br-e4fae5e8f365上:

 通過--network指定使用網路my_net2:

容器自動分配到IP 172.22.16.2,我們也可以通過--ip指定一個靜態ip:

注:只有使用--subnet建立的網路才能指定靜態IP。

my_net建立時沒有指定--subnet,如果指定,報錯如下: 

 現在我們的環境為:

發現有兩個busybox掛到my_net2上,進入到容器互相ping,發現可以互通:

得出結論:同一網路中的容器、閘道器之間可以通訊。

現在我們嘗試一下my_net2與預設bridge網路是否可以通訊,進入到busybox容器pinghttpd容器:

ping不通。如何實現不同網路之間的通訊呢?

解決方案:在不同的網路之間加上路由。如果host上對每個網路都有一條路由,同時作業系統上打開了ip forwarding,host就集成了一個路由器,掛接在不同網橋上的網路就能夠相互通訊。

首先在docker host下通過ip r檢視路由表:

172.17.0.0和172.22.16.0這兩個網路的路由都定義好了。在檢視是否啟動ip forwarding:

 已啟動,最後檢視iptables,發現iptables DROP掉了drocker0和br-e4fae5e6f365之間的雙向流量:

docker在設計上就是要隔離不同的network。現在知道原因之後,我們只需要給httpd容器新增一塊my_net2的網絡卡,就可以實現通訊:

 現在busybox可以訪問httpd了:

  • 容器間通訊

容器之間的通訊可以通過IP、Docker DNS Server或joined容器三種方式通訊。

(1)IP通訊

兩個容器必須要有屬於同一個網路的網絡卡才能通過IP互動。

具體做法就是在容器建立時通過--network指定相應的網路,或者通過docker network connect將現有的容器加入到指定網路(前面已經講過了)。

(2)Docker DNS Server

IP訪問的弊端是不夠靈活。在部署應用之前可能無法確定IP,部署之後在指定要訪問的IP會比較麻煩,對於這個問題,可以通過docker自帶的DNS服務解決。

Docker1.0版本開始,docker daemon就實現了一個內嵌的DNS Server,使容器可以直接通過容器名通訊(前提是要查到預設的容器名或者是使用--name自己命名)。

先通過docker run -it --network=my_net2 --name bb1 busybox,建立一個名為bb1的容器,然後建立bb2通過名稱互動:

docker DNS只能子啊user-defined網路中使用。也就是說,預設的bridge網路是無法使用DNS的。

建立一個bb3,然後進入bb4 ping bb3 ,發現ping不通:

 (3)joined容器

joined容器可以使兩個或者多個容器共享一個網路棧,共享網絡卡和配置資訊,joined容器之間可以通過127.0.0.1直接通訊。

建立一個httpd容器:

docker run -d -it --name=web1 httpd

建立busybox容器並通過--network=container:web1指定joined容器為web1:

 檢視busybox和web1的哇改名卡mac地址和IP地址一摸一樣,它們共享了相同的網路棧。

 busybox可以直接使用127.0.0.1訪問web1的http服務:

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

1.不同的容器中的程式希望通過loopback高校快速的通訊,比如Web Server與App Server。

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

  •  容器訪問外網

注:這裡的外網指的是容器網路以外的網路,並非特指Internet

(1)容器訪問外網

現在Docker host是可以訪問外網的:

進入busybox發現容器也可以訪問外網:

上面的busybox位於docker0這個私有的bridge網路中(172.17.0.0/16),當busybox從容器向外ping時會發生什麼呢?

首先檢視Docker Host的iptables規則:

發現,當網橋收到來自172.17.0.0/16網段的外出包,把它交給MASQUEREAD處理。而MASQUEREAD的處理方式是將包的源地址替換成host的地址傳送出去,即做了一次網路地址轉換(NAT)。

下面我們通過tcpdump檢視地址是如何轉換的。先檢視docker host的路由表:

預設路由通過ens33發出去,所以我們同時監控ens33和docker0上的icmp(ping)資料包。

當busybox ping www.baidu.com時,tcpdump輸出如下:

docker0收到busybox的ping包,源地址為容器IP172.17.0.2,之後交給MASQUEREAD處理。

ping包的源地址變成了ens33的IP 192.168.xx.xxx。

總結一下這個過程:

1.busybox傳送ping包:172.17.0.2>www.baidu.com

2.docker0收到包,發現說傳送到外網的,交給NAT處理

3.NAT將源地址換成ens33的IP:192.168.xx.xxx>www.baidu.com

4.ping包從ens33傳送出去,到達www.baidu.com

(2)外網訪問容器

docker是通過埠對映的方式另外網訪問容器的。

docker可將容器對外提供服務的埠對映到host的某個埠,外網通過該埠訪問容器。容器啟動時通過-p引數對映埠。

容器啟動後可通過docker ps或者docker port檢視host對映的埠,發現容器的80埠對映到了host的32768埠。

之後通過<host ip>:<對映埠>訪問web服務 :

curl hostip:32768

除了對映動態埠,也可以通過-p指定對映到host某個特定埠:

每一個對映的埠,host都會啟動一個docker-proxy程序來處理訪問容器的流量:

以32768埠分析:

1.docker-proxy監聽host的32768埠

2.當curl訪問hostip:32768時,docker-proxy轉發給容器172.17.0.2:80

3.httpd容器響應請求並返回結果