小集群中扩容机器网络不通问题分析

背景:

业务自动扩容出现机器问题,新扩容机器无法访问原有集群机器的服务问题。查了比较长时间,这里总结下排除信息和过程。初步结论是小集群中扩缩容节点使用了内网IP复用而ARP cache没有回收造成的网络不通的问题。

案例:

产研反馈:之前发现的网络问题又出现了(说明之前出现过3次以上),某服务到部分节点不通,试了下 这几个方向都是 ping 不通的,为了便于说明,把几台机器做个编号:

机器A: xx.xx.2.217

机器B:xx.xx.3.115

机器C:xx.xx.1.185

机器D:xx.xx.3.71

  1. 现象

初步分析是基于aws自动扩容组新扩出来的机器A,通过内网IP无法ping到集群中原有机器BCD,部分能通部分不通。首先想到是arp问题,分析现象是机器B上的IP A217对应 的mac地址一直处于STALE状态,腐化但不被删除,缓存时间可长达数天,远远超过了arp cache默认时间 60s。集群中其他机器B缓存了该IP错误的mac地址,导致A无法调用B的服务。217能ping 到BCD等机器,三台机器能收到arp包,但无法收到他们的回包(tcpdump分析)。

部分能通信的机器上此时对应机器A 的mac 是 xx:xx:xx:59:91:b2,正常机器

# arp|grep 217
ip-xx-xx-2-217.ap-south  ether   xx:xx:xx:59:91:b2   C                     eth0

集群原有机器B、C、D上的mac addr是xx:xx:xx:35:94:dc ,缓存了不一样的mac ,如下:

 # arp|grep 217 
ip-xx-xx-2-217.ap-south  ether   xx:xx:xx:35:94:dc   C                     eth0

xx:xx:xx:35:94:dc 可能是上次分配IP 217时绑定过的mac addr。mac 缓存记录状态持续处于STALE状态,如下:

# ip neigh ls all|grep 217
xx.xx.2.217 dev eth0 lladdr xx:xx:xx:35:94:dc STALE

在217 ping 185 ,第十个包才收到回应

$ ping xx.xx.1.185

PING xx.xx.1.185 (xx.xx.1.185) 56(84) bytes of data.

64 bytes from xx.xx.1.185: icmp_seq=10 ttl=64 time=0.127 ms //不清楚原因

64 bytes from xx.xx.1.185: icmp_seq=11 ttl=64 time=0.121 ms

64 bytes from xx.xx.1.185: icmp_seq=12 ttl=64 time=0.107 ms

  1. 初步猜测与分析方向:

  1. 怀疑是arp缓存过期慢问题,如果关闭arp缓存可能可以解决

担心风险:关闭后任何机器跟其他机器通信都要arp解析mac地址,担心对性能,arp广播数量暴涨影响。

  1. 怀疑跟aws auto scaling group+ spot有关

怀疑spot IP重分配太快,导致上一次没有在交换机被清理,下一次又用到了同一IP,导致现在问题。

准备关闭spot ,但为了arp 问题关闭spot貌似是在回避问题绕过问题,spot好技术不能使用。spot的分配时实际并没有频繁,10+次/天。

  1. 怀疑无回包可能是本机路由有缓存且是错误缓存

A217存活期间,怀疑是mac所在老机器还活着,且时不时跟集群现有机器b、c、d通信,导致mac一直未更新。查看路由是空的。

@ip-xx-xx-0-236:~$ route -Cn

Kernel IP routing cache

Source Destination Gateway Flags Metric Ref Use Iface

通过tcpdump观察,发现并没有这个其他机器用这个mac地址通讯

  1. 怀疑是路由缓存后“本地确认”问题

用iptables drop掉的情况,阻断再次连接,结果缓存还是存在,未被清理。

sudo iptables -A INPUT -s xx.xx.2.217 -j DROP
236:~$ ip -s -s neigh show |grep 2.217
xx.xx.2.217 dev eth0 lladdr xx:xx:xx:59:91:b2 used 240332/240326/240281 probes 1 STALE
236:~$ sudo iptables -A INPUT -s xx.xx.2.217 -j DROP
236:~$ date
Mon Aug 10 02:23:52 UTC 2020
236:~$ date
Mon Aug 10 03:16:43 UTC 2020
236:~$ sudo iptables -D INPUT -s xx.xx.2.217 -j DROP

发现依然是STALE状态

  1. 怀疑arp mac记录状态机在不断变化,续租更新到STALE状态

