[ root@bowser1704 ~] # route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.19.159.253 0.0.0.0 UG 0 0 0 eth0
10.42.0.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0
10.42.1.0 10.42.1.0 255.255.255.0 UG 0 0 0 flannel.1
10.42.2.0 10.42.2.0 255.255.255.0 UG 0 0 0 flannel.1
根据第二步中的对照组
1 2 对照可以发现,1 和 2 结果不一样。在这里不一样的原因是 1 中直接走 cni 了,但是 2 中会走 flannel。所以走 flannel 可能有影响,并且根据别人使用 host-gw 会有帮助,也许 vxlan 有问题。
cni0 是容器网桥,flannel.1 是 cni 插件 flannel 网桥,走 flannel 意味着会有 vxlan。
5. 追踪数据包 tcpdump 抓包
# 监听 pod ip / endpoing
tcpdump -i any -vv host 10.42.1.34
# 另一个 shell curl svc
curl 10.43.105.114:8080/sd/health
# P.S. 此时不能有其他人访问这个 pod。
发现有输出,说明监听到了,也就是 svc 转发到了对应的 pod。并且等了一分钟左右他居然是能连通的 。
初次之外,建立 TCP 连接时,一直重发 SYN,第七次才真正传送到了。
明确两点:
tcp 数据报是转发到了 pod IP 上的。
63s 的延迟之后又可以了。
在 tcp 三次握手时:
客户端首先要发送一个 SYN 给服务端
服务端再发送一个 SYN-ACK 回复客户端
客户端最后发送一个 ACK 给服务端,握手就成功了。
当连接未建立成功,重传需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s。并且第六次重传输会取消 checksum,也就是 ‘no cksum’ 。
根据 TCP 的原理以及前几步骤的确认,可以确认问题出现在 IP packet 进入 flannel 之后,并且做了 SNAT 以及 利用 VXLAN。
6. 追踪产生 bug 的原因
由第三步可以暂时认定是有 SNAT 的原因。并且做 SNAT 转出来还是自己的 IP。
关于内核 SNAT 的步骤,参考 Linux NAT core 。
检查 source IP 是否为 IP pool 内的 IP,如果是的话直接返回。
找到 IP pool 里面最少使用的 IP,将 IP packet 的 source IP 换成这个 IP。
检查允许的端口,如果目前的端口,本来就空闲,就不变。之后再返回。
找一个用于 SNAT 的端口 by calling nf_nat_l4proto_unique_tuple() 。
iptables 中的 KUBE-MARK-MASQ 用作标记要不要 SNAT,POSTROUING 会做一次 SNAT,并且 VXLAN 封装之后还会做一次 SNAT。
flannel issue
coreos/flannel#1282 (comment)
coreos/flannel#1282 (comment)
coreos/flannel#1282 (comment)
有人关于 –random-fully 参数做了详细的测试,这个参数是用于选择 SNAT 的两种算法。
大意是 iptables 和 kube-proxy 对 –random-fully 支持的问题。
k8s issue https://github.com/kubernetes/kubernetes/issues/88986#issuecomment-640929804
这个 issue 中的 comment 详细讲述的原因和如何去解决这个问题。
上面这些原因是引起使用 VXLAN/VETH 时 incorrect checksum 的原因,并且是一起触发的。
由于 packet mark 的方式,当我们在第一次对 packet mark 0x4000/0x4000 并且进行 SNAT 之后,进入 VETH,经过 cni 插件出来之后,POSTROUTING 仍然会识别到这个包的 mark,并且进行二次 SNAT。
并且因为 –random-fully SNAT 方式,source port 会改变,并且因为 kernel bug 不会进行第二次 check sum,所以最后的 checksum is incorrect,解决方案可以是禁止 double SNAT 所以不会有 bad checksum 的情况,或者是升级内核使得他会进行第二次 checksum。
kernel checksum bug 参考链接
checksum-offload 指定了内核不做校验和,交给网卡 / 硬件去做,但是使用 VXLAN 时,由于上面某些原因校验值是错的。导致 checksum 错误,所以会丢弃,TCP 不得不重传,然后因为 5 次重传耗时 63s「第六次重传取消 checksum」,所以结果是 63s delay。
https://github.com/projectcalico/calico/issues/3145
Linux’ TCP stack will sometimes/always attempt to send packets with an incorrect checksum, or that are far too large for the network link, with the result that the packet is rejected and TCP has to re-transmit. This slows down network throughput enormously.
如下图,本级 checksum 一直 incorrect。
关闭 Checksum Offloading
ethtool -K flannel.1 tx-checksum-ip-generic off
7. fix this bug
https://github.com/kubernetes/kubernetes/pull/92035#issuecomment-644502203
pr 中详细讲述了触发这个 bug 的几要素。他的方法是使得走 flannel 后不会校验失败。
关闭 Checksum Offloading
因为是用的是 flannel 等虚拟网络硬件,做 checksum 等操作会 incorrect,所以关闭其实还是最省心的。
iptables https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture
explaination for the same bug https://tech.xing.com/a-reason-for-unexplained-connection-timeouts-on-kubernetes-docker-abd041cf7e02
linux code https://github.com/torvalds/linux/blob/24de3d377539e384621c5b8f8f8d8d01852dddc8/net/netfilter/nf_nat_core.c#L290-L291
weaveworks/weave#1255 (comment)
VXLAN https://community.mellanox.com/s/article/vxlan-considerations-for-connectx-3-pro
checksum-bug https://tech.vijayp.ca/linux-kernel-bug-delivers-corrupt-tcp-ip-data-to-mesos-kubernetes-docker-containers-4986f88f7a19
https://www.dynatrace.com/news/blog/detecting-network-errors-impact-on-services/