作者:楊冬 歡迎轉載,也請保留這段聲明。謝謝!
出處: https://andyyoung01.github.io/ 或 http://andyyoung01.16mb.com/
本篇文章主要探索Docker的單機容器網絡,了解一下單個Docker主機上網絡的各種模式,從而為后續理解跨主機容器網絡打下基礎。
Docker默認容器網絡的建立和控制是一種結合了network namespace,iptables,Linux網橋及route table等多種技術的綜合解決方案,本篇主要針對於如何使用單主機網絡的各種模式,對於實現細節不做過多的探索(這篇文章 http://tonybai.com/2016/01/15/understanding-container-networking-on-single-host/ 對於docker單主機網絡的實現機制做了詳細的探索)。
Docker的網絡模式
Docker支持的網絡模式主要有如下幾種:

None
啟動容器時,使用了參數 --network="none"
。在此種模式下,容器和外部網絡沒有連接。在容器中只有loopback的網絡接口,但它沒有對外的任何路由。
Bridge
啟動容器時,使用了參數 --network="bridge"
或者未指定此參數。這是docker默認的網絡模式。它允許此主機上的容器彼此進行通信,也允許容器訪問主機的外部網絡。下圖顯示了Docker bridge網絡的示意圖:

在主機上,docker創建了一個通常名為docker0的網橋,這里它的ip設置為172.17.0.1.在創建每個容器時,同時創建了一對veth網絡接口。接口的一端連接到docker0的網橋上,另一端連接到容器的內部。從容器發起的到外部網絡的連接是通過IP forwarding和設置了NAT規則的iptables rules實現的(圖中的綠色箭頭)。從外部網絡到容器內部的連接使用了一條完全不同的路徑。如果容器將自身的端口映射到主機上,則docker會啟動一個docker-proxy進程來進行監聽,通過此proxy將數據轉發到容器中(圖中的紅色箭頭)。
默認情況下,同一台docker主機上的容器彼此之間可以通過他們的IP地址進行通信。如果需要通過容器的主機名進行通信,容器之間必須設置了link。
Host
啟動容器時,使用了參數 --network="host"
。在這種模式下,容器共享主機的networking namespace,所以主機上的網絡接口對容器都可用,同時在bridge模式下docker所做的各種網絡設置都被略過,這意味着容器的網絡性能和普通的主機網絡性能 一樣快 。在運行一些對網絡性能要求較高的應用時,如負載均衡器或高性能web服務器時,應使用此模式。
但此模式給予容器對本地系統服務的完全訪問權限,所以比其它模式的安全性要差。
Container
啟動容器時,使用了參數 --network="container:<name|id>"
。這種模式下容器使用了另外一個容器的networking namespace,也就是說它與另外一個容器共享網絡棧。
User-defined network
在這種模式下,用戶可以使用Docker網絡驅動或外部網絡驅動plugin來創建自定義的網絡。用戶可以將多個容器連接到同一個網絡上。一旦容器連接到用戶自定義的網絡后,容器可以使用另外一個容器的ip地址 或名稱 來彼此通信。此功能需要Docker 1.10之后的版本。在較新版本的docker daemon中內置了一個DNS服務器,對於任意在創建時指定了name或net-alias或通過link提供了別名的容器,它可以提供內置的 服務發現功能 ,從而無需再使用第三方軟件提供的DNS服務(無需再使用文章“ 使用resolvable通過DNS查找容器 ”中提供的方法)。
對於overlay網絡或使用了支持多主機連接的插件的容器,連接到同一多主機網絡但從不同主機上啟動的容器也可以彼此之間以這種方式通信。
Docker的network命令
Docker的network命令既可以用於單主機網絡的相關操作,也可以用於多主機overlay網絡的操作,本篇主要使用其與單主機網絡相關的命令。
創建網絡
當在主機上安裝docker后,docker引擎自動創建三個網絡,可用如下命令列出默認的三個網絡:
[yangdong@centos7 ~]$ docker network ls
NETWORK ID NAME DRIVER SCOPE
2a820cde1d0c bridge bridge local 54be0bc791bf host host local 8488a8a4ca59 none null local
除此之外,用戶還可以創建自己的bridge或overlay的網絡。如果運行 docker network create
命令並且指定一個網絡名稱,則此命令為用戶創建一個bridge網絡:
[yangdong@centos7 ~]$ docker network create simple-network
a88875cc258fb24bbf55db67efefd05976dc8d1a8e25a2166a1acbd1dc9e125a
[yangdong@centos7 ~]$ docker network inspect simple-network
[
{
"Name": "simple-network", "Id": "a88875cc258fb24bbf55db67efefd05976dc8d1a8e25a2166a1acbd1dc9e125a", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1/16" } ] }, "Internal": false, "Containers": {}, "Options": {}, "Labels": {} } ]
連接容器
可以將容器動態得連接到一個或多個網絡上。一旦連接后,容器可以通過其它容器的ip地址或名稱進行通信。下面看一下例子:
首先創建兩個容器:
[yangdong@centos7 ~]$ docker run -itd --name=container1 busybox bdaadbef1c5b3a53c1cf54ddda70e170e386fc578c815f07b09d21ca2fcd3b20 [yangdong@centos7 ~]$ docker run -itd --name=container2 busybox 126cf3af1ddd033a0925ca879e8d744293cb95949d560877ca630a29b4630630
然后創建一個隔離的bridge的網絡用於測試:
[yangdong@centos7 ~]$ docker network create -d bridge --subnet 172.25.0.0/16 isolated_nw 38159357c0979fdbc6ca0be29475867115e002ef27d8f79fee014b03ffd86b8d
這里通過命令行參數 --subnet
指定了容器使用的子網網段。下面將container2連接到剛才創建的網絡上:
[yangdong@centos7 ~]$ docker network connect isolated_nw container2
然后啟動第三個容器,在啟動的同時將其連接到isolated_nw網絡上,同時手動指定該容器的ip:
[yangdong@centos7 ~]$ docker run --network=isolated_nw --ip=172.25.3.3 -itd --name=container3 busybox d649491fd218c65fb0dc26aa79cb0d6f43dabac2bdc2f404c515f97042e60206
只要容器連接到一個由用戶指定子網網段(通過 --subnet
)的網絡上時,就可以為容器指定ip地址。
上述命令運行完畢后,整個主機上的網絡狀態如下圖所示:

使用docker attach命令連接到運行的container2容器內部並查看其網絡棧:
[yangdong@centos7 ~]$ docker attach container2
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:05 inet addr:172.17.0.5 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::42:acff:fe11:5/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:9 errors:0 dropped:0 overruns:0 frame:0 TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:718 (718.0 B) TX bytes:648 (648.0 B) eth1 Link encap:Ethernet HWaddr 02:42:AC:19:00:02 inet addr:172.25.0.2 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::42:acff:fe19:2/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:32 errors:0 dropped:0 overruns:0 frame:0 TX packets:8 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:3282 (3.2 KiB) TX bytes:648 (648.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
可見eth0連接到了默認的bridge網絡,eth1連接到了用戶創建的isolated_nw網絡,此網絡可以通過docker內置的DNS服務器進行其它容器的名稱解析,所以在container2中可以通過名稱ping通container3:
/ # ping -w 4 container3
PING container3 (172.25.3.3): 56 data bytes 64 bytes from 172.25.3.3: seq=0 ttl=64 time=0.146 ms 64 bytes from 172.25.3.3: seq=1 ttl=64 time=0.113 ms 64 bytes from 172.25.3.3: seq=2 ttl=64 time=0.100 ms 64 bytes from 172.25.3.3: seq=3 ttl=64 time=0.112 ms --- container3 ping statistics --- 4 packets transmitted, 4 packets received, 0% packet loss round-trip min/avg/max = 0.100/0.117/0.146 ms
然而在默認的bridge網絡中,並不是這樣。在默認的bridge網絡中,Docker並不支持自動的服務發現:
/ # ping -w 4 container1 ping: bad address 'container1'
在默認的bridge網絡中,可以使用傳統的 docker run --link
命令來啟用通過名稱的解析。當然,在沒有使用 --link
時,可以通過彼此的ip地址進行通信。
退出container2的終端,使用快捷鍵CTRL-p然后CTRL-q。
在這個示例當中,container2連接到了兩個網絡上,所以它可以與container1和container3通信。但container1和container3並不在一個網絡當中所以它們之間並不能通信。下面連接到container3的控制台然后測試一下(container1的ip為172.17.0.4):
[yangdong@centos7 ~]$ docker attach container3
/ # ping 172.17.0.4
PING 172.17.0.2 (172.17.0.2): 56 data bytes ^C --- 172.17.0.2 ping statistics --- 4 packets transmitted, 0 packets received, 100% packet loss
斷開容器連接
用戶可以通過 docker network disconnect
命令斷開容器到某個網絡的連接,當斷開此連接后,容器就不能通過此網絡與其它容器通信了:
[yangdong@centos7 ~]$ docker network disconnect isolated_nw container2
[yangdong@centos7 ~]$ docker attach container2
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:05 inet addr:172.17.0.5 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::42:acff:fe11:5/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:17 errors:0 dropped:0 overruns:0 frame:0 TX packets:16 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:1222 (1.1 KiB) TX bytes:1152 (1.1 KiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:16 errors:0 dropped:0 overruns:0 frame:0 TX packets:16 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:922 (922.0 B) TX bytes:922 (922.0 B) / # ping container3 ping: bad address 'container3'
移除一個網絡
當一個網絡中所有的容器都停止或斷開連接后,可以移除該網絡:
[yangdong@centos7 ~]$ docker network disconnect isolated_nw container3
[yangdong@centos7 ~]$ docker network rm isolated_nw
isolated_nw
[yangdong@centos7 ~]$ docker network ls
NETWORK ID NAME DRIVER SCOPE
2a820cde1d0c bridge bridge local 54be0bc791bf host host local 8488a8a4ca59 none null local a88875cc258f simple-network bridge local