~$ watch -n1 "ip -s -s neigh show |grep 217"

Every 1.0s: ip -s -s neigh show |grep 217 Mon Aug 10 04:05:54 2020

xx.xx.2.217 dev eth0 lladdr xx:xx:xx:59:91:b2 used 246119/246114/246068 probes 1 STALE

状态没有变化,也就是没有经过delay、reachable、probe等状态

其实能想到,扩容机器下线很久后,都还有arp缓存,跟“续租”没啥关系;

  1. 怀疑过期时间参数配置错误,太长导致一直没更新

217机器A回收3天后,115等多台机器还是保留着这个arp cache 。

$ ip -s neigh list|grep 217

xx.xx.2.217 dev eth0 lladdr xx:xx:xx:35:94:dc used 173095/173065/173028 probes 4 STALE

ps: 173095/173065/173028

表示这条记录存在了过了48小时多。远超过net.ipv4.neigh.*.gc_interval = 30 时间30s。

  1. 初步确认现象并多机器验证:

当机器A回收很久后,机器BCD的arp cache记录没有被清理,机器BCD上的对应机器A217的mac addr是过期的错误的mac,下次如果有机器复用了IP217,BCD还是回包到老mac上,导致新A217无法跟BCD正常通信。

举一反三并验证问题:怀疑集群中还有大量这种情况。之前自动扩容出来,然后被缩容掉的ip还被集群中其他主机arp 缓存了,找了一台现有机器xx-xx-0-236分析

 #ip -s -s neigh show |grep STA
xx.xx.0.77 dev eth0 lladdr 06:5f:b9:b3:d4:1e used 160634/160608/160583 probes 4 STALE
xx.xx.2.167 dev eth0 lladdr 06:36:73:af:f7:ee used 409277/409246/409226 probes 4 STALE
xx.xx.0.40 dev eth0 lladdr 06:80:0a:a6:c7:ba used 878/873/847 probes 1 STALE
xx.xx.1.179 dev eth0 lladdr 06:fd:14:c9:60:40 used 165297/165272/165240 probes 1 STALE
xx.xx.3.130 dev eth0 lladdr 06:62:80:a0:43:0a used 1230063/410256/410226 probes 4 STALE  //长达14天
xx.xx.3.18 dev eth0 lladdr 06:d1:f4:27:6b:64 used 161097/161069/161043 probes 4 STALE
...

有大量IP是长时间被缓存的,是扩容后缩容了,被集群其他主机缓存了,长时间没被清理,多找了几台分析,证实了集群中大量存在STALE,基本确定是本机arp cache不删除问题。

  1. 根因分析

确定没有回收,大概率是跟gc相关参数配置有关。

查看MAC cache gc内核参数:

net.ipv4.neigh.*.gc_interval = 30
net.ipv4.neigh.*.gc_stale_time = 60
net.ipv4.neigh.*.gc_thresh1 = 128
net.ipv4.neigh.*.gc_thresh2 = 512
net.ipv4.neigh.*.gc_thresh3 = 1024
net.ipv4.neigh.*.gc_stale_time = 60

gc_interval间隔是30s,gc_state_time=60s,应该很快被回收才对,但没有回收,应该是根本原因。

  1. 相关参数

跟gc相关的内核参数有14个:

net.ipv4.conf.*.arp_accept = 0
net.ipv4.conf.*.arp_announce = 0
net.ipv4.conf.*.arp_filter = 0
net.ipv4.conf.*.arp_ignore = 0
net.ipv4.conf.*.arp_notify = 0
net.ipv4.conf.*.drop_gratuitous_arp = 0
net.ipv4.conf.*.proxy_arp = 0
net.ipv4.conf.*.proxy_arp_pvlan = 0
net.ipv4.neigh.*.gc_interval = 30
net.ipv4.neigh.*.gc_stale_time = 60
net.ipv4.neigh.*.gc_thresh1 = 128
net.ipv4.neigh.*.gc_thresh2 = 512
net.ipv4.neigh.*.gc_thresh3 = 1024
net.ipv4.neigh.*.gc_stale_time = 60
  1. GC过程及条件

gc_interval,gc_thresh1,gc_thresh2,gc_thresh3,gc_stale_time。

gc_thresh3是arp_tbl中允许拥有的邻居数量的上限,一旦超过这个上限,并且表中没有可以清理掉的垃圾邻居,那么就无法创建新的邻居,这个 值缺省被置为1024。

