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容器響應請求並返回結果