当前位置:首页 > Web开发 > 正文

[Kubernetes][Kubernetes容器网络2]深入解析容器跨主机网络

11-04 Web开发

深入解析容器跨主机网络

在Docker默认配置下,不同宿主机上的容器通过IP地址是无法相互通信的。

因此社区出现了很多用于解决容器跨主机通信问题的方案。

Flannel

Flannel 支持三种后端实现:

VXLAN

host-gw

UDP

先以 UDP 模式为例

Flannel UDP模式基本原理

假设有两台宿主机:

Node1: container-1,IP 地址为 100.96.1.2,对应的 docker0 网桥的地址是 100.96.1.1/24

Node2:container-2,IP 地址为 100.96.2.3,对应的 docker0 网桥的地址为 100.96.2.1/24

如果 container-1 要和 container-2 通信,那么进程发起的 IP 包的目的地址为 100.96.2.3,该 IP datagram 被 docker0 转发到宿主机,Node-1 根据宿主机的路由表来决定该 IP 包的下一跳 IP 地址。

位于Node-1上的Flannel预先在Node-1上添加一系列路由规则,如下:

# 在Node 1上 $ ip route default via 10.168.0.1 dev eth0 100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.1.0 100.96.1.0/24 dev docker0 proto kernel scope link src 100.96.1.1 10.168.0.0/24 dev eth0 proto kernel scope link src 10.168.0.2

可以看到,根据该路由表,目的 IP 为 100.96.2.3 的 IP 包会被发送给本机的 flannel0 接口。

这个flannel0接口是一个 TUN 设备(Tunnel 设备)。

在 Linux 中,TUN 设备是一个工作在网络层的虚拟网络设备,该设备的功能是在内核和用户应用进程之间传递 IP 包。这个用户应用进程就是创建了该 TUN 设备的进程,在我们这里当然就是宿主机上的 Flannel 进程。

所以,到这里,容器 container-1 发送的目的地址为 100.96.2.3 的 IP 包就被 Flannel 进程捕获,该 Flannel 进程在宿主机上的名称为 flanneld。

flanneld 需要根据该目的 IP,找到 Node-2 的 IP 地址才能继续进行转发。

这里就需要 Flannel 项目中的另一个非常重要的概念:子网。

在 Flannel 项目中,一台宿主机上的所有容器,都属于该宿主机被分配的一个子网,该子网地址就是该宿主机上 docker0 网桥的入口地址。比如在 Node-1 上,docker0 网桥所代表的子网的网络地址为 100.96.1.0/24,在 Node-2 上,docker0 网桥所代表的子网的网络地址为 100.96.2.0/24。

而这个对应关系是由Flannel项目维护的,所以站在上帝视角的 flanneld 进程通过 IP 包的目的地址就知道了目的 IP 所在的子网地址,它将所有的宿主机和子网地址的对应关系保存在 ETCD 中,就可以通过目的 IP 所在的子网地址找到该子网所在的节点。

$ etcdctl ls /coreos.com/network/subnets /coreos.com/network/subnets/100.96.1.0-24 /coreos.com/network/subnets/100.96.2.0-24 /coreos.com/network/subnets/100.96.3.0-24

上面的指令查看出容器IP和子网的对应关系,100.96.2.3 所在的子网为 100.96.2.0/24

$ etcdctl get /coreos.com/network/subnets/100.96.2.0-24 {"PublicIP":"10.168.0.3"}

根据该子网地址,同样可以在 ETCD 中找到对应节点的 IP 地址为 10.168.0.3

在获得这个 IP 地址之后,flanneld 将原来的 IP 包封装进一个目的 IP 为 10.168.0.3 的 UDP 包,然后经过宿主机发送给 Node-2。此时,这个 UDP 包的源地址一定为 Node-1 节点的 IP 地址。当然,作为应用层的 flanneld 进程需要监听一个端口以捕获该 UDP 包中的内容,flanneld 进程的监听端口为 8285

当这个 UDP 包被发送到 Node-2 之后,首先经过 Node-2 上网络栈的层层解封,flanneld 进程将会获得 container-1 发出的 IP 包,然后它将该 IP 包通过 flanel0 接口再次发送给 Node-2 上的网络协议栈,Node-2 上的路由表:

# 在Node 2上 $ ip route default via 10.168.0.1 dev eth0 100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.2.0 100.96.2.0/24 dev docker0 proto kernel scope link src 100.96.2.1 10.168.0.0/24 dev eth0 proto kernel scope link src 10.168.0.3

Node-2 的网络协议栈将该 IP 包匹配路由规则后,首先通过 docker0 向子网 100.96.2.0/24 上的所有设备发送一个 ARP 请求,获得 100.96.2.3 的对应的 MAC 地址后,将该数据包发送给二层的虚拟交换机 docker0 网桥,docker0 网络将该数据包转发给 container-2 容器,然后 container-2 容器的网络栈层层解封装,将数据最终传递给 container-2 容器中的某个进程。

基本原理图:

技术图片

UDP模式的缺陷

我们可以看到,实际上 Flannel 项目最关键的步骤都是由 flanneld 进程完成的,该进程从 Linux 内核网络协议栈的网络层直接获取 IP 包,然后利用保存在 etcd 中的映射关系,重新将该 IP 包作为数据,封装出一个新的 UDP 包,再将该 UDP 包通过宿主机网络栈发送到目的主机,再考虑数据从 container-1 通过 eth0 发送的过程,仅发送阶段,数据就需要在用户进程和内核之间拷贝三次。

技术图片

在 Linux 中,用户态操作以及数据在用户态和内核态之间的拷贝的代价是比较高的。这正是 Flannel UDP 模式性能不好的原因。

我们需要减少用户态和内核态的切换次数,并且把核心的处理逻辑都放在内核态进行。

Flannel VXLAN 模式

VXLAN:Virtual Extensible LAN(虚拟可扩展局域网),是Linux内核中支持的一种网络虚拟化技术。

温馨提示: 本文由杰米博客推荐,转载请保留链接: https://www.jmwww.net/file/web/10005.html