1.flannel的UDP模式
不同节点pod间通信(UDP模式)
发送流程
1.发送数据报文,发现是不同网段,但找不到路由,转给默认网关cni0
2.cni0查看宿主机路由表:去往目的pod所在网段转给tun设备flannel0,而后由flanneld进程截获,由它指导内核封装数据报文,由于flanneld进程在启动之初会收集各pod网段与k8s节点网卡对应关系并传给apiserver或etcd集群,同时规定flanneld进程凡是收到pod的数据报文都要转给对应的节点网卡,flanneld查询到源pod和目的pod所在的节点网卡地址后,指导内核生成路由规则,将报文转给本节点网卡
3.报文从物理网卡进入公网传给对端物理网卡
接收流程
1.物理网卡对报文外层进行解封,将原始报文暴露出来
2.物理网卡将解封后的原始报文转交给flanneld进程(凡是给pod的报文都交由flanneld进程)而后指导内核将报文转给对应的flannel0
3.flannel0查看路由表后,发现目的pod所在网段的网关为cni0
4.cni0将报文转给pod
发送过程解析
到达宿主机以后,宿主机启动路由表查询过程。
#5.3.1:在k8s-1节点上查询:
#5.3.2:flannel0为tun设备,发送给flannel0接口的RAW IP包(无MAC信息)将被flanneld进程接收到,flanneld进程接收到RAW IP包后在原有的基础上进行UDP封包.UDP封包的形式为:172.12.1.11:src port -> 172.12.1.12:8285。
此时在flannel0上抓包:
#抓包的wireshark解包格式为:
#5.3.3:flanneld在启动时会将该节点的网络信息通过api-server保存到etcd当中,故在发送报文时可以通过查询etcd得到10.244.1.7这个容器的IP属于172.12.1.12。
#5.3.4:flanneld将封装好的UDP报文从用户空间发往Linux内核协议栈,然后经ens33发出,从这里可以看出网络包在通过ens33发出前先是加上了UDP头(8个字节),再然后加上了IP头(20个字节)进行封装,这也是为什么flannel0的MTU要比ens33的MTU小28个字节的原因(防止封装后的以太网帧超过ens33的MTU而在经过ens33时被丢弃)
#此时在ens33上抓包:
#wireshark 解开包的形式如上。
此过程中flanneld的作用:
#UDP封包解包
#节点上的路由表的动态更新
UDP模式的缺点
相比于两台宿主机之间的直接通信,基于 Flannel UDP 模式的容器通信多了一个额外的步骤,即 flanneld 的处理过程。而这个过程,由于使用到了 flannel0 这个 TUN 设备,仅在发出IP包的过程中,就需要经过三次用户态与内核态之间的数据拷贝,如下所示:
第一次:用户态的容器进程发出的 IP 包经过 docker0 网桥进入内核态;
第二次:IP 包根据路由表进入 TUN(flannel0)设备,从而回到用户态的 flanneld 进程;
第三次:flanneld 进行 UDP 封包之后重新进入内核态,将 UDP 包通过宿主机的 eth0 发出去。
此外,Flannel 进行 UDP 封装和解封装的过程,也都是在用户态完成的。在 Linux 操作系统中,上述这些上下文切换和用户态操作的代价其实是比较高的,这也正是造成 Flannel UDP 模式性能不好的主要原因。