從Docker零基礎到懂一點實踐教程(七)
Docker容器的網路連線
Docker容器的網路基礎
docker0
通過ifconfig
命令我們可以檢視到一個名為“docker0”的虛擬網橋,Docker就是通過這個網路裝置為容器提供各種網路服務的。
schen@scvmu01:~/dockerfile/df_test7$ ifconfig docker0
docker0 Link encap:Ethernet HWaddr 02:42:50:85:11:08
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80:: 42:50ff:fe85:1108/64 Scope:Link
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:45565 errors:0 dropped:0 overruns:0 frame:0
TX packets:57804 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2637647 (2.6 MB) TX bytes:125311929 (125.3 MB)
schen@scvmu01:~/dockerfile/df_test7$
在OSI七層模型中網橋是一個單純的資料鏈路層裝置,但是Linux的虛擬網橋卻有一些不同的特點:
1. 可以設定IP地址
2. 相當於擁有一個隱藏的虛擬網絡卡
在Linux中虛擬網橋是通用網路裝置抽象的一種,我們可以為其分配IP地址,當虛擬網橋擁有IP地址後,Linux就可以通過路由表在網路層定位這個裝置,這就相當於擁有了一個隱藏的虛擬網絡卡,而這個虛擬網絡卡的名字就是虛擬網橋的名字。
在一個容器啟動時,Docker會自動建立網路連線的兩端,一端是在容器中的網路裝置“eth0”,另一端是在執行docker守護程序的主機上,以“veth”開頭的網路介面,用來實現“docker0”網橋與容器之間的網路通訊。
schen@scvmu01:~$ sudo brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02420a2cd5c8 no
schen@scvmu01:~$
schen@scvmu01:~$ docker run -it --name nw_test1 ubuntu /bin/bash
root@78890c5328d8:/# schen@scvmu01:~$
schen@scvmu01:~$
schen@scvmu01:~$ sudo brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02420a2cd5c8 no veth15614bf
schen@scvmu01:~$
我們看到Docker在網橋“docker0”上添加了一個名為“veth15614bf”的網路介面,而使用ifconfig
命令也同樣可以看到:
schen@scvmu01:~$ ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:0a:2c:d5:c8
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:aff:fe2c:d5c8/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:16 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:1072 (1.0 KB) TX bytes:648 (648.0 B)
enp0s3 Link encap:Ethernet HWaddr 08:00:27:91:ae:e1
inet addr:192.168.199.202 Bcast:192.168.199.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe91:aee1/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1027 errors:0 dropped:0 overruns:0 frame:0
TX packets:666 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:178876 (178.8 KB) TX bytes:101849 (101.8 KB)
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:284 errors:0 dropped:0 overruns:0 frame:0
TX packets:284 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1
RX bytes:23376 (23.3 KB) TX bytes:23376 (23.3 KB)
veth15614bf Link encap:Ethernet HWaddr 46:82:38:62:be:be
inet6 addr: fe80::4482:38ff:fe62:bebe/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 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:648 (648.0 B) TX bytes:648 (648.0 B)
schen@scvmu01:~$
備註:檢視網橋所需的工具brctl
可以通過sudo apt-get install bridge-utils
命令獲取。
自定義虛擬網橋
當“docker0”提供的IP地址範圍不能滿足我們的需求時,我們可以建立一個自定義的虛擬網橋並讓Docker使用它,具體步驟是:
1. 新增一個虛擬網橋 sudo brctl addbr br0
2. 配置這個虛擬網橋 sudo ifconfig br0 192.168.100.1 netmask 255.255.255.0
3. 更改Docker守護程序的啟動配置 DOCKER_OPTS="-b=br0"
schen@scvmu01:~$ sudo brctl addbr br0
schen@scvmu01:~$
schen@scvmu01:~$ sudo ifconfig br0 192.168.200.1 netmask 255.255.255.0
schen@scvmu01:~$
schen@scvmu01:~$ ifconfig br0
br0 Link encap:Ethernet HWaddr 2e:1b:de:5d:1c:32
inet addr:192.168.200.1 Bcast:192.168.200.255 Mask:255.255.255.0
inet6 addr: fe80::2c1b:deff:fe5d:1c32/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:578 (578.0 B)
schen@scvmu01:~$
schen@scvmu01:~$ sudo vi /etc/default/docker
schen@scvmu01:~$
schen@scvmu01:~$ grep "^DOCKER_OPTS" /etc/default/docker
DOCKER_OPTS="-b=br0"
schen@scvmu01:~$
schen@scvmu01:~$ sudo service docker restart
schen@scvmu01:~$
schen@scvmu01:~$ ps -ef | grep dockerd
root 3381 1 6 22:25 ? 00:00:02 dockerd -H fd:// -b=br0
schen 3506 2889 0 22:25 pts/2 00:00:00 grep --color=auto dockerd
schen@scvmu01:~$
schen@scvmu01:~$ docker run -it --name nw_test2 ubuntu /bin/bash
root@c4b71a368aed:/# schen@scvmu01:~$
schen@scvmu01:~$
schen@scvmu01:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c4b71a368aed ubuntu "/bin/bash" 19 seconds ago Up 16 seconds nw_test2
schen@scvmu01:~$
schen@scvmu01:~$ sudo brctl show
bridge name bridge id STP enabled interfaces
br0 8000.727c26f1e078 no vethdeb7922
docker0 8000.02420a2cd5c8 no
schen@scvmu01:~$
schen@scvmu01:~$ ifconfig vethdeb7922
vethdeb7922 Link encap:Ethernet HWaddr 72:7c:26:f1:e0:78
inet6 addr: fe80::707c:26ff:fef1:e078/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:24 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:3431 (3.4 KB)
schen@scvmu01:~$
schen@scvmu01:~$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nw_test2
192.168.200.2
schen@scvmu01:~$
我們看到這個容器已經連線到了我們自定義的網橋“br0”,它的IP地址屬於我們指定的網段。
Docker容器的互聯
允許所有容器間互聯
之前我們講過,同一主機上的容器是通過虛擬網橋進行連線的,因此預設狀態下,它們之間是可以互相訪問的。與此同時,Docker也提供了一個關於容器互聯的選項--icc
,在預設情況下它的值為“true”,表示允許容器之間的互相連線。
為了演示,我們使用如下Dockerfile進行構建,在這一節中的所有實驗,我們使用的都是同一個映象:
schen@scvmu01:~/dockerfile/cct_test$ cat Dockerfile
# Container connection test
FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install -y iputils-ping
RUN apt-get install -y nginx
RUN apt-get install -y curl
RUN apt-get install -y libnet-ifconfig-wrapper-perl
EXPOSE 80
CMD /bin/bash
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ docker build -t shichen/cct .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu:16.04
---> bd3d4369aebc
Step 2 : RUN apt-get update
---> Using cache
---> 1f8637646fb7
Step 3 : RUN apt-get install -y iputils-ping
---> Using cache
---> be6a0581ce76
Step 4 : RUN apt-get install -y nginx
---> Using cache
---> eabc0b3001b4
Step 5 : RUN apt-get install -y curl
---> Using cache
---> 5ce546325260
Step 6 : RUN apt-get install -y libnet-ifconfig-wrapper-perl
---> Using cache
---> b0b563031e07
Step 7 : EXPOSE 80
---> Using cache
---> 281098766be7
Step 8 : CMD /bin/bash
---> Using cache
---> 7f5295283364
Successfully built 7f5295283364
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ docker images shichen/cct
REPOSITORY TAG IMAGE ID CREATED SIZE
shichen/cct latest 7f5295283364 2 minutes ago 280.7 MB
schen@scvmu01:~/dockerfile/cct_test$
我們來啟動一個容器,並在其中執行Nginx,再啟動另一個容器,通過ping
和curl
命令進行驗證:
[email protected]:~/dockerfile/cct_test$ docker run -it -p 80 --name cct1 shichen/cct
[email protected]:/# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 02:42:ac:11:00:03
inet addr:172.17.0.3 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:508 (508.0 B) TX bytes:508 (508.0 B)
[email protected]:/# [email protected]:/# nginx
[email protected]:/# [email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$ docker run -it --name cct2 shichen/cct
[email protected]:/# ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=1.60 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=1.05 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.579 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.361 ms
^C
--- 172.17.0.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3006ms
rtt min/avg/max/mdev = 0.361/0.901/1.607/0.479 ms
[email protected]:/#
[email protected]:/# curl http://172.17.0.3:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[email protected]:/#
我們知道,每次容器啟動時Docker都會為其重新分配IP地址,因此通過IP地址連線既不方便也不保險,好在Docker為我們提供了--link
選項,用於在啟動容器時為其他容器新增別名,這樣我們就可以通過別名來訪問對應的容器了:
[email protected]:~/dockerfile/cct_test$ docker run -it --name cct3 --link=cct1:web_server shichen/cct
[email protected]4c0985296c2a:/# ping web_server
PING web_server (172.17.0.3) 56(84) bytes of data.
64 bytes from web_server (172.17.0.3): icmp_seq=1 ttl=64 time=0.877 ms
64 bytes from web_server (172.17.0.3): icmp_seq=2 ttl=64 time=1.35 ms
64 bytes from web_server (172.17.0.3): icmp_seq=3 ttl=64 time=0.726 ms
64 bytes from web_server (172.17.0.3): icmp_seq=4 ttl=64 time=1.44 ms
^C
--- web_server ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3008ms
rtt min/avg/max/mdev = 0.726/1.102/1.449/0.308 ms
[email protected]4c0985296c2a:/#
[email protected]4c0985296c2a:/# env | grep -i web_server
WEB_SERVER_PORT_80_TCP=tcp://172.17.0.3:80
WEB_SERVER_PORT=tcp://172.17.0.3:80
WEB_SERVER_PORT_80_TCP_PROTO=tcp
WEB_SERVER_PORT_80_TCP_ADDR=172.17.0.3
WEB_SERVER_NAME=/cct3/web_server
WEB_SERVER_PORT_80_TCP_PORT=80
[email protected]4c0985296c2a:/#
[email protected]4c0985296c2a:/# grep -i web_server /etc/hosts
172.17.0.3 web_server c6c4aaf0b503 cct1
[email protected]4c0985296c2a:/#
不難發現,為了實現這個機制,Docker對容器做出了適當的修改。那麼,當我們重啟Docker時,所有容器的IP地址都會改變,不過不用擔心,Docker會自動為我們做出對應的修改,以使得我們可以繼續正確地使用別名:
[email protected]:~/dockerfile/cct_test$ sudo service docker restart
[email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$ docker restart cct1 cct2 cct3
cct1
cct2
cct3
[email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$ docker attach cct3
[email protected]4c0985296c2a:/#
[email protected]4c0985296c2a:/# ping web_server
PING web_server (172.17.0.2) 56(84) bytes of data.
64 bytes from web_server (172.17.0.2): icmp_seq=1 ttl=64 time=2.59 ms
64 bytes from web_server (172.17.0.2): icmp_seq=2 ttl=64 time=0.465 ms
64 bytes from web_server (172.17.0.2): icmp_seq=3 ttl=64 time=0.331 ms
64 bytes from web_server (172.17.0.2): icmp_seq=4 ttl=64 time=0.109 ms
^C
--- web_server ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.109/0.873/2.590/0.999 ms
[email protected]4c0985296c2a:/#
[email protected]4c0985296c2a:/# env | grep -i web_server
WEB_SERVER_PORT_80_TCP=tcp://172.17.0.2:80
WEB_SERVER_PORT=tcp://172.17.0.2:80
WEB_SERVER_PORT_80_TCP_PROTO=tcp
WEB_SERVER_PORT_80_TCP_ADDR=172.17.0.2
WEB_SERVER_NAME=/cct3/web_server
WEB_SERVER_PORT_80_TCP_PORT=80
[email protected]4c0985296c2a:/#
[email protected]4c0985296c2a:/# grep -i web_server /etc/hosts
172.17.0.2 web_server dc8b267d6fa7 cct1
[email protected]4c0985296c2a:/#
拒絕所有容器間互聯
要拒絕所有容器之間的互聯,我們只需要將Docker守護程序的--icc
引數設定為“false”即可。
允許特定容器間的連線
要允許特定容器間的連線,需要滿足以下三個條件:
1. Docker守護程序--icc
選項設定為“false”
2. Docker守護程序--iptables
選項設定為“true”
3. 在容器執行時通過--link
選項指定別名
當--iptables=true
時,Docker會配置Linux的iptables,這個選項預設是開啟的。配合--icc=false
,Docker會為我們阻斷所有容器之間的互聯,而僅允許那些通過--link
選項配置的連線:
schen@scvmu01:~/dockerfile/cct_test$ grep ^DOCKER_OPTS /etc/default/docker
DOCKER_OPTS="--icc=false --iptables=true"
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ sudo service docker restart
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ ps -ef | grep dockerd
root 12002 1 2 22:41 ? 00:00:08 dockerd -H fd:// --icc=false --iptables=true
schen 12196 2546 0 22:46 pts/2 00:00:00 grep --color=auto dockerd
schen@scvmu01:~/dockerfile/cct_test$
我們重啟之前的三個容器,可以發現通過--link
配置過的“cct3”能夠正常訪問“cct1”提供的網頁服務,而“cct2”卻不能:
[email protected]:~/dockerfile/cct_test$ docker restart cct1 cct2 cct3
cct1
cct2
cct3
[email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$ docker attach cct1
[email protected]:/#
[email protected]:/# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 02:42:ac:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:24 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:1944 (1.9 KB) TX bytes:648 (648.0 B)
[email protected]:/# nginx
[email protected]:/#
[email protected]:/# [email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$ docker attach cct3
[email protected]:/#
[email protected]:/# curl web_server
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[email protected]:/#
[email protected]:/# [email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$
[email protected]:~/dockerfile/cct_test$ docker attach cct2
[email protected]:/#
[email protected]:/# curl 172.17.0.2
curl: (7) Failed to connect to 172.17.0.2 port 80: Connection timed out
[email protected]:/#
其實如果多加嘗試,我們還會發現,即便是在“cct3”中,也是無法通過ping
命令訪問“cct1”的。我們可以通過檢視Linux的iptables設定來尋找答案:
schen@scvmu01:~/dockerfile/cct_test$ docker attach cct3
root@4c0985296c2a:/#
root@4c0985296c2a:/# ping web_server
PING web_server (172.17.0.2) 56(84) bytes of data.
^C
--- web_server ping statistics ---
131 packets transmitted, 0 received, 100% packet loss, time 130433ms
root@4c0985296c2a:/#
root@4c0985296c2a:/# schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ sudo iptables -L -n
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER-ISOLATION all -- 0.0.0.0/0 0.0.0.0/0
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain DOCKER (1 references)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:80
ACCEPT tcp -- 172.17.0.4 172.17.0.2 tcp dpt:80
ACCEPT tcp -- 172.17.0.2 172.17.0.4 tcp spt:80
Chain DOCKER-ISOLATION (1 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
schen@scvmu01:~/dockerfile/cct_test$
不難發現,在“DOCKER”鏈中Linux只允許了“cct3”與“cct1”之間的80埠通訊,這就解釋了我們剛剛遇到的情況。
Docker容器與外部網路的連線
首先介紹兩個概念,一個叫做“ip-forward”,另一個叫做“iptables”,這兩個因素決定著Docker與外部網路的連線。