通过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也提供了一个关于容器互联的选项--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
命令进行验证:
schen@scvmu01:~/dockerfile/cct_test$ docker run -it -p 80 --name cct1 shichen/cct
root@c6c4aaf0b503:/# 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)
root@c6c4aaf0b503:/# root@c6c4aaf0b503:/# nginx
root@c6c4aaf0b503:/# schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ docker run -it --name cct2 shichen/cct
root@ecb093406ceb:/# 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
root@ecb093406ceb:/#
root@ecb093406ceb:/# curl http://172.17.0.3:80
<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.orga>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.coma>.p>
<p><em>Thank you for using nginx.em>p>
body>
html>
root@ecb093406ceb:/#
我们知道,每次容器启动时Docker都会为其重新分配IP地址,因此通过IP地址连接既不方便也不保险,好在Docker为我们提供了--link
选项,用于在启动容器时为其他容器添加别名,这样我们就可以通过别名来访问对应的容器了:
schen@scvmu01:~/dockerfile/cct_test$ docker run -it --name cct3 --link=cct1:web_server shichen/cct
root@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
root@4c0985296c2a:/#
root@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
root@4c0985296c2a:/#
root@4c0985296c2a:/# grep -i web_server /etc/hosts
172.17.0.3 web_server c6c4aaf0b503 cct1
root@4c0985296c2a:/#
不难发现,为了实现这个机制,Docker对容器做出了适当的修改。那么,当我们重启Docker时,所有容器的IP地址都会改变,不过不用担心,Docker会自动为我们做出对应的修改,以使得我们可以继续正确地使用别名:
schen@scvmu01:~/dockerfile/cct_test$ sudo service docker restart
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ docker restart cct1 cct2 cct3
cct1
cct2
cct3
schen@scvmu01:~/dockerfile/cct_test$
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.
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
root@4c0985296c2a:/#
root@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
root@4c0985296c2a:/#
root@4c0985296c2a:/# grep -i web_server /etc/hosts
172.17.0.2 web_server dc8b267d6fa7 cct1
root@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”却不能:
schen@scvmu01:~/dockerfile/cct_test$ docker restart cct1 cct2 cct3
cct1
cct2
cct3
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ docker attach cct1
root@dc8b267d6fa7:/#
root@dc8b267d6fa7:/# 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)
root@dc8b267d6fa7:/# nginx
root@dc8b267d6fa7:/#
root@dc8b267d6fa7:/# schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ docker attach cct3
root@4c0985296c2a:/#
root@4c0985296c2a:/# curl web_server
<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.orga>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.coma>.p>
<p><em>Thank you for using nginx.em>p>
body>
html>
root@4c0985296c2a:/#
root@4c0985296c2a:/# schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$
schen@scvmu01:~/dockerfile/cct_test$ docker attach cct2
root@ecb093406ceb:/#
root@ecb093406ceb:/# curl 172.17.0.2
curl: (7) Failed to connect to 172.17.0.2 port 80: Connection timed out
root@ecb093406ceb:/#
其实如果多加尝试,我们还会发现,即便是在“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端口通讯,这就解释了我们刚刚遇到的情况。
首先介绍两个概念,一个叫做“ip-forward”,另一个叫做“iptables”,这两个因素决定着Docker与外部网络的连接。
“ip-forward”本身表示的是Linux系统中的一个变量,它的值决定了系统是否可以转发流量。在Docker守护进程的选项中,--ip-forward
默认会被设置为“true”,这样每次Docker启动时都会对系统的“ip-forward”变量进行设置,使系统可以正常转发流量。我们可以通过sudo sysctl net.ipv4.conf.all.forwarding
命令来查看到这个变量的值。
schen@scvmu01:~$ ps -ef | grep dockerd
root 1129 1 0 19:55 ? 00:00:08 dockerd -H fd:// --icc=false --iptables=true
schen 1701 1631 0 20:41 pts/0 00:00:00 grep --color=auto dockerd
schen@scvmu01:~$
schen@scvmu01:~$ sudo sysctl net.ipv4.conf.all.forwarding
net.ipv4.conf.all.forwarding = 1
schen@scvmu01:~$
iptables是与Linux内核集成的包过滤防火墙系统,几乎所有的Linux发行版都会包含这个功能。
graph LR
接收的数据包-->PREROUTING
PREROUTING-->FORWARD
PREROUTING-->INPUT
FORWARD-->POSTROUTING
INPUT-->主机
主机-->OUTPUT
OUTPUT-->POSTROUTING
POSTROUTING-->发送的数据包
上图可以看做是iptables最简单的工作模型,同时它也代表着iptables对网络数据包处理流程的抽象。图中的英文部分为链的名称,每一个链都是数据处理中的一个环节,而在每个环节中又包含了不同的操作。
图中包含了数据包的三种流动方式:
1. 数据接收路径:当网卡上收到的数据包以本地主机为目的地时,它首先经过“PREROUTING”链处理,然后经过“INPUT”链处理,最终到达本地主机。
2. 数据发送路径:当数据包从本地主机发出时,它首先经过“OUTPUT”链处理,然后经过“POSTROUTING”链处理,最终到达网卡并发出。
3. 数据转发路径:当网卡上收到的数据包并不是以本地主机为目的地时,它首先经过“PREROUTING”链处理,然后经过“FORWARD”链处理,最后经过“POSTROUTING”链处理,最终到达网卡并发出。
iptables中各种链所支持的操作:
链 | 所支持的操作 |
---|---|
INPUT | filter, mangle |
OUTPUT | filter, nat, mangle, raw |
FORWARD | filter, mangle |
PREROUTING | nat, mangle, raw |
POSTROUTING | nat, raw, mangle |
iptables中还有三个非常重要的概念:
1. “表(table)”:它是包含有某种特定操作(操作名即为表名)的链的集合,iptables共有四个表,它们分别是filter、nat、mangle、raw。
2. “链(chain)”:它是各种“规则”的集合,iptables共有五个链,它们分别是INPUT、OUTPUT、FORWARD、PREROUTING、POSTROUTING。
3. “规则(policy)”:它是一条条的过滤语句,iptables最主要的规则有三个,它们分别是ACCEPT、REJECT、DROP。
以filter表为例,它所包含的链为INPUT、OUTPUT、FORWARD,在装有Docker的主机上,FORWARD链还包含有DOCKER和DOCKER-ISOLATION两个子链,而子链还会包含一种特殊的规则RETURN,用于回到主链上继续处理。这个表看起来会是这样:
schen@scvmu01:~$ sudo iptables -t filter -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
Chain DOCKER-ISOLATION (1 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
schen@scvmu01:~$
下面我们来启动两个容器,并在它们上面运行Nginx,可以看到iptables为它们添加了相应的规则,以允许来自任何地址的请求访问它们的80端口:
schen@scvmu01:~$ docker run -p 80 -it --name cct4 shichen/cct
root@e9eabc418845:/#
root@e9eabc418845:/# nginx
root@e9eabc418845:/#
root@e9eabc418845:/# 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)
root@e9eabc418845:/#
root@e9eabc418845:/# schen@scvmu01:~$
schen@scvmu01:~$
schen@scvmu01:~$ docker run -p 80 -it --name cct5 shichen/cct
root@8562caf46113:/#
root@8562caf46113:/# nginx
root@8562caf46113:/#
root@8562caf46113:/# 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: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)
root@8562caf46113:/#
root@8562caf46113:/# schen@scvmu01:~$
schen@scvmu01:~$
schen@scvmu01:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8562caf46113 shichen/cct "/bin/sh -c /bin/bash" 15 seconds ago Up 11 seconds 0.0.0.0:32770->80/tcp cct5
e9eabc418845 shichen/cct "/bin/sh -c /bin/bash" 39 seconds ago Up 35 seconds 0.0.0.0:32769->80/tcp cct4
schen@scvmu01:~$
schen@scvmu01:~$ 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 -- 0.0.0.0/0 172.17.0.2 tcp dpt:80
ACCEPT tcp -- 0.0.0.0/0 172.17.0.3 tcp dpt: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:~$
这时我们可以通过另一台主机“scvmu02”来访问它们所提供的web服务:
schen@scvmu01:~$ ifconfig enp0s3
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:2666 errors:0 dropped:0 overruns:0 frame:0
TX packets:1150 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:571012 (571.0 KB) TX bytes:226028 (226.0 KB)
schen@scvmu01:~$
schen@scvmu01:~$ docker port cct4
80/tcp -> 0.0.0.0:32769
schen@scvmu01:~$
schen@scvmu01:~$ docker port cct5
80/tcp -> 0.0.0.0:32770
schen@scvmu01:~$
schen@scvmu02:~$ curl http://192.168.199.202:32769
<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.orga>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.coma>.p>
<p><em>Thank you for using nginx.em>p>
body>
html>
schen@scvmu02:~$
schen@scvmu02:~$ curl http://192.168.199.202:32770
<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.orga>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.coma>.p>
<p><em>Thank you for using nginx.em>p>
body>
html>
schen@scvmu02:~$
现在我们可以通过添加一条iptables规则,来阻止从主机“scvmu02”到其中一个容器“cct5”的web服务访问:
schen@scvmu02:~$ ifconfig enp0s3
enp0s3 Link encap:Ethernet HWaddr 08:00:27:30:ed:3b
inet addr:192.168.199.201 Bcast:192.168.199.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe30:ed3b/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:726 errors:0 dropped:0 overruns:0 frame:0
TX packets:706 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:112979 (112.9 KB) TX bytes:112806 (112.8 KB)
schen@scvmu02:~$
schen@scvmu01:~$ sudo iptables -I DOCKER -s 192.168.199.201 -d 172.17.0.3 -p TCP --dport 80 -j DROP
schen@scvmu01:~$
schen@scvmu01:~$ 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
DROP tcp -- 192.168.199.201 172.17.0.3 tcp dpt:80
ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:80
ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:80
ACCEPT tcp -- 0.0.0.0/0 172.17.0.3 tcp dpt: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:~$
schen@scvmu02:~$ curl http://192.168.199.202:32769
<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.orga>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.coma>.p>
<p><em>Thank you for using nginx.em>p>
body>
html>
schen@scvmu02:~$
schen@scvmu02:~$ curl http://192.168.199.202:32770
curl: (7) Failed to connect to 192.168.199.202 port 32770: Connection timed out
schen@scvmu02:~$
我们看到,在主机“scvmu02”上依然可以访问容器“cct4”上提供的web服务,但却无法再访问“cct5”上提供的web服务了。
由此可见,Docker正是通过iptables提供的防火墙机制来实现对容器的各种访问控制,因此掌握iptables的相关知识将会对Docker网络配置的学习有很大的帮助。