连接队列溢出
查看是否全连接或半连接队列溢出导致丢包,造成部分连接异常 (timeout):
$ netstat -s | grep -E 'overflow|drop'
3327 times the listen queue of a socket overflowed
32631 SYNs to LISTEN sockets dropped
进入容器 netns 后,查看各状态的连接数统计:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
故障案例:
- 健康检查失败
- 网络时同时不通
解决方案:
- 调大 sommaxconn
- 调大 backlog
- 若是 nginx,还受 nginx 本身的 backlog 配置,也调大下
conntrack 表爆满
看内核日志:
# demsg
$ journalctl -k | grep "nf_conntrack: table full"
nf_conntrack: nf_conntrack: table full, dropping packet
若有以上报错,证明 conntrack 表满了,需要调大 conntrack 表:
sysctl -w net.netfilter.nf_conntrack_max=1000000
contrack 插入失败
检查是否有插入失败:
$ conntrack -L
若发现有 insert_failed
不为 0,证明 conntrack 插入失败。由于 conntrack 插入是个乐观锁的过程,在一些极端场景下是可能插入失败的。
故障案例:
- DNS 5 秒延时。DNS 默认使用 UDP,同一台机器上多个 Pod 多个线程同时发 DNS 请求,如果 kube-proxy 开启了
--masqurade-all=true
,会全部 SNAT 成 node ip,又由于 UDP 无状态,建立连接时不会插入 conntrack,真正发包才会插入 conntrack,每条 DNS 分 A 和 AAAA 两个请求,默认共用同一 socket,源端口也就相同,导致可能存在一些源IP+源端口相同的请求同时插入 conntrack,最后丢弃一些请求。 - 存在多个VIP的LB,转发到 NodePort导致丢包。LB如果有多个VIP,可能同时存在多个VIP不同,目的端口相同的报文到同一个节点的 NodePort,到NodePort后会做SNAT与DNAT,SNAT后源IP就成相同的了(node ip),导致插入conntrack时有相同元组,最后导致插入失败。
arp 表爆满
看内核日志:
# demsg
$ journalctl -k | grep "neighbor table overflow"
arp_cache: neighbor table overflow!
若有以上报错,证明 arp 表满了,查看当前 arp 记录数:
$ arp -an | wc -l
1335
查看 arp gc 阀值:
$ sysctl -a | grep gc_thresh
net.ipv4.neigh.default.gc_thresh1 = 128
net.ipv4.neigh.default.gc_thresh2 = 512
net.ipv4.neigh.default.gc_thresh3 = 1024
调大 arp 表:
sysctl -w net.ipv4.neigh.default.gc_thresh1=80000
sysctl -w net.ipv4.neigh.default.gc_thresh2=90000
sysctl -w net.ipv4.neigh.default.gc_thresh3=100000
端口监听挂掉
如果容器内的端口已经没有进程监听了,内核就会返回 Reset 包,客户端就会报错连接被拒绝,可以进容器 netns 检查下端口是否存活:
netstat -tunlp
tcp_tw_recycle 导致丢包
在低版本内核中(比如 3.10),支持使用 tcp_tw_recycle 内核参数来开启 TIME_WAIT 的快速回收,但如果 client 也开启了 timestamp (一般默认开启),同时也就会导致在 NAT 环境丢包,甚至没有 NAT 时,稍微高并发一点,也会导致 PAWS 校验失败,导致丢包:
# 看 SYN 丢包是否全都是 PAWS 校验失败
$ cat /proc/net/netstat | grep TcpE| awk '{print $15, $22}'
PAWSPassive ListenDrops
96305 96305
参考资料: