當Docker server啓動時,會在主機上建立一個名爲docker0的虛擬網橋,此主機上啓動的Docker容器會鏈接到這個虛擬網橋上。虛擬網橋的工做方式和物理交換機相似,這樣主機上的全部容器就經過交換機連在了一個二層網絡中。接下來就要爲容器分配IP了,Docker會從RFC1918所定義的私有IP網段中,選擇一個和宿主機不一樣的IP地址和子網分配給docker0,鏈接到docker0的容器就從這個子網中選擇一個未佔用的IP使用。如通常Docker會使用172.17.0.0/16這個網段,並將172.17.0.1/16分配給docker0網橋(在主機上使用ifconfig命令是能夠看到docker0的,能夠認爲它是網橋的管理接口,在宿主機上做爲一塊虛擬網卡使用)。單機環境下的網絡拓撲以下,主機地址爲10.10.0.186/24。nginx
Docker完成以上網絡配置的過程大體是這樣的:web
1. 在主機上建立一對虛擬網卡veth pair設備。veth設備老是成對出現的,它們組成了一個數據的通道,數據從一個設備進入,就會從另外一個設備出來。所以,veth設備經常使用來鏈接兩個網絡設備。docker
2. Docker將veth pair設備的一端放在新建立的容器中,並命名爲eth0。另外一端放在主機中,以veth65f9這樣相似的名字命名,並將這個網絡設備加入到docker0網橋中,能夠經過brctl show命令查看。安全
1
2
3
|
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02425f21c208 no
|
3. 從docker0子網中分配一個IP給容器使用,並設置docker0的IP地址爲容器的默認網關。網絡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
# 運行容器;
$ docker run --name=nginx_bridge --net=bridge -p 80:80 -d nginx
9582dbec7981085ab1f159edcc4bf35e2ee8d5a03984d214bce32a30eab4921a
# 查看容器;
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9582dbec7981 nginx
"nginx -g 'daemon ..."
3 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp nginx_bridge
# 查看容器網絡;
$ docker inspect 9582dbec7981
"Networks"
: {
"bridge"
: {
"IPAMConfig"
:
null
,
"Links"
:
null
,
"Aliases"
:
null
,
"NetworkID"
:
"9e017f5d4724039f24acc8aec634c8d2af3a9024f67585fce0a0d2b3cb470059"
,
"EndpointID"
:
"81b94c1b57de26f9c6690942cd78689041d6c27a564e079d7b1f603ecc104b3b"
,
"Gateway"
:
"172.17.0.1"
,
"IPAddress"
:
"172.17.0.2"
,
"IPPrefixLen"
: 16,
"IPv6Gateway"
:
""
,
"GlobalIPv6Address"
:
""
,
"GlobalIPv6PrefixLen"
: 0,
"MacAddress"
:
"02:42:ac:11:00:02"
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
$ docker network inspect bridge
[
{
"Name"
:
"bridge"
,
"Id"
:
"9e017f5d4724039f24acc8aec634c8d2af3a9024f67585fce0a0d2b3cb470059"
,
"Created"
:
"2017-08-09T23:20:28.061678042-04:00"
,
"Scope"
:
"local"
,
"Driver"
:
"bridge"
,
"EnableIPv6"
:
false
,
"IPAM"
: {
"Driver"
:
"default"
,
"Options"
:
null
,
"Config"
: [
{
"Subnet"
:
"172.17.0.0/16"
}
]
},
"Internal"
:
false
,
"Attachable"
:
false
,
"Ingress"
:
false
,
"Containers"
: {
"9582dbec7981085ab1f159edcc4bf35e2ee8d5a03984d214bce32a30eab4921a"
: {
"Name"
:
"nginx_bridge"
,
"EndpointID"
:
"81b94c1b57de26f9c6690942cd78689041d6c27a564e079d7b1f603ecc104b3b"
,
"MacAddress"
:
"02:42:ac:11:00:02"
,
"IPv4Address"
:
"172.17.0.2/16"
,
"IPv6Address"
:
""
}
},
"Options"
: {
"com.docker.network.bridge.default_bridge"
:
"true"
,
"com.docker.network.bridge.enable_icc"
:
"true"
,
"com.docker.network.bridge.enable_ip_masquerade"
:
"true"
,
"com.docker.network.bridge.host_binding_ipv4"
:
"0.0.0.0"
,
"com.docker.network.bridge.name"
:
"docker0"
,
"com.docker.network.driver.mtu"
:
"1500"
},
"Labels"
: {}
}
]
|
網絡拓撲介紹完後,接着介紹一下bridge模式下容器是如何通訊的。tcp
在bridge模式下,連在同一網橋上的容器能夠相互通訊(若出於安全考慮,也能夠禁止它們之間通訊,方法是在DOCKER_OPTS變量中設置–icc=false,這樣只有使用–link才能使兩個容器通訊)。spa
Docker能夠開啓容器間通訊(意味着默認配置--icc=true),也就是說,宿主機上的全部容器能夠不受任何限制地相互通訊,這可能致使拒絕服務攻擊。進一步地,Docker能夠經過--ip_forward和--iptables兩個選項控制容器間、容器和外部世界的通訊。.net
容器也能夠與外部通訊,咱們看一下主機上的Iptable規則,能夠看到這麼一條code
1
|
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
|
這條規則會將源地址爲172.17.0.0/16的包(也就是從Docker容器產生的包),而且不是從docker0網卡發出的,進行源地址轉換,轉換成主機網卡的地址。這麼說可能不太好理解,舉一個例子說明一下。假設主機有一塊網卡爲eth0,IP地址爲10.10.101.105/24,網關爲10.10.101.254。從主機上一個IP爲172.17.0.1/16的容器中ping百度(180.76.3.151)。IP包首先從容器發往本身的默認網關docker0,包到達docker0後,也就到達了主機上。而後會查詢主機的路由表,發現包應該從主機的eth0發往主機的網關10.10.105.254/24。接着包會轉發給eth0,並從eth0發出去(主機的ip_forward轉發應該已經打開)。這時候,上面的Iptable規則就會起做用,對包作SNAT轉換,將源地址換爲eth0的地址。這樣,在外界看來,這個包就是從10.10.101.105上發出來的,Docker容器對外是不可見的。server
那麼,外面的機器是如何訪問Docker容器的服務呢?咱們首先用下面命令建立一個含有web應用的容器,將容器的80端口映射到主機的80端口。
1
|
$ docker run --name=nginx_bridge --net=bridge -p 80:80 -d nginx
|
而後查看Iptable規則的變化,發現多了這樣一條規則:
1
|
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.2:80
|
此條規則就是對主機eth0收到的目的端口爲80的tcp流量進行DNAT轉換,將流量發往172.17.0.2:80,也就是咱們上面建立的Docker容器。因此,外界只需訪問10.10.101.105:80就能夠訪問到容器中的服務。
除此以外,咱們還能夠自定義Docker使用的IP地址、DNS等信息,甚至使用本身定義的網橋,可是其工做方式仍是同樣的。E