系统环境描述:
系统环境:macos,vmware下搭建k8s单master/双worker伪集群环境.
软件环境描述:
单Master结点(192.168.182.100)、WORK1(192.168.182.101)、WORK2(192.168.182.102)结点环境,网络环境为flannel+ipvs, 创建了deployment服务nginx,replicas=2,即work1、work2分别有个pod运行,同进生成svc。
备注:此3台机器最开始搭建的是calio+iptables网络环境,后来使用kubeadm reset重置后,重新搭建的flannel+ipvs.
查看ivs的配置
这个nginx的svc最开始是ClusterIP模式,因出现此问题后,改成NodePort模式测试。
问题复现:
1、在work1节点上访问svc时,当ipvs将负载轮询到work2上的pod的ip进行访问时,出现3秒的访问时间;
2、在work2节点上访问svc时,当ipvs将负载轮询到work1上的pod的ip进行访问时,出现3秒的访问时间;
3、在master节点上访问svc时,每次都出现3秒的访问时间;
备1:访问svc时使用curl http://10.96.223.27:3000/hostname.html命令访问;
备2:将svc改成NodePort方案时,从笔记本电脑本机访问master、worker1、worker2时均无任何问题,能够快速访问到;
备3:另外一个问题是任何一次访问后,通过watch ipvsadm -lnc,监测ipvsadm的连接变化发现每个访问不会立即释放,即处于TIME_WAIT状态,原因是k8s设计初衷为优雅的释放链接,这个暂且不管,参考https://mp.weixin.qq.com/s/sN4ztCVJT-4HGRfkApr65w
问题截图:
问题分析:
先来了解下k8s+flannel的网络方案架构图,以便分析网络数据包的流转:
分析:结合上图,访问svc的网络传输路径
Work1节点 —> curl —> ipvs —> flannel.1 —> ens33 ——>|vm 网络|——> ens33 —>flannel.1 — >cni—>pod work2节点
S1 S2. S3. S4
s1/s2/s3/s4定义为几个阶段
知道大概的访问路径,开始抓包,对上面s1/s2/s3/s4阶段都进行抓包,抓了work2上的包 :ens33和flannel.1 如下图,发现work1发给work2时,会发3条SYN请求,即在上面流程的S3阶段处对ens33网卡抓包,也收到了3条SYN请求,但是ens33转给flannel.1网桥处理时,前2个SYN请求未被抓到,具体原因暂时不明;
问题出现在ens33未将SYN转发给flannel,具体原因不确定,这就是要去分析的:
猜测一下:
1、是否像https://www.v2ex.com/t/645262所说的,缺少什么路由?
2、另外我搭建的环境可能并不干净,因为是在搭建过calio网络方案的3台虚拟机器上reset集群后,重新搭建的基于flannel网络方案的k8s,另外虚拟机与主机网络是采用NAT方案,不像尚硅谷教育的视频教程(传送门:https://study.163.com/course/courseLearn.htm?courseId=1209568805#/learn/video?lessonId=1279884406&courseId=1209568805 )里的,基于koolshare软路由搭建的overlay网络。
3、通过抓包可以看到work1发起的前2次SYN的发起端口为46238,这2个SYN并未被work2的ens33转发给flannel,而第3次SYN的发起端口变成了39988后,SYN被成功转发给flannel, work2机器的目标端口一至都为8472。
经过一阵查询,发现以上猜测都没什么用~不过发现几个命令可以用来分析丢包问题:
ethtool -k ens33 |grep rx #查看网卡状态
netstat -s #查询网络协议统计数据,一般都用netstat -ntpl查看端口,这个统计参数很有用
cat /proc/net/snmp |grep Udp
通过2次请求,发现Udp下的InCsumErrors这个统计值每次都会加2次,Shit~这下有点蒙逼了,我用的明明是curl请求的http,如果真有问题,也是统计到tcp下,怎么会将错误统计在Udp下呢?颠覆了LZ对http协议的认知啊~
百度了一下发现HTTP3真的用UDP来实现,但是还是觉得奇怪,我用的CURL,curl就这么快使用http3了?
不是有那么句话:没有什么网络问题不是一次抓包不能解决的,如果不能,那就再抓一次!
再抓次导到wireshark中查看下
对比前3帧SYN(用tcpdump打出来的能看到SYN标志,此处虽然是UDP协议,但其实是SYN),发现了最大的不同,前2帧的Checksum值为0xffff,最后一帧值为missing,即没有值。终于离真相近了一步!另外看下了下数据帧中的http协议使用的是1.1版本,并不是3.0版本。
到目前,我们应该将原因定位到lvs了,猜测并思考下:
1、lvs将http以UDP连接转发,才出现的本应该是tcp连接,抓到的却是udp? 并且netstat -s统计的也是udp的Checksum错误
2、lvs什么配置或机制导致发送的第3帧与前2帧的checksum值不一致,是否有什么配置导致的?
3、是网络导致的使用UDP协议,与lvs无关?
经过查询分析,发现flannel的原理是将网络包封装在UDP里,原来如此!!!
接下来就是看怎么配置的flannel了,使用的是默认的flannel的配置文件
#下载flannel脚本
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
#应用flannel网络
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
经过很多查询,都查不到太多线索,最后通过“flannel udp 关闭checksum”关键词找到一篇文章记录了相关类似的问题(传送门),该文章记录的是用dig命令查询dns时出现超时,仔细了解了相关内容后,使用了该文章的命令修改flannel网卡的checksum参数,将发送端checksum校验关闭,最终发现请求正常了!!!
[root@worker2 ~]# ethtool -K flannel.1 tx-checksum-ip-generic off
归因:flannel网络设置将发送端的checksum打开了,但是flannel想利用了Checksum offloading的机制,自己不计算checksum,想留给网卡硬件来计算,这样的目的是不消耗CPU,利用网卡硬件分担CPU消耗,这原本是没问题的,但是flannel下层还有个ens33,这个ens33才是真的NIC,但是对ens33来说,从flannel发过来的是属于报文,ens33是否会对上层协议报文进行校验,然后将值填写到udp层报文内容去呢?这个我感觉不会,如果会的话,还要再了解下flannel的虚拟网络及硬件网卡之间的offloadinig机制。
总结:经过分析抓包,对k8s的网络架构有了更深入的了解。对于各层协议及各网卡处理的封包、拆包加强了一定认识。