Docker的桥接网络是怎么工作的
我们都知道docker支持多种网络,默认网络bridge是通过一个网桥进行容器间通信的。这篇文章从零开始,用一些Linux的命令来查看主机和容器间的网络通信,也顺带介绍一些网络的基本知识。
准备工作
我们需要先安装virtualBox和vagrant。通过vagrant来驱动virtualBox搭建一个虚拟测试环境。首先在本地任意路径新建一个空文件夹比如test
,运行以下命令:
里面应该有一句# config.vm.network "private_network", ip: "192.168.33.10"
,删掉前面的#
注释,相当于给它分配一个192.168.33.10
的IP。这个vagrant镜像已经在ubuntu的基础上帮我们安装了docker,用起来很方便。然后运行以下命令启动并连接虚拟机。
主机和容器间的网络连接
进入虚拟机后,在vagrant主机上运行ifconfig
,就能看到有4个网络设备及它们的IPv4地址:
- docker0:172.17.0.1
- eth0:10.0.2.15
- eth1:192.168.33.10
- lo:127.0.0.1
其中的eth0
和eth1
是普通的以太网卡,eth1
就是我们解除注释的IP:192.168.33.10
。lo
是所谓的环回网卡,每台机器都有。它将这台机器/容器绑定到127.0.0.1
的IP上,这样子就算没有真实的网卡,也能通过这个IP访问自己,对于测试来说尤其方便。最上面的docker0
就是我们常说的网桥。怎么知道它是个网桥呢?安装bridge-utils
的包就能看到了:
网桥设备就好比交换机,可以和其他的网络设备相连接,就像在其他网络设备上拉根网线到这个交换机一样。那么docker怎么使用这个网桥呢,让我们来启动一个容器看看:
进入容器后,运行ifconfig
,就能够看到有2个网络设备及它们的IPv4地址:
- eth0:172.17.0.2
- lo:127.0.0.1
它也有自己的lo
,还有一块以太网卡eth0
,目前的IP是172.17.0.2
。使用快捷键Ctrl+P
然后再Ctrl+Q
,就能退出容器并保持它继续运行。在vagrant主机上运行route
命令,可以看到类似下面这个表格:
Destination | Gateway | Genmask | Flags | Metric | Ref | Use | Iface |
---|---|---|---|---|---|---|---|
default | 10.0.2.2 | 0.0.0.0 | UG | 0 | 0 | 0 | eth0 |
10.0.2.0 | * | 255.255.255.0 | U | 0 | 0 | 0 | eth0 |
172.17.0.0 | * | 255.255.0.0 | U | 0 | 0 | 0 | docker0 |
192.168.33.0 | * | 255.255.255.0 | U | 0 | 0 | 0 | eth1 |
这个就是vagrant主机的路由表。我们重点看一下172.17.0.0
这一行。它的Genmask为255.255.0.0
,就意味着255.255
对应着的IP172.17
是网络地址,而Genmask中0.0
对应着的IP0.0
是主机地址。整行的意思就是当目标地址是172.17.*.*
的时候,匹配这条路由规则。还有一种写法是172.17.0.0/16
。当Gateway不为*
号时,那就会路由到Gateway去,否则就路由到Iface去。刚才我们知道新容器的IP是172.17.0.2
,所以当vagrant主机上的某个数据包的地址是这个新容器的IP时,就会匹配这条路由规则,由docker0来接受这个数据包。如果数据包的地址都不匹配这些规则,就送到default
那一行的Gateway
里。
那么docker0在接收数据包之后,又会送到哪里去呢?我们在vagrant主机再次运行brctl show
,便能看到docker0这个网桥有所变化。它的interfaces
里增加了一个vethXXX
,在我的机器上叫vethd6d3942
。在vagrant主机再次运行ifconfig
,我们也能看到这一块新增的VETH虚拟网卡。实际上每启动一个容器,docker便会增加一个叫vethXXX
的设备,并把它连接到docker0上,于是docker0就可以把收到的数据包发给这个VETH设备。VETH设备总是成对出现,一端进去的请求总会从peer也就是另一端出来,这样就能将一个namespace的数据发往另一个namespace,就像虫洞一样。那么现在这一端是vethd6d3942
,它的另一端是哪儿呢?运行这个命令(记得把VETH设备名改成你自己主机上的设备名):
可以看到这个VETH设备的peer_ifindex
是某个数字,在我的机器上是5
。这个ifindex是一个网络接口的唯一识别编号。通过docker exec -it ubuntu bash
进入容器里,然后运行:
可以看到5: eth0
,原来跨越namespace跑到容器里头来啦。这就是主机上的VETH设备能跟容器内部通信的原因。每当新启动一个容器,主机就会增加一对VETH设备,把一个连接到docker0上,另一个挂载到容器内部的eth0里。
IP和mac地址映射
还有一个问题:每个网络设备都有自己的mac地址,通过ip怎么能找到它呢?在容器外运行这个命令:
我们就能看到Address
和HWaddress
,它们分别对应着IP地址和mac地址,这样就匹配起来了。到容器里ifconfig
一下,看看172.17.0.2
的mac地址,是不是和主机arp -n
运行结果中172.17.0.2
那行的mac地址一样呢?
参考资料
Linux上的基础网络设备详解,介绍了不同的网络设备工作原理。
Docker网络配置,从零开始配置docker的网络。
基础网络概念,来自鸟哥,深入浅出地介绍了网络的基础知识。
Linux常用网络命令,来自鸟哥,看完了就对茫茫的网络命令有了清晰的了解。