一、前言
我们知道docker官方并没有提供多主机的容器通信方案,单机网络的模式主要有host,container,brige,none。
none这种模式,顾名思义就是docker本身不去管理网络模式,交由其他管理和分配,比如cni。
Flannel是一个专为kubernetes定制的三层网络解决方案,主要用于解决容器的跨主机通信问题。
首先,flannel利用Kubernetes API或者etcd用于存储整个集群的网络配置,其中最主要的内容为设置集群的网络地址空间。例如,设定整个集群内所有容器的IP都取自网段“10.1.0.0/16”。接着,flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。然后,flanneld再将本主机获取的subnet以及用于主机间通信的Public IP,同样通过kubernetes API或者etcd存储起来。最后,flannel利用各种backend ,例如udp,vxlan,host-gw等等,跨主机转发容器间的网络流量,完成容器间的跨主机通信。
二、Flannel Network
实现原理
Flannel为每个主机提供独立的子网,整个集群的网络信息存储在etcd上。对于跨主机的转发,目标容器的IP地址,需要从etcd获取。
步骤:
IP数据报被封装并通过容器的eth0发送
Container1的eth0通过veth对与Docker0交互并将数据包发送到Docker0,然后Docker0转发包
Docker0确定Container3的IP地址,通过查询本地路由表到外部容器,并将数据包发送到虚拟NIC Flannel0
Flannel0收到的数据包被转发到Flanneld进程, Flanneld进程封装了数据包通过查询etcd维护的路由表并发送数据包通过主机的eth0
数据包确定网络中的目标主机主机
目的主机的Flanneld进程监听8285端口,负责解封包
解封装的数据包将转发到虚拟 NIC Flannel0
Flannel0查询路由表,解封包,并将数据包发送到Docker0
Docker0确定目标容器并发送包到目标容器
在常用的vxlan模式中,涉及到上面步骤提到的封包和拆包,这也是Flannel网络传输效率相对低的原因。
我们重点说一下host-gw模式。
hostgw是最简单的backend,它的原理非常简单,直接添加路由,将目的主机当做网关,直接路由原始封包。
例如,我们从etcd中监听到一个EventAdded事件subnet为10.1.15.0/24被分配给主机Public IP 192.168.0.100,hostgw要做的工作就是在本主机上添加一条目的地址为10.1.15.0/24,网关地址为192.168.0.100,输出设备为上文中选择的集群间交互的网卡即可。对于EventRemoved事件,只需删除对应的路由。
因为没有了封包和拆包,host-gw的性能是最好的。
不过host-gw 要求主机网络二层直接互联,所以每个节点上有n-1个路由,而n个节点一共有n(n-1)/2个路由以保证flannel的flat网络能力。
为什么host-gw 要求主机网络二层直接互联?
首先通过抓包分析,抓包结果如下图:
可以看出host-gw在传输层走的是tcp,然后在网络层的源IP和目的IP均是容器的IP,虚拟IP。
这就决定了二层互联,因为只有交换机是不关注源IP和目的IP。
假如两台主机在两个lan中,二层不通,三层通,那么就需要路由器,而路由器是无法识别容器的这些ip,当然也可以配置路由规则,但是显然没有这么做的。
Openshift默认也是使用Flannel host-gw容器网络方案,其官网也清晰的画出了host-gw的data flow diagram。
示例配置和启动参数
flannel 还是比较容易理解的,后面我看情况再来一篇深入解读的