gc_thresh2是第二个阀值,如果表中的邻居数量超过这个阀值,并且在需要创建新的邻居时,发现已经超过5秒时间表没有被刷 新过,则必须立即刷新arp_tbl表,进行强制垃圾回收,这个值缺省被置为512。

gc_thresh1是触发真正清理的条目数量下限阈值,它缺省被置为128。

gc_interval应该是常规的垃圾回收间隔时间,缺省置为30秒。间隔这个时间后执行gc动作,符合条件的缓存记录从arp_tbl表中删除。

gc_stale_time 是STALE状态的记录生存时间。

常规垃圾回收的定时器,其定时处理函数是neigh_periodic_timer。

  1. ARP 缓存状态机

  1. 确定原因

neigh/default/gc_thresh1 - INTEGER Minimum number of entries to keep. Garbage collector will not purge entries if there are fewer than this number. Default: 128

我们的集群都较小小,gc_thresh*是针对cache条目数量的,可能都不到128

static void neigh_periodic_work(struct work_struct *work)
{
	struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work);
	...
	if (atomic_read(&tbl->entries) < tbl->gc_thresh1)
		goto out;

所以当缓存中的条目数量少于gc_threash1时,根本不会执行gc过程,直接goto out跳出了

我们生产的集群都比较小,查看arp -a|wc -l,发现都在50-100之间,基本判断<128,没有达到进行真正回收的阈值下限。我们尝试 把gc 条件的条目数设置小一点,使之触发gc。

$sudo sysctl -w net.ipv4.neigh.default.gc_thresh1=30

结果很快回收了,1分钟左右

小结:之前的128是不合适的,设为30其实也不一定合适,还真有可能arp cache entries<30的,所以设置为0或1可能是更好的选择

  1. GC过程原理再次理解:

在gc_interval间隔时间后,会触发执行arp cache gc,但真正要不要执行purge ,还要看cache 中的条目数量, gc_thresh1就是这个下限。少于这个数量就不会真正执行purge。

我们的集群都比较小,cache数量基本在50-100条左右,gc_thresh1默认值是128 ,导致一直没有真正删掉cache 条目

总结综述:arp cache长期不更新的情况

现象:ec2 A 217是新扩容机器 ,起来后跟集群中原有机器ec2 BCD通信,BCD会缓存A的mac addr。当 A 被回收后,BCD主机中对应A217 的arp cache 没有清理,被标识为STALE状态。当A之前用的IP 217被下次弹出的E 使用了,会出现E跟BCD服务调用不同的情况,从应用层看无法建立连接。从三层看是E能ping BCD, 但BCD无法回包到E。这些arp缓存记录长期不清理。

根因:是小集群中单台机器互相通信的机器数量少,没达到进行gc的条目数量,根因是机器的arp 参数配置不科学,没有根据小集群的特点进行配置。跟交换机缓存、路由缓存、arp续租等都没有关系。

GC有三层控制:1、触发gc动作,2、cache记录已经过期,3、cache列表中记录数达到gc_thresh*阈值。

解决方案比较:

从单台上来看很简单,有几种方法都可以:

  1. 定时arp flush 一次(按天/小时)比关闭arp cache风险小

缺点:但flush之前一段时间还是有业务风险

  1. 改基础镜像,每次开新机器都要自动使用小gc_thresh1参数

缺点:在底层改动,不确定是否适用其他业务,SA同学不同意

  1. 把gc 条件的条目数gc_thresh1(默认128)设置小一点(小集群中能达到的互相通信的机器数量,可能是个位数),使之真正清理cache记录。

缺点:必须全网每台机器改一遍,以后新扩的机器都要改。在ec2初始化完成后SRE验收时或是在使用部署服务时去改。

  1. 新实例启动时广播arp

缺点:原理可行但AWS vpc不支持广播因此无法执行

为什么要记录这个文档

因为这是一个小集群,很容易出现arp cache数量小于默认参数128的情况

有人理解为关闭arp cache就好,借此机会深入复习理解ARP原理

在分析过程中也发现国外有用户碰到这个问题,貌似没有彻底解决。如:linux - When do STALE arp entries become FAILED when never used? - Server Fault

参考资料:

阿里云开发者社区-云计算社区-阿里云

Linux实现的ARP缓存老化时间原理解析_Netfilter,iptables/OpenVPN/TCP guard:-(-CSDN博客_arp老化时间

https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt

arp(7) - Linux manual page

https://github.com/torvalds/linux/blob/47ec5303d73ea344e84f46660fff693c57641386/net/core/neighbour.c

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值