从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,再启动另一个容器,通过pingcurl命令进行验证:

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端口通讯,这就解释了我们刚刚遇到的情况。

Docker容器与外部网络的连接

首先介绍两个概念,一个叫做“ip-forward”,另一个叫做“iptables”,这两个因素决定着Docker与外部网络的连接。

ip-forward

“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

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网络配置的学习有很大的帮助。

你可能感兴趣的:(Docker)