Docker 的网络模式可以解决一个节点上的容器之间的网络通信问题,但是对于跨主机的容器之间的通信就无能为力了,就需要借助第三方的工具来实现容器的跨主机通信;

学习 k8s 时,以使用最多的 Flannel 这个网络插件来实现容器的跨主机通信;

Flannel 目前支持三种后端的实现:UDPVXLANhost-gw 三种方式。为了加深理解容器跨主机网络通信的实现原理,下面将用图解的方式去分析这三种模式的工作流程;

Flannel 网络插件目前不支持 NetworkPolicy ,支持网络册率的插件包括:Calico、Weave 和 kube-router 等项目;
要想使用 Flannel 的同时还使用 NetworkPolicy 的话,你就需要再额外安装一个网络插件,比如 Calico 项目,来负责执行 NetworkPolicy。

UDP 方式

k8s UDP.jpg
上面就是基于 Flannel UDP 模式的跨主通信的基本流程了,从上面的整个流程来看,Flanneld 主要有两方面的功能:

  • UDP 封包解包
  • 节点上的路由表的动态更新

我们可以明显看出来数据包是通过 tun 设备从内核态复制到用户态的应用中的,然后再通过用户态复制到内核态,仅一次网络传输就进行了两次用户态和内核态的切换,显然这种效率是不会很高的,由于低效率所以这种方式基本上不使用,要提高效率最简单的方式就是把封包解包这些事情都交给内核去干好了,事实上 Linux 内核本身也提供了比较成熟的网络封包解包(隧道传输)实现方案 VXLAN,Flanneld 也实现了基于 VXLAN 的方案,该方案在我们日常使用的时候也是最普遍的。

VXLAN 方式

k8s VXLAN.jpg
VXLAN,即 Virtual Extensible LAN(虚拟可扩展局域网),是 Linux 内核本身就支持的一种网络虚似化技术。所以说,VXLAN 可以完全在内核态实现上述封装和解封装的工作,从而通过与前面相似的“隧道”机制。

VXLAN 模式下,Flanneld 进程只维护各节点对应的 VTEP 相关信息,报文的封装和解装工作,以及传送 VXLAN 包的工作都由 Linux 内核来完成,通信效率就更高了。

host-gw 方式

k8s host-gw.jpg
host-gw 即 Host Gateway,从名字中就可以想到这种方式是通过把主机当作网关来实现跨节点网络通信的。

采用 host-gw 模式后 Flanneld 的唯一作用就是负责主机上路由表的动态更新,其实就是将每个 Flannel 子网(Flannel Subnet,比如:10.244.1.0/24)的“下一跳”,设置成了该子网对应的宿主机的 IP 地址,当然,Flannel 子网和主机的信息,都是保存在 etcd 当中的。Flanneld 进程只需要 WACTH 这些数据的变化,然后实时更新路由表即可。

这样就完成了整个跨主机通信流程,这个流程可能是最简单最容器理解的模式了,而且容器通信的过程还免除了额外的封包和解包带来的性能损耗,所以理论上性能肯定要更好,但是该模式是通过节点上的路由表来实现各个节点之间的跨节点网络通信,那么就得保证两个节点是可以直接路由过去的。按照内核当中的路由规则,网关必须在跟主机当中至少一个 IP 处于同一网段,故造成的结果就是采用host-gw 这种模式的时候,集群中所有的节点必须处于同一个网络当中,这对于集群规模比较大时需要对节点进行网段划分的话会存在一定的局限性,另外一个则是随着集群当中节点规模的增大,Flanneld 需要维护主机上成千上万条路由表的动态更新也是一个不小的压力。