文章目录
  1. 1. 准备工作
  2. 2. 主机和容器间的网络连接
  3. 3. IP和mac地址映射
  4. 4. 参考资料

我们都知道docker支持多种网络,默认网络bridge是通过一个网桥进行容器间通信的。这篇文章从零开始,用一些Linux的命令来查看主机和容器间的网络通信,也顺带介绍一些网络的基本知识。

准备工作

我们需要先安装virtualBoxvagrant。通过vagrant来驱动virtualBox搭建一个虚拟测试环境。首先在本地任意路径新建一个空文件夹比如test,运行以下命令:

virtual box host
1
2
3
4
mkdir test
cd test
vagrant init minimum/ubuntu-trusty64-docker
vi Vagrantfile

里面应该有一句# config.vm.network "private_network", ip: "192.168.33.10",删掉前面的#注释,相当于给它分配一个192.168.33.10的IP。这个vagrant镜像已经在ubuntu的基础上帮我们安装了docker,用起来很方便。然后运行以下命令启动并连接虚拟机。

virtual box host
1
2
vagrant up
vagrant ssh

主机和容器间的网络连接

进入虚拟机后,在vagrant主机上运行ifconfig,就能看到有4个网络设备及它们的IPv4地址:

  • docker0:172.17.0.1
  • eth0:10.0.2.15
  • eth1:192.168.33.10
  • lo:127.0.0.1

其中的eth0eth1是普通的以太网卡,eth1就是我们解除注释的IP:192.168.33.10lo是所谓的环回网卡,每台机器都有。它将这台机器/容器绑定到127.0.0.1的IP上,这样子就算没有真实的网卡,也能通过这个IP访问自己,对于测试来说尤其方便。最上面的docker0就是我们常说的网桥。怎么知道它是个网桥呢?安装bridge-utils的包就能看到了:

vagrant host
1
2
sudo apt-get install bridge-utils
brctl show

网桥设备就好比交换机,可以和其他的网络设备相连接,就像在其他网络设备上拉根网线到这个交换机一样。那么docker怎么使用这个网桥呢,让我们来启动一个容器看看:

vagrant host
1
docker run -it --name=ubuntu ubuntu:14.04 bash

进入容器后,运行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设备名改成你自己主机上的设备名):

vagrant host
1
ethtool -S vethd6d3942

可以看到这个VETH设备的peer_ifindex是某个数字,在我的机器上是5。这个ifindex是一个网络接口的唯一识别编号。通过docker exec -it ubuntu bash进入容器里,然后运行:

ubuntu container
1
ip link

可以看到5: eth0,原来跨越namespace跑到容器里头来啦。这就是主机上的VETH设备能跟容器内部通信的原因。每当新启动一个容器,主机就会增加一对VETH设备,把一个连接到docker0上,另一个挂载到容器内部的eth0里。

IP和mac地址映射

还有一个问题:每个网络设备都有自己的mac地址,通过ip怎么能找到它呢?在容器外运行这个命令:

vagrant host
1
arp -n

我们就能看到AddressHWaddress,它们分别对应着IP地址和mac地址,这样就匹配起来了。到容器里ifconfig一下,看看172.17.0.2的mac地址,是不是和主机arp -n运行结果中172.17.0.2那行的mac地址一样呢?

参考资料

Linux上的基础网络设备详解,介绍了不同的网络设备工作原理。
Docker网络配置,从零开始配置docker的网络。
基础网络概念,来自鸟哥,深入浅出地介绍了网络的基础知识。
Linux常用网络命令,来自鸟哥,看完了就对茫茫的网络命令有了清晰的了解。

文章目录
  1. 1. 准备工作
  2. 2. 主机和容器间的网络连接
  3. 3. IP和mac地址映射
  4. 4. 参考资料