概念
网络栈
包括了:网卡(Network Interface)、回环设备(Loopback Device)、路由表(Routing Table)和 iptables 规则。对于一个进程来说,这些要素,其实就构成了它发起和响应网络请求的基本环境。
注意:作为一个容器,它可以声明直接使用宿主机的网络栈(–net=host),即:不开启 Network Namespace,比如:
1 | # 容器启动后,直接监听的就是宿主机的 80 端口。 |
像这样直接使用宿主机网络栈的方式,虽然可以为容器提供良好的网络性能,但也会不可避
免地引入共享网络资源的问题,比如端口冲突。
所以,在大多数情况下,我们都希望容器进程能使用自己 Network Namespace 里的网络栈,即:拥有属于自己的 IP 地址和端口。
网桥
能够起到虚拟交换机作用的网络设备,它是一个工作在数据链路层(Data Link)的设备,主要功能是根据 MAC 地址学习来将数据包转发到网桥的不同端口(Port)上。
实现原理
被隔离的容器进程,跟其他 Network Namespace 里的容器进程进行交互
为了实现上述目的,Docker 项目会默认在宿主机上创建一个名叫 docker0 的网桥,凡是连接在 docker0 网桥上的容器,就可以通过它来进行通信。
如何把这些容器“连接”到 docker0 网桥上呢?——使用一种名叫Veth Pair的虚拟设备
Veth Pair 设备的特点是:它被创建出来后,总是以两张虚拟网卡(Veth Peer)的形式成
对出现的。并且,从其中一个“网卡”发出的数据包,可以直接出现在与它对应的另一张“网卡”上,哪怕这两个“网卡”在不同的 Network Namespace 里。这就使得 Veth Pair 常常被用作连接不同 Network Namespace 的“网线”。比如:
启动了一个叫作 nginx-1 的容器:
1 | docker run –d --name nginx-1 nginx |
然后进入到这个容器中查看一下它的网络设备:
1 | # 进入 nginx-1 容器 |
启动了一个叫作 nginx-2 的容器:
1 | docker run –d --name nginx-2 nginx |
如果你在 nginx-1 容器里 ping 一下 nginx-2 容器的 IP 地址(172.17.0.3),就会发现同一宿主机上的两个容器默认就是相互连通的。
这其中的原理:当你在 nginx-1 容器里访问 nginx-2 容器的 IP 地址(比如 ping 172.17.0.3)的时候,这个目的 IP 地址会匹配到 nginx-1 容器里的第二条路由规则。可以看到,这条路由规则的网关(Gateway)是 0.0.0.0,这就意味着这是一条直连规则,即:凡是匹配到这条规则的IP包,应该经过本机的 eth0 网卡,通过二层网络直接发往目的主机。
而要通过二层网络到达 nginx-2 容器,就需要有 172.17.0.3 这个 IP 地址对应的 MAC 地址。所以 nginx-1 容器的网络协议栈,就需要通过 eth0 网卡发送一个 ARP 广播,来通过IP地址查找对应的 MAC 地址。
总结:同一个宿主机上的不同容器通过 docker0 网桥进行通信的流程:
因此,可以理解为:默认情况下,被限制在 Network Namespace 里的容器进程,实际上是通过 Veth Pair 设备 + 宿主机网桥的方式,实现了跟同其他容器的数据交换。
与之类似地,当你在一台宿主机上,访问该宿主机上的容器的 IP 地址时,这个请求的数据
包,也是先根据路由规则到达 docker0 网桥,然后被转发到对应的 Veth Pair 设备,最后出现在容器里。这个过程的示意图:
同样地,当一个容器试图连接到另外一个宿主机时,比如:ping 10.168.0.3,它发出的请
求数据包,首先经过 docker0 网桥出现在宿主机上。然后根据宿主机的路由表里的直连路
由规则(10.168.0.0/24 via eth0)),对 10.168.0.3 的访问请求就会交给宿主机的 eth0 处理。
所以,这个数据包就会经宿主机的 eth0 网卡转发到宿主机网络上,最终到达10.168.0.3 对应的宿主机上。
类比一下,如果我们通过软件的方式,创建一个整个集群“公用”的网桥,然后把集群里的所有容器都连接到这个网桥上,不就可以相互通信了吗?
构建这种容器网络的核心在于:我们需要在已有的宿主机网络上,再通过软件构建一个覆盖在已有宿主机网络之上的、可以把所有容器连通在一起的虚拟网络。所以,这种技术就被称为:Overlay Network(覆盖网络)。
而这个 Overlay Network 本身,可以由每台宿主机上的一个“特殊网桥”共同组成。
比如,当 Node 1 上的 Container 1 要访问 Node 2 上的 Container 3 的时候,Node 1 上的“特殊网桥”在收到数据包之后,能够通过某种方式,把数据包发送到正确的宿主机,比如 Node 2 上。而 Node 2 上的“特殊网桥”在收到数据包后,也能够通过某种方式,把数据包转发给正确的容器,比如 Container 3。
总结
- 容器要想跟外界进行通信,它发出的 IP 包就必须从它的
Network Namespace
里出来,来到宿主机上。而解决这个问题的方法就是:为容器创建一个一端在容器里充当默认网卡、另一端在宿主机上的Veth Pair
设